cmCallVisualStudioMacro.cxx 14 KB

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