cmCallVisualStudioMacro.cxx 13 KB

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