cmCallVisualStudioMacro.cxx 13 KB

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