cmCallVisualStudioMacro.cxx 14 KB

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