1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCallVisualStudioMacro.h"
7 #include "cmStringAlgorithms.h"
8 #include "cmSystemTools.h"
11 # define HAVE_COMDEF_H
14 // Just for this file:
16 static bool LogErrorsAsMessages;
18 #if defined(HAVE_COMDEF_H)
22 // Copied from a correct comdef.h to avoid problems with deficient versions
23 // of comdef.h that exist in the wild... Fixes issue #7533.
25 # ifdef _NATIVE_WCHAR_T_DEFINED
27 # pragma comment(lib, "comsuppwd.lib")
29 # pragma comment(lib, "comsuppw.lib")
33 # pragma comment(lib, "comsuppd.lib")
35 # pragma comment(lib, "comsupp.lib")
39 //! Use ReportHRESULT to make a cmSystemTools::Message after calling
40 //! a COM method that may have failed.
41 # define ReportHRESULT(hr, context) \
43 if (LogErrorsAsMessages) { \
44 std::ostringstream _hresult_oss; \
45 _hresult_oss.flags(std::ios::hex); \
46 _hresult_oss << context << " failed HRESULT, hr = 0x" << hr << '\n'; \
47 _hresult_oss.flags(std::ios::dec); \
48 _hresult_oss << __FILE__ << "(" << __LINE__ << ")"; \
49 cmSystemTools::Message(_hresult_oss.str()); \
53 //! Using the given instance of Visual Studio, call the named macro
54 HRESULT InstanceCallMacro(IDispatch* vsIDE, const std::string& macro,
55 const std::string& args)
57 HRESULT hr = E_POINTER;
59 _bstr_t macroName(macro.c_str());
60 _bstr_t macroArgs(args.c_str());
63 DISPID dispid = (DISPID)-1;
64 wchar_t execute_command[] = L"ExecuteCommand";
65 OLECHAR* name = execute_command;
68 vsIDE->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
69 ReportHRESULT(hr, "GetIDsOfNames(ExecuteCommand)");
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.
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;
87 params.rgvarg = &vargs[0];
88 params.rgdispidNamedArgs = 0;
89 params.cArgs = sizeof(vargs) / sizeof(vargs[0]);
90 params.cNamedArgs = 0;
94 memset(&excep, 0, sizeof(excep));
96 hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
97 DISPATCH_METHOD, ¶ms, &result, &excep, &arg);
99 std::ostringstream oss;
100 /* clang-format off */
101 oss << "\nInvoke(ExecuteCommand)\n"
102 " Macro: " << macro << "\n"
103 " Args: " << args << '\n';
104 /* clang-format on */
106 if (DISP_E_EXCEPTION == hr) {
107 /* clang-format off */
108 oss << "DISP_E_EXCEPTION EXCEPINFO:" << excep.wCode << "\n"
109 " wCode: " << excep.wCode << "\n"
110 " wReserved: " << excep.wReserved << '\n';
111 /* clang-format on */
112 if (excep.bstrSource) {
113 oss << " bstrSource: " << (const char*)(_bstr_t)excep.bstrSource
116 if (excep.bstrDescription) {
117 oss << " bstrDescription: "
118 << (const char*)(_bstr_t)excep.bstrDescription << '\n';
120 if (excep.bstrHelpFile) {
121 oss << " bstrHelpFile: " << (const char*)(_bstr_t)excep.bstrHelpFile
124 /* clang-format off */
125 oss << " dwHelpContext: " << excep.dwHelpContext << "\n"
126 " pvReserved: " << excep.pvReserved << "\n"
127 " pfnDeferredFillIn: "
128 << reinterpret_cast<void*>(excep.pfnDeferredFillIn) << "\n"
129 " scode: " << excep.scode << '\n';
130 /* clang-format on */
133 std::string exstr(oss.str());
134 ReportHRESULT(hr, exstr.c_str());
136 VariantClear(&result);
143 //! Get the Solution object from the IDE object
144 HRESULT GetSolutionObject(IDispatch* vsIDE, IDispatchPtr& vsSolution)
146 HRESULT hr = E_POINTER;
149 DISPID dispid = (DISPID)-1;
150 wchar_t solution[] = L"Solution";
151 OLECHAR* name = solution;
154 vsIDE->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT, &dispid);
155 ReportHRESULT(hr, "GetIDsOfNames(Solution)");
164 params.rgdispidNamedArgs = 0;
166 params.cNamedArgs = 0;
168 VariantInit(&result);
170 memset(&excep, 0, sizeof(excep));
172 hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
173 DISPATCH_PROPERTYGET, ¶ms, &result, &excep, &arg);
174 ReportHRESULT(hr, "Invoke(Solution)");
177 vsSolution = V_DISPATCH(&result);
180 VariantClear(&result);
187 //! Get the FullName property from the Solution object
188 HRESULT GetSolutionFullName(IDispatch* vsSolution, std::string& fullName)
190 HRESULT hr = E_POINTER;
192 if (0 != vsSolution) {
193 DISPID dispid = (DISPID)-1;
194 wchar_t full_name[] = L"FullName";
195 OLECHAR* name = full_name;
197 hr = vsSolution->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_USER_DEFAULT,
199 ReportHRESULT(hr, "GetIDsOfNames(FullName)");
208 params.rgdispidNamedArgs = 0;
210 params.cNamedArgs = 0;
212 VariantInit(&result);
214 memset(&excep, 0, sizeof(excep));
216 hr = vsSolution->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
217 DISPATCH_PROPERTYGET, ¶ms, &result, &excep,
219 ReportHRESULT(hr, "Invoke(FullName)");
222 fullName = (std::string)(_bstr_t)V_BSTR(&result);
225 VariantClear(&result);
232 //! Get the FullName property from the Solution object, given the IDE object
233 HRESULT GetIDESolutionFullName(IDispatch* vsIDE, std::string& fullName)
235 IDispatchPtr vsSolution;
236 HRESULT hr = GetSolutionObject(vsIDE, vsSolution);
237 ReportHRESULT(hr, "GetSolutionObject");
240 GetSolutionFullName(vsSolution, fullName);
241 ReportHRESULT(hr, "GetSolutionFullName");
247 //! Get all running objects from the Windows running object table.
248 //! Save them in a map by their display names.
249 HRESULT GetRunningInstances(std::map<std::string, IUnknownPtr>& mrot)
251 // mrot == Map of the Running Object Table
253 IRunningObjectTablePtr runningObjectTable;
254 IEnumMonikerPtr monikerEnumerator;
256 ULONG numFetched = 0;
258 HRESULT hr = GetRunningObjectTable(0, &runningObjectTable);
259 ReportHRESULT(hr, "GetRunningObjectTable");
262 hr = runningObjectTable->EnumRunning(&monikerEnumerator);
263 ReportHRESULT(hr, "EnumRunning");
267 hr = monikerEnumerator->Reset();
268 ReportHRESULT(hr, "Reset");
272 while (S_OK == monikerEnumerator->Next(1, &moniker, &numFetched)) {
273 std::string runningObjectName;
274 IUnknownPtr runningObjectVal;
277 hr = CreateBindCtx(0, &ctx);
278 ReportHRESULT(hr, "CreateBindCtx");
281 LPOLESTR displayName = 0;
282 hr = moniker->GetDisplayName(ctx, 0, &displayName);
283 ReportHRESULT(hr, "GetDisplayName");
285 runningObjectName = (std::string)(_bstr_t)displayName;
286 CoTaskMemFree(displayName);
289 hr = runningObjectTable->GetObject(moniker, &runningObjectVal);
290 ReportHRESULT(hr, "GetObject");
292 mrot.insert(std::make_pair(runningObjectName, runningObjectVal));
304 //! Do the two file names refer to the same Visual Studio solution? Or are
305 //! we perhaps looking for any and all solutions?
306 bool FilesSameSolution(const std::string& slnFile, const std::string& slnName)
308 if (slnFile == "ALL" || slnName == "ALL") {
312 // Otherwise, make lowercase local copies, convert to Unix slashes, and
313 // see if the resulting strings are the same:
314 std::string s1 = cmSystemTools::LowerCase(slnFile);
315 std::string s2 = cmSystemTools::LowerCase(slnName);
316 cmSystemTools::ConvertToUnixSlashes(s1);
317 cmSystemTools::ConvertToUnixSlashes(s2);
322 //! Find instances of Visual Studio with the given solution file
323 //! open. Pass "ALL" for slnFile to gather all running instances
324 //! of Visual Studio.
325 HRESULT FindVisualStudioInstances(const std::string& slnFile,
326 std::vector<IDispatchPtr>& instances)
328 std::map<std::string, IUnknownPtr> mrot;
330 HRESULT hr = GetRunningInstances(mrot);
331 ReportHRESULT(hr, "GetRunningInstances");
334 std::map<std::string, IUnknownPtr>::iterator it;
335 for (it = mrot.begin(); it != mrot.end(); ++it) {
336 if (cmHasLiteralPrefix(it->first, "!VisualStudio.DTE.")) {
337 IDispatchPtr disp(it->second);
338 if (disp != (IDispatch*)0) {
340 hr = GetIDESolutionFullName(disp, slnName);
341 ReportHRESULT(hr, "GetIDESolutionFullName");
343 if (FilesSameSolution(slnFile, slnName)) {
344 instances.push_back(disp);
346 // std::cout << "Found Visual Studio instance." << std::endl;
347 // std::cout << " ROT entry name: " << it->first << std::endl;
348 // std::cout << " ROT entry object: "
349 // << (IUnknown*) it->second << std::endl;
350 // std::cout << " slnFile: " << slnFile << std::endl;
351 // std::cout << " slnName: " << slnName << std::endl;
361 #endif // defined(HAVE_COMDEF_H)
363 int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
364 const std::string& slnFile)
368 LogErrorsAsMessages = false;
370 #if defined(HAVE_COMDEF_H)
371 HRESULT hr = CoInitialize(0);
372 ReportHRESULT(hr, "CoInitialize");
375 std::vector<IDispatchPtr> instances;
376 hr = FindVisualStudioInstances(slnFile, instances);
377 ReportHRESULT(hr, "FindVisualStudioInstances");
380 count = static_cast<int>(instances.size());
383 // Force release all COM pointers before CoUninitialize:
395 //! Get all running objects from the Windows running object table.
396 //! Save them in a map by their display names.
397 int cmCallVisualStudioMacro::CallMacro(const std::string& slnFile,
398 const std::string& macro,
399 const std::string& args,
400 const bool logErrorsAsMessages)
402 int err = 1; // no comdef.h
404 LogErrorsAsMessages = logErrorsAsMessages;
406 #if defined(HAVE_COMDEF_H)
407 err = 2; // error initializing
409 HRESULT hr = CoInitialize(0);
410 ReportHRESULT(hr, "CoInitialize");
413 std::vector<IDispatchPtr> instances;
414 hr = FindVisualStudioInstances(slnFile, instances);
415 ReportHRESULT(hr, "FindVisualStudioInstances");
420 std::vector<IDispatchPtr>::iterator it;
421 for (it = instances.begin(); it != instances.end(); ++it) {
422 hr = InstanceCallMacro(*it, macro, args);
423 ReportHRESULT(hr, "InstanceCallMacro");
426 err = 3; // error attempting to call the macro
430 if (instances.empty()) {
431 // no instances to call
433 // cmSystemTools::Message(
434 // "cmCallVisualStudioMacro::CallMacro no instances found to call",
439 // Force release all COM pointers before CoUninitialize:
448 if (LogErrorsAsMessages) {
449 cmSystemTools::Message("cmCallVisualStudioMacro::CallMacro is not "
450 "supported on this platform");
454 if (err && LogErrorsAsMessages) {
455 std::ostringstream oss;
456 oss << "cmCallVisualStudioMacro::CallMacro failed, err = " << err;
457 cmSystemTools::Message(oss.str());