cmCallVisualStudioMacro.cxx 13 KB

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