1
0

cmCallVisualStudioMacro.cxx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmCallVisualStudioMacro.h"
  14. #include "cmSystemTools.h"
  15. #if defined(_MSC_VER)
  16. #define HAVE_COMDEF_H
  17. #endif
  18. #if defined(HAVE_COMDEF_H)
  19. #include <comdef.h>
  20. //----------------------------------------------------------------------------
  21. ///! Use ReportHRESULT to make a cmSystemTools::Error after calling
  22. ///! a COM method that may have failed.
  23. #define ReportHRESULT(hr, context) \
  24. if (FAILED(hr)) \
  25. { \
  26. std::ostringstream oss; \
  27. oss.flags(std::ios::hex); \
  28. oss << context << " failed HRESULT, hr = 0x" << hr << std::endl; \
  29. oss.flags(std::ios::dec); \
  30. oss << __FILE__ << "(" << __LINE__ << ")"; \
  31. cmSystemTools::Error(oss.str().c_str()); \
  32. }
  33. //----------------------------------------------------------------------------
  34. ///! Using the given instance of Visual Studio, call the named macro
  35. HRESULT InstanceCallMacro(
  36. IDispatch* vsIDE,
  37. const std::string& macro,
  38. const std::string& args)
  39. {
  40. HRESULT hr = E_POINTER;
  41. _bstr_t macroName(macro.c_str());
  42. _bstr_t macroArgs(args.c_str());
  43. if (0 != vsIDE)
  44. {
  45. DISPID dispid = (DISPID) -1;
  46. OLECHAR *name = L"ExecuteCommand";
  47. hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
  48. LOCALE_USER_DEFAULT, &dispid);
  49. ReportHRESULT(hr, "GetIDsOfNames(ExecuteCommand)");
  50. if (SUCCEEDED(hr))
  51. {
  52. VARIANTARG vargs[2];
  53. DISPPARAMS params;
  54. VARIANT result;
  55. EXCEPINFO excep;
  56. UINT arg = (UINT) -1;
  57. // No VariantInit or VariantClear calls are necessary for
  58. // these two vargs. They are both local _bstr_t variables
  59. // that remain in scope for the duration of the Invoke call.
  60. //
  61. V_VT(&vargs[1]) = VT_BSTR;
  62. V_BSTR(&vargs[1]) = macroName;
  63. V_VT(&vargs[0]) = VT_BSTR;
  64. V_BSTR(&vargs[0]) = macroArgs;
  65. params.rgvarg = &vargs[0];
  66. params.rgdispidNamedArgs = 0;
  67. params.cArgs = sizeof(vargs)/sizeof(vargs[0]);
  68. params.cNamedArgs = 0;
  69. VariantInit(&result);
  70. memset(&excep, 0, sizeof(excep));
  71. hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
  72. DISPATCH_METHOD, &params, &result, &excep, &arg);
  73. ReportHRESULT(hr, "Invoke(ExecuteCommand)");
  74. VariantClear(&result);
  75. }
  76. }
  77. return hr;
  78. }
  79. //----------------------------------------------------------------------------
  80. ///! Get the Solution object from the IDE object
  81. HRESULT GetSolutionObject(
  82. IDispatch* vsIDE,
  83. IDispatchPtr& vsSolution)
  84. {
  85. HRESULT hr = E_POINTER;
  86. if (0 != vsIDE)
  87. {
  88. DISPID dispid = (DISPID) -1;
  89. OLECHAR *name = L"Solution";
  90. hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
  91. LOCALE_USER_DEFAULT, &dispid);
  92. ReportHRESULT(hr, "GetIDsOfNames(Solution)");
  93. if (SUCCEEDED(hr))
  94. {
  95. DISPPARAMS params;
  96. VARIANT result;
  97. EXCEPINFO excep;
  98. UINT arg = (UINT) -1;
  99. params.rgvarg = 0;
  100. params.rgdispidNamedArgs = 0;
  101. params.cArgs = 0;
  102. params.cNamedArgs = 0;
  103. VariantInit(&result);
  104. memset(&excep, 0, sizeof(excep));
  105. hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
  106. DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
  107. ReportHRESULT(hr, "Invoke(Solution)");
  108. if (SUCCEEDED(hr))
  109. {
  110. vsSolution = V_DISPATCH(&result);
  111. }
  112. VariantClear(&result);
  113. }
  114. }
  115. return hr;
  116. }
  117. //----------------------------------------------------------------------------
  118. ///! Get the FullName property from the Solution object
  119. HRESULT GetSolutionFullName(
  120. IDispatch* vsSolution,
  121. std::string& fullName)
  122. {
  123. HRESULT hr = E_POINTER;
  124. if (0 != vsSolution)
  125. {
  126. DISPID dispid = (DISPID) -1;
  127. OLECHAR *name = L"FullName";
  128. hr = vsSolution->GetIDsOfNames(IID_NULL, &name, 1,
  129. LOCALE_USER_DEFAULT, &dispid);
  130. ReportHRESULT(hr, "GetIDsOfNames(FullName)");
  131. if (SUCCEEDED(hr))
  132. {
  133. DISPPARAMS params;
  134. VARIANT result;
  135. EXCEPINFO excep;
  136. UINT arg = (UINT) -1;
  137. params.rgvarg = 0;
  138. params.rgdispidNamedArgs = 0;
  139. params.cArgs = 0;
  140. params.cNamedArgs = 0;
  141. VariantInit(&result);
  142. memset(&excep, 0, sizeof(excep));
  143. hr = vsSolution->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
  144. DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
  145. ReportHRESULT(hr, "Invoke(FullName)");
  146. if (SUCCEEDED(hr))
  147. {
  148. fullName = (std::string) (_bstr_t) V_BSTR(&result);
  149. }
  150. VariantClear(&result);
  151. }
  152. }
  153. return hr;
  154. }
  155. //----------------------------------------------------------------------------
  156. ///! Get the FullName property from the Solution object, given the IDE object
  157. HRESULT GetIDESolutionFullName(
  158. IDispatch* vsIDE,
  159. std::string& fullName)
  160. {
  161. IDispatchPtr vsSolution;
  162. HRESULT hr = GetSolutionObject(vsIDE, vsSolution);
  163. ReportHRESULT(hr, "GetSolutionObject");
  164. if (SUCCEEDED(hr))
  165. {
  166. GetSolutionFullName(vsSolution, fullName);
  167. ReportHRESULT(hr, "GetSolutionFullName");
  168. }
  169. return hr;
  170. }
  171. //----------------------------------------------------------------------------
  172. ///! Get all running objects from the Windows running object table.
  173. ///! Save them in a map by their display names.
  174. HRESULT GetRunningInstances(std::map<std::string, IUnknownPtr>& mrot)
  175. {
  176. // mrot == Map of the Running Object Table
  177. IRunningObjectTablePtr runningObjectTable;
  178. IEnumMonikerPtr monikerEnumerator;
  179. IMonikerPtr moniker;
  180. ULONG numFetched = 0;
  181. HRESULT hr = GetRunningObjectTable(0, &runningObjectTable);
  182. ReportHRESULT(hr, "GetRunningObjectTable");
  183. if(SUCCEEDED(hr))
  184. {
  185. hr = runningObjectTable->EnumRunning(&monikerEnumerator);
  186. ReportHRESULT(hr, "EnumRunning");
  187. }
  188. if(SUCCEEDED(hr))
  189. {
  190. hr = monikerEnumerator->Reset();
  191. ReportHRESULT(hr, "Reset");
  192. }
  193. if(SUCCEEDED(hr))
  194. {
  195. while (S_OK == monikerEnumerator->Next(1, &moniker, &numFetched))
  196. {
  197. std::string runningObjectName;
  198. IUnknownPtr runningObjectVal;
  199. IBindCtxPtr ctx;
  200. hr = CreateBindCtx(0, &ctx);
  201. ReportHRESULT(hr, "CreateBindCtx");
  202. if(SUCCEEDED(hr))
  203. {
  204. LPOLESTR displayName = 0;
  205. hr = moniker->GetDisplayName(ctx, 0, &displayName);
  206. ReportHRESULT(hr, "GetDisplayName");
  207. if (displayName)
  208. {
  209. runningObjectName = (std::string) (_bstr_t) displayName;
  210. CoTaskMemFree(displayName);
  211. }
  212. hr = runningObjectTable->GetObject(moniker, &runningObjectVal);
  213. ReportHRESULT(hr, "GetObject");
  214. if(SUCCEEDED(hr))
  215. {
  216. mrot.insert(std::make_pair(runningObjectName, runningObjectVal));
  217. }
  218. }
  219. numFetched = 0;
  220. moniker = 0;
  221. }
  222. }
  223. return hr;
  224. }
  225. //----------------------------------------------------------------------------
  226. ///! Do the two file names refer to the same Visual Studio solution? Or are
  227. ///! we perhaps looking for any and all solutions?
  228. bool FilesSameSolution(
  229. const std::string& slnFile,
  230. const std::string& slnName)
  231. {
  232. if (slnFile == "ALL" || slnName == "ALL")
  233. {
  234. return true;
  235. }
  236. // Otherwise, make lowercase local copies, convert to Unix slashes, and
  237. // see if the resulting strings are the same:
  238. std::string s1 = cmSystemTools::LowerCase(slnFile);
  239. std::string s2 = cmSystemTools::LowerCase(slnName);
  240. cmSystemTools::ConvertToUnixSlashes(s1);
  241. cmSystemTools::ConvertToUnixSlashes(s2);
  242. return s1 == s2;
  243. }
  244. //----------------------------------------------------------------------------
  245. ///! Find instances of Visual Studio with the given solution file
  246. ///! open. Pass "ALL" for slnFile to gather all running instances
  247. ///! of Visual Studio.
  248. HRESULT FindVisualStudioInstances(
  249. const std::string& slnFile,
  250. std::vector<IDispatchPtr>& instances)
  251. {
  252. std::map<std::string, IUnknownPtr> mrot;
  253. HRESULT hr = GetRunningInstances(mrot);
  254. ReportHRESULT(hr, "GetRunningInstances");
  255. if(SUCCEEDED(hr))
  256. {
  257. std::map<std::string, IUnknownPtr>::iterator it;
  258. for(it = mrot.begin(); it != mrot.end(); ++it)
  259. {
  260. if (cmSystemTools::StringStartsWith(it->first.c_str(),
  261. "!VisualStudio.DTE."))
  262. {
  263. IDispatchPtr disp(it->second);
  264. if (disp != (IDispatch*) 0)
  265. {
  266. std::string slnName;
  267. hr = GetIDESolutionFullName(disp, slnName);
  268. ReportHRESULT(hr, "GetIDESolutionFullName");
  269. if (FilesSameSolution(slnFile, slnName))
  270. {
  271. instances.push_back(disp);
  272. //std::cout << "Found Visual Studio instance." << std::endl;
  273. //std::cout << " ROT entry name: " << it->first << std::endl;
  274. //std::cout << " ROT entry object: " << (IUnknown*) it->second << std::endl;
  275. //std::cout << " slnFile: " << slnFile << std::endl;
  276. //std::cout << " slnName: " << slnName << std::endl;
  277. }
  278. }
  279. }
  280. }
  281. }
  282. return hr;
  283. }
  284. #endif //defined(HAVE_COMDEF_H)
  285. //----------------------------------------------------------------------------
  286. int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
  287. const std::string& slnFile)
  288. {
  289. int count = 0;
  290. #if defined(HAVE_COMDEF_H)
  291. HRESULT hr = CoInitialize(0);
  292. ReportHRESULT(hr, "CoInitialize");
  293. if(SUCCEEDED(hr))
  294. {
  295. std::vector<IDispatchPtr> instances;
  296. hr = FindVisualStudioInstances(slnFile, instances);
  297. ReportHRESULT(hr, "FindVisualStudioInstances");
  298. if(SUCCEEDED(hr))
  299. {
  300. count = instances.size();
  301. }
  302. // Force release all COM pointers before CoUninitialize:
  303. instances.clear();
  304. CoUninitialize();
  305. }
  306. #endif
  307. return count;
  308. }
  309. //----------------------------------------------------------------------------
  310. ///! Get all running objects from the Windows running object table.
  311. ///! Save them in a map by their display names.
  312. int cmCallVisualStudioMacro::CallMacro(
  313. const std::string& slnFile,
  314. const std::string& macro,
  315. const std::string& args)
  316. {
  317. int err = 1; // no comdef.h
  318. #if defined(HAVE_COMDEF_H)
  319. err = 2; // error initializing
  320. HRESULT hr = CoInitialize(0);
  321. ReportHRESULT(hr, "CoInitialize");
  322. if(SUCCEEDED(hr))
  323. {
  324. std::vector<IDispatchPtr> instances;
  325. hr = FindVisualStudioInstances(slnFile, instances);
  326. ReportHRESULT(hr, "FindVisualStudioInstances");
  327. if(SUCCEEDED(hr))
  328. {
  329. err = 0; // no error
  330. std::vector<IDispatchPtr>::iterator it;
  331. for(it = instances.begin(); it != instances.end(); ++it)
  332. {
  333. hr = InstanceCallMacro(*it, macro, args);
  334. ReportHRESULT(hr, "InstanceCallMacro");
  335. if (FAILED(hr))
  336. {
  337. err = 3; // error attempting to call the macro
  338. }
  339. }
  340. if(0 == instances.size())
  341. {
  342. // no instances to call
  343. //cmSystemTools::Message(
  344. // "cmCallVisualStudioMacro::CallMacro no instances found to call",
  345. // "Warning");
  346. }
  347. }
  348. // Force release all COM pointers before CoUninitialize:
  349. instances.clear();
  350. CoUninitialize();
  351. }
  352. #else
  353. cmSystemTools::Error("cmCallVisualStudioMacro::CallMacro is not "
  354. "supported on this platform");
  355. #endif
  356. if (err)
  357. {
  358. std::ostringstream oss;
  359. oss << "cmCallVisualStudioMacro::CallMacro failed, err = " << err;
  360. cmSystemTools::Error(oss.str().c_str());
  361. }
  362. return err;
  363. }