PageRenderTime 37ms CodeModel.GetById 10ms app.highlight 20ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/tools/vstool/main.cs

https://bitbucket.org/lindenlab/viewer-beta/
C# | 721 lines | 574 code | 88 blank | 59 comment | 74 complexity | 60d09d7ea7d749cf386a3f45e80662b4 MD5 | raw file
  1// Code about getting running instances visual studio
  2// was borrowed from 
  3// http://www.codeproject.com/KB/cs/automatingvisualstudio.aspx
  4
  5
  6using System;
  7using System.Collections;
  8using System.Collections.Generic;
  9using System.Reflection;
 10using System.Runtime.InteropServices;
 11using System.Runtime.InteropServices.ComTypes;
 12using Microsoft.CSharp;
 13
 14namespace VSTool
 15{
 16    // The MessageFilter class comes from:
 17    // http://msdn.microsoft.com/en-us/library/ms228772(VS.80).aspx
 18    // It allows vstool to get timing error messages from 
 19    // visualstudio and handle them.
 20    public class MessageFilter : IOleMessageFilter
 21    {
 22        //
 23        // Class containing the IOleMessageFilter
 24        // thread error-handling functions.
 25
 26        // Start the filter.
 27        public static void Register()
 28        {
 29            IOleMessageFilter newFilter = new MessageFilter(); 
 30            IOleMessageFilter oldFilter = null; 
 31            CoRegisterMessageFilter(newFilter, out oldFilter);
 32        }
 33
 34        // Done with the filter, close it.
 35        public static void Revoke()
 36        {
 37            IOleMessageFilter oldFilter = null; 
 38            CoRegisterMessageFilter(null, out oldFilter);
 39        }
 40
 41        //
 42        // IOleMessageFilter functions.
 43        // Handle incoming thread requests.
 44        int IOleMessageFilter.HandleInComingCall(int dwCallType, 
 45          System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr 
 46          lpInterfaceInfo) 
 47        {
 48            //Return the flag SERVERCALL_ISHANDLED.
 49            return 0;
 50        }
 51
 52        // Thread call was rejected, so try again.
 53        int IOleMessageFilter.RetryRejectedCall(System.IntPtr 
 54          hTaskCallee, int dwTickCount, int dwRejectType)
 55        {
 56            if (dwRejectType == 2)
 57            // flag = SERVERCALL_RETRYLATER.
 58            {
 59                // Retry the thread call immediately if return >=0 & 
 60                // <100.
 61                return 99;
 62            }
 63            // Too busy; cancel call.
 64            return -1;
 65        }
 66
 67        int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, 
 68          int dwTickCount, int dwPendingType)
 69        {
 70            //Return the flag PENDINGMSG_WAITDEFPROCESS.
 71            return 2; 
 72        }
 73
 74        // Implement the IOleMessageFilter interface.
 75        [DllImport("Ole32.dll")]
 76        private static extern int 
 77          CoRegisterMessageFilter(IOleMessageFilter newFilter, out 
 78          IOleMessageFilter oldFilter);
 79    }
 80
 81    [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), 
 82    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
 83    interface IOleMessageFilter 
 84    {
 85        [PreserveSig]
 86        int HandleInComingCall( 
 87            int dwCallType, 
 88            IntPtr hTaskCaller, 
 89            int dwTickCount, 
 90            IntPtr lpInterfaceInfo);
 91
 92        [PreserveSig]
 93        int RetryRejectedCall( 
 94            IntPtr hTaskCallee, 
 95            int dwTickCount,
 96            int dwRejectType);
 97
 98        [PreserveSig]
 99        int MessagePending( 
100            IntPtr hTaskCallee, 
101            int dwTickCount,
102            int dwPendingType);
103    }
104
105    class ViaCOM
106    {
107        public static object GetProperty(object from_obj, string prop_name)
108        {
109            try
110            {
111                Type objType = from_obj.GetType();
112                return objType.InvokeMember(
113                    prop_name,
114                    BindingFlags.GetProperty, null,
115                    from_obj,
116                    null);
117            }
118            catch (Exception e)
119            {
120                Console.WriteLine("Error getting property: \"{0}\"", prop_name);
121                Console.WriteLine(e.Message);
122                throw e;
123            }
124        }
125
126        public static object SetProperty(object from_obj, string prop_name, object new_value)
127        {
128            try
129            {
130                object[] args = { new_value };
131                Type objType = from_obj.GetType();
132                return objType.InvokeMember(
133                    prop_name,
134                    BindingFlags.DeclaredOnly |
135                    BindingFlags.Public |
136                    BindingFlags.NonPublic |
137                    BindingFlags.Instance |
138                    BindingFlags.SetProperty,
139                    null,
140                    from_obj,
141                    args);
142            }
143            catch (Exception e)
144            {
145                Console.WriteLine("Error setting property: \"{0}\"", prop_name);
146                Console.WriteLine(e.Message);
147                throw e;
148            }
149        }
150
151        public static object CallMethod(object from_obj, string method_name, params object[] args)
152        {
153            try
154            {
155                Type objType = from_obj.GetType();
156                return objType.InvokeMember(
157                    method_name,
158                    BindingFlags.DeclaredOnly |
159                    BindingFlags.Public |
160                    BindingFlags.NonPublic |
161                    BindingFlags.Instance |
162                    BindingFlags.InvokeMethod,
163                    null,
164                    from_obj,
165                    args);
166            }
167            catch (Exception e)
168            {
169                Console.WriteLine("Error calling method \"{0}\"", method_name);
170                Console.WriteLine(e.Message);
171                throw e;
172            }
173        }
174    };
175
176    /// <summary>
177	/// The main entry point class for VSTool.
178	/// </summary>
179    class VSToolMain
180    {
181        #region Interop imports
182        [DllImport("ole32.dll")]  
183        public static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot); 
184 
185        [DllImport("ole32.dll")]  
186        public static extern int  CreateBindCtx(int reserved, out IBindCtx ppbc);
187        #endregion 
188
189        static System.Boolean ignore_case = true;
190
191        static string solution_name = null;
192        static bool use_new_vs = false;
193        static Hashtable projectDict = new Hashtable();
194        static string startup_project = null;
195        static string config = null;
196
197        static object dte = null;
198        static object solution = null;
199
200        /// <summary>
201		/// The main entry point for the application.
202		/// </summary>
203		[STAThread]
204		static int Main(string[] args)
205		{
206            int retVal = 0;
207            bool need_save = false;
208
209            try
210            {
211                parse_command_line(args);
212
213                Console.WriteLine("Editing solution: {0}", solution_name);
214
215                bool found_open_solution = GetDTEAndSolution();
216
217                if (dte == null || solution == null)
218                {
219                    retVal = 1;
220                }
221                else
222                {
223                    MessageFilter.Register();
224
225                    // Walk through all of the projects in the solution
226                    // and list the type of each project.
227                    foreach (DictionaryEntry p in projectDict)
228                    {
229                        string project_name = (string)p.Key;
230                        string working_dir = (string)p.Value;
231                        if (SetProjectWorkingDir(solution, project_name, working_dir))
232                        {
233                            need_save = true;
234                        }
235                    }
236
237                    if (config != null)
238                    {
239                        need_save = SetActiveConfig(config);
240                    }
241
242                    if (startup_project != null)
243                    {
244                        need_save = SetStartupProject(startup_project);
245                    }
246
247                    if (need_save)
248                    {
249                        if (found_open_solution == false)
250                        {
251                            ViaCOM.CallMethod(solution, "Close", null);
252                        }
253                    }
254                }
255            }
256            catch (Exception e)
257            {
258                Console.WriteLine(e.Message);
259                retVal = 1;
260            }
261            finally
262            {
263                if (solution != null)
264                {
265                    Marshal.ReleaseComObject(solution);
266                    solution = null;
267                }
268
269                if (dte != null)
270                {
271                    Marshal.ReleaseComObject(dte);
272                    dte = null;
273                }
274
275                MessageFilter.Revoke();
276            }
277            return retVal;
278        }
279
280        public static bool parse_command_line(string[] args)
281        {
282            string options_desc = 
283                "--solution <solution_name>   : MSVC solution name. (required)\n" +
284                "--use_new_vs                 : Ignore running versions of visual studio.\n" +
285                "--workingdir <project> <dir> : Set working dir of a VC project.\n" +
286                "--config <config>            : Set the active config for the solution.\n" +
287                "--startup <project>          : Set the startup project for the solution.\n";
288
289            try
290            {
291                // Command line param parsing loop.
292                int i = 0;
293                for (; i < args.Length; ++i)
294                {
295                    if ("--solution" == args[i])
296                    {
297                        if (solution_name != null)
298                        {
299                            throw new ApplicationException("Found second --solution option");
300                        }
301                        solution_name = args[++i];
302                    }
303                    else if ("--use_new_vs" == args[i])
304                    {
305                        use_new_vs = true;
306                    }
307
308                    else if ("--workingdir" == args[i])
309                    {
310                        string project_name = args[++i];
311                        string working_dir = args[++i];
312                        projectDict.Add(project_name, working_dir);
313                    }
314                    else if ("--config" == args[i])
315                    {
316                        if (config != null)
317                        {
318                            throw new ApplicationException("Found second --config option");
319                        }
320                        config = args[++i];
321                    }
322                    else if ("--startup" == args[i])
323                    {
324                        if (startup_project != null)
325                        {
326                            throw new ApplicationException("Found second --startup option");
327                        }
328                        startup_project = args[++i];
329                    }
330                    else
331                    {
332                        throw new ApplicationException("Found unrecognized token on command line: " + args[i]);
333                    }
334                }
335
336                if (solution_name == null)
337                {
338                    throw new ApplicationException("The --solution option is required.");
339                }
340            }
341            catch(ApplicationException e)
342            {
343
344                Console.WriteLine("Oops! " + e.Message);
345                Console.Write("Command line:");
346                foreach (string arg in args)
347                {
348                    Console.Write(" " + arg);
349                }
350                Console.Write("\n\n");
351                Console.WriteLine("VSTool command line usage");
352                Console.Write(options_desc);
353                throw e;
354            }
355            return true;
356        }
357
358        public static bool GetDTEAndSolution()
359        {
360            bool found_open_solution = true;
361
362            Console.WriteLine("Looking for existing VisualStudio instance...");
363
364            // Get an instance of the currently running Visual Studio .NET IDE.
365            // dte = (EnvDTE.DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.7.1");
366            string full_solution_name = System.IO.Path.GetFullPath(solution_name);
367            if (false == use_new_vs)
368            {
369                dte = GetIDEInstance(full_solution_name);
370            }
371
372            if (dte == null)
373            {
374                try
375                {
376                    Console.WriteLine("  Didn't find open solution, starting new background VisualStudio instance...");
377                    Console.WriteLine("  Reading .sln file version...");
378                    string version = GetSolutionVersion(full_solution_name);
379
380                    Console.WriteLine("  Using version: {0}...", version);
381                    string progid = GetVSProgID(version);
382
383                    Type objType = Type.GetTypeFromProgID(progid);
384                    dte = System.Activator.CreateInstance(objType);
385                    Console.WriteLine("  Reading solution: \"{0}\"", full_solution_name);
386
387                    solution = ViaCOM.GetProperty(dte, "Solution");
388                    object[] openArgs = { full_solution_name };
389                    ViaCOM.CallMethod(solution, "Open", openArgs);
390                }
391                catch (Exception e)
392                {
393                    Console.WriteLine(e.Message);
394                    Console.WriteLine("Quitting do to error opening: {0}", full_solution_name);
395                    solution = null;
396                    dte = null;
397                    return found_open_solution;
398                }
399                found_open_solution = false;
400            }
401
402            if (solution == null)
403            {
404                solution = ViaCOM.GetProperty(dte, "Solution");
405            }
406
407            return found_open_solution;
408        }
409
410        /// <summary>
411        /// Get the DTE object for the instance of Visual Studio IDE that has 
412        /// the specified solution open.
413        /// </summary>
414        /// <param name="solutionFile">The absolute filename of the solution</param>
415        /// <returns>Corresponding DTE object or null if no such IDE is running</returns>
416        public static object GetIDEInstance( string solutionFile )
417        {
418            Hashtable runningInstances = GetIDEInstances( true );
419            IDictionaryEnumerator enumerator = runningInstances.GetEnumerator();
420
421            while ( enumerator.MoveNext() )
422            {
423                try
424                {
425                    object ide = enumerator.Value;
426                    if (ide != null)
427                    {
428                        object sol = ViaCOM.GetProperty(ide, "Solution");
429                        if (0 == string.Compare((string)ViaCOM.GetProperty(sol, "FullName"), solutionFile, ignore_case))
430                        {
431                            return ide;
432                        }
433                    }
434                } 
435                catch{}
436            }
437
438            return null;
439        }
440
441        /// <summary>
442        /// Get a table of the currently running instances of the Visual Studio .NET IDE.
443        /// </summary>
444        /// <param name="openSolutionsOnly">Only return instances that have opened a solution</param>
445        /// <returns>A hashtable mapping the name of the IDE in the running object table to the corresponding DTE object</returns>
446        public static Hashtable GetIDEInstances( bool openSolutionsOnly )
447        {
448            Hashtable runningIDEInstances = new Hashtable();
449            Hashtable runningObjects = GetRunningObjectTable();
450
451            IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
452            while ( rotEnumerator.MoveNext() )
453            {
454                string candidateName = (string) rotEnumerator.Key;
455                if (!candidateName.StartsWith("!VisualStudio.DTE"))
456                    continue;
457
458                object ide = rotEnumerator.Value;
459                if (ide == null)
460                    continue;
461
462                if (openSolutionsOnly)
463                {
464                    try
465                    {
466                        object sol = ViaCOM.GetProperty(ide, "Solution");
467                        string solutionFile = (string)ViaCOM.GetProperty(sol, "FullName");
468                        if (solutionFile != String.Empty)
469                        {
470                            runningIDEInstances[ candidateName ] = ide;
471                        }
472                    } 
473                    catch {}
474                }
475                else
476                {
477                    runningIDEInstances[ candidateName ] = ide;
478                }                       
479            }
480            return runningIDEInstances;
481        }
482
483        /// <summary>
484        /// Get a snapshot of the running object table (ROT).
485        /// </summary>
486        /// <returns>A hashtable mapping the name of the object in the ROT to the corresponding object</returns>
487        [STAThread]
488        public static Hashtable GetRunningObjectTable()
489        {
490            Hashtable result = new Hashtable();
491
492            int numFetched = 0;
493            IRunningObjectTable runningObjectTable;   
494            IEnumMoniker monikerEnumerator;
495            IMoniker[] monikers = new IMoniker[1];
496
497            GetRunningObjectTable(0, out runningObjectTable);    
498            runningObjectTable.EnumRunning(out monikerEnumerator);
499            monikerEnumerator.Reset();          
500            
501            while (monikerEnumerator.Next(1, monikers, new IntPtr(numFetched)) == 0)
502            {     
503                IBindCtx ctx;
504                CreateBindCtx(0, out ctx);     
505                    
506                string runningObjectName;
507                monikers[0].GetDisplayName(ctx, null, out runningObjectName);
508
509                object runningObjectVal;  
510                runningObjectTable.GetObject( monikers[0], out runningObjectVal); 
511
512                result[ runningObjectName ] = runningObjectVal;
513            } 
514
515            return result;
516        }
517
518        public static string GetSolutionVersion(string solutionFullFileName) 
519        {
520            string version;
521            System.IO.StreamReader solutionStreamReader = null;
522            string firstLine;
523            string format;
524            
525            try
526            {
527                solutionStreamReader = new System.IO.StreamReader(solutionFullFileName);
528                do
529                {
530                    firstLine = solutionStreamReader.ReadLine();
531                }
532                while (firstLine == "");
533                
534                format = firstLine.Substring(firstLine.LastIndexOf(" ")).Trim();
535        
536                switch(format)
537                {
538                    case "7.00":
539                        version = "VC70";
540                        break;
541
542                    case "8.00":
543                        version = "VC71";
544                        break;
545
546                    case "9.00":
547                        version = "VC80";
548                        break;
549                
550                    case "10.00":
551                        version = "VC90";
552                        break;
553
554                    case "11.00":
555                        version = "VC100";
556                        break;
557
558                    default:
559                        throw new ApplicationException("Unknown .sln version: " + format);
560                }
561            }
562            finally
563            {
564                if(solutionStreamReader != null) 
565                {
566                    solutionStreamReader.Close();
567                }
568            }
569            
570            return version;
571        }
572
573        public static string GetVSProgID(string version)
574        {
575            string progid = null;
576            switch(version)
577            {
578                case "VC70":
579                    progid = "VisualStudio.DTE.7";
580                    break;
581
582                case "VC71":
583                    progid = "VisualStudio.DTE.7.1";
584                    break;
585
586                case "VC80":
587                    progid = "VisualStudio.DTE.8.0";
588                    break;
589                
590                case "VC90":
591                    progid = "VisualStudio.DTE.9.0";
592                    break;
593
594                case "VC100":
595                    progid = "VisualStudio.DTE.10.0";
596                    break;
597
598                default:
599                    throw new ApplicationException("Can't handle VS version: " + version);
600            }
601
602            return progid;
603        }
604
605        public static bool SetProjectWorkingDir(object sol, string project_name, string working_dir)
606        {
607            bool made_change = false;
608            Console.WriteLine("Looking for project {0}...", project_name);
609            try
610            {
611                object prjs = ViaCOM.GetProperty(sol, "Projects");
612                object count = ViaCOM.GetProperty(prjs, "Count");
613                for(int i = 1; i <= (int)count; ++i)
614                {
615                    object[] prjItemArgs = { (object)i };
616                    object prj = ViaCOM.CallMethod(prjs, "Item", prjItemArgs);
617                    string name = (string)ViaCOM.GetProperty(prj, "Name");
618                    if (0 == string.Compare(name, project_name, ignore_case))
619                    {
620                        Console.WriteLine("Found project: {0}", project_name);
621                        Console.WriteLine("Setting working directory");
622
623                        string full_project_name = (string)ViaCOM.GetProperty(prj, "FullName");
624                        Console.WriteLine(full_project_name);
625
626                        // *NOTE:Mani Thanks to incompatibilities between different versions of the 
627                        // VCProjectEngine.dll assembly, we can't cast the objects recevied from the DTE to
628                        // the VCProjectEngine types from a different version than the one built 
629                        // with. ie, VisualStudio.DTE.7.1 objects can't be converted in a project built 
630                        // in VS 8.0. To avoid this problem, we can use the com object interfaces directly, 
631                        // without the type casting. Its tedious code, but it seems to work.
632
633                        // oCfgs should be assigned to a 'Project.Configurations' collection.
634                        object oCfgs = ViaCOM.GetProperty(ViaCOM.GetProperty(prj, "Object"), "Configurations");
635
636                        // oCount will be assigned to the number of configs present in oCfgs.
637                        object oCount = ViaCOM.GetProperty(oCfgs, "Count");
638
639                        for (int cfgIndex = 1; cfgIndex <= (int)oCount; ++cfgIndex)
640                        {
641                            object[] itemArgs = {(object)cfgIndex};
642                            object oCfg = ViaCOM.CallMethod(oCfgs, "Item", itemArgs);
643                            object oDebugSettings = ViaCOM.GetProperty(oCfg, "DebugSettings");
644                            ViaCOM.SetProperty(oDebugSettings, "WorkingDirectory", (object)working_dir);
645                        }
646
647                        break;
648                    }
649                }
650                made_change = true;
651            }
652            catch( Exception e )
653            {
654                Console.WriteLine(e.Message);
655                Console.WriteLine("Failed to set working dir for project, {0}.", project_name);
656            }
657
658            return made_change;
659        }
660
661        public static bool SetStartupProject(string startup_project)
662        {
663            bool result = false;
664            try
665            {
666                // You need the 'unique name of the project to set StartupProjects.
667                // find the project by generic name.
668                Console.WriteLine("Trying to set \"{0}\" to the startup project", startup_project);
669                object prjs = ViaCOM.GetProperty(solution, "Projects");
670                object count = ViaCOM.GetProperty(prjs, "Count");
671                for (int i = 1; i <= (int)count; ++i)
672                {
673                    object[] itemArgs = { (object)i };
674                    object prj = ViaCOM.CallMethod(prjs, "Item", itemArgs);
675                    object prjName = ViaCOM.GetProperty(prj, "Name");
676                    if (0 == string.Compare((string)prjName, startup_project, ignore_case))
677                    {
678                        object solBuild = ViaCOM.GetProperty(solution, "SolutionBuild");
679                        ViaCOM.SetProperty(solBuild, "StartupProjects", ViaCOM.GetProperty(prj, "UniqueName"));
680                        Console.WriteLine("  Success!");
681                        result = true;
682                        break;
683                    }
684                }
685
686                if (result == false)
687                {
688                    Console.WriteLine("  Could not find project \"{0}\" in the solution.", startup_project);
689                }
690            }
691            catch (Exception e)
692            {
693                Console.WriteLine("  Failed to set the startup project!");
694                Console.WriteLine(e.Message);
695            }
696            return result;
697        }
698
699        public static bool SetActiveConfig(string config)
700        {
701            bool result = false;
702            try
703            {
704                Console.WriteLine("Trying to set active config to \"{0}\"", config);
705                object solBuild = ViaCOM.GetProperty(solution, "SolutionBuild");
706                object solCfgs = ViaCOM.GetProperty(solBuild, "SolutionConfigurations");
707                object[] itemArgs = { (object)config };
708                object solCfg = ViaCOM.CallMethod(solCfgs, "Item", itemArgs);
709                ViaCOM.CallMethod(solCfg, "Activate", null);
710                Console.WriteLine("  Success!");
711                result = true;
712            }
713            catch (Exception e)
714            {
715                Console.WriteLine("  Failed to set \"{0}\" as the active config.", config);
716                Console.WriteLine(e.Message);
717            }
718            return result;
719        }
720    }
721}