cmCallVisualStudioMacro.cxx 14 KB

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