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