1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
13 #include "cmCallVisualStudioMacro.h"
14 #include "cmSystemTools.h"
22 // Just for this file:
24 static bool LogErrorsAsMessages;
27 #if defined(HAVE_COMDEF_H)
33 //----------------------------------------------------------------------------
34 // Copied from a correct comdef.h to avoid problems with deficient versions
35 // of comdef.h that exist in the wild... Fixes issue #7533.
37 #if ( _MSC_VER >= 1300 )
39 #ifdef _NATIVE_WCHAR_T_DEFINED
41 # pragma comment(lib, "comsuppwd.lib")
43 # pragma comment(lib, "comsuppw.lib")
47 # pragma comment(lib, "comsuppd.lib")
49 # pragma comment(lib, "comsupp.lib")
53 // VS6 only had comsupp.lib:
54 # pragma comment(lib, "comsupp.lib")
58 //----------------------------------------------------------------------------
59 ///! Use ReportHRESULT to make a cmSystemTools::Message after calling
60 ///! a COM method that may have failed.
61 #define ReportHRESULT(hr, context) \
64 if (LogErrorsAsMessages) \
66 std::ostringstream oss; \
67 oss.flags(std::ios::hex); \
68 oss << context << " failed HRESULT, hr = 0x" << hr << std::endl; \
69 oss.flags(std::ios::dec); \
70 oss << __FILE__ << "(" << __LINE__ << ")"; \
71 cmSystemTools::Message(oss.str().c_str()); \
76 //----------------------------------------------------------------------------
77 ///! Using the given instance of Visual Studio, call the named macro
78 HRESULT InstanceCallMacro(
80 const std::string& macro,
81 const std::string& args)
83 HRESULT hr = E_POINTER;
85 _bstr_t macroName(macro.c_str());
86 _bstr_t macroArgs(args.c_str());
90 DISPID dispid = (DISPID) -1;
91 OLECHAR *name = L"ExecuteCommand";
93 hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
94 LOCALE_USER_DEFAULT, &dispid);
95 ReportHRESULT(hr, "GetIDsOfNames(ExecuteCommand)");
103 UINT arg = (UINT) -1;
105 // No VariantInit or VariantClear calls are necessary for
106 // these two vargs. They are both local _bstr_t variables
107 // that remain in scope for the duration of the Invoke call.
109 V_VT(&vargs[1]) = VT_BSTR;
110 V_BSTR(&vargs[1]) = macroName;
111 V_VT(&vargs[0]) = VT_BSTR;
112 V_BSTR(&vargs[0]) = macroArgs;
114 params.rgvarg = &vargs[0];
115 params.rgdispidNamedArgs = 0;
116 params.cArgs = sizeof(vargs)/sizeof(vargs[0]);
117 params.cNamedArgs = 0;
119 VariantInit(&result);
121 memset(&excep, 0, sizeof(excep));
123 hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
124 DISPATCH_METHOD, ¶ms, &result, &excep, &arg);
126 std::ostringstream oss;
128 oss << "Invoke(ExecuteCommand)" << std::endl;
129 oss << " Macro: " << macro.c_str() << std::endl;
130 oss << " Args: " << args.c_str() << std::endl;
132 if (DISP_E_EXCEPTION == hr)
134 oss << "DISP_E_EXCEPTION EXCEPINFO:" << excep.wCode << std::endl;
135 oss << " wCode: " << excep.wCode << std::endl;
136 oss << " wReserved: " << excep.wReserved << std::endl;
137 if (excep.bstrSource)
139 oss << " bstrSource: " <<
140 (const char*)(_bstr_t)excep.bstrSource << std::endl;
142 if (excep.bstrDescription)
144 oss << " bstrDescription: " <<
145 (const char*)(_bstr_t)excep.bstrDescription << std::endl;
147 if (excep.bstrHelpFile)
149 oss << " bstrHelpFile: " <<
150 (const char*)(_bstr_t)excep.bstrHelpFile << std::endl;
152 oss << " dwHelpContext: " << excep.dwHelpContext << std::endl;
153 oss << " pvReserved: " << excep.pvReserved << std::endl;
154 oss << " pfnDeferredFillIn: " << excep.pfnDeferredFillIn << std::endl;
155 oss << " scode: " << excep.scode << std::endl;
158 std::string exstr(oss.str());
159 ReportHRESULT(hr, exstr.c_str());
161 VariantClear(&result);
169 //----------------------------------------------------------------------------
170 ///! Get the Solution object from the IDE object
171 HRESULT GetSolutionObject(
173 IDispatchPtr& vsSolution)
175 HRESULT hr = E_POINTER;
179 DISPID dispid = (DISPID) -1;
180 OLECHAR *name = L"Solution";
182 hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
183 LOCALE_USER_DEFAULT, &dispid);
184 ReportHRESULT(hr, "GetIDsOfNames(Solution)");
191 UINT arg = (UINT) -1;
194 params.rgdispidNamedArgs = 0;
196 params.cNamedArgs = 0;
198 VariantInit(&result);
200 memset(&excep, 0, sizeof(excep));
202 hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
203 DISPATCH_PROPERTYGET, ¶ms, &result, &excep, &arg);
204 ReportHRESULT(hr, "Invoke(Solution)");
208 vsSolution = V_DISPATCH(&result);
211 VariantClear(&result);
219 //----------------------------------------------------------------------------
220 ///! Get the FullName property from the Solution object
221 HRESULT GetSolutionFullName(
222 IDispatch* vsSolution,
223 std::string& fullName)
225 HRESULT hr = E_POINTER;
229 DISPID dispid = (DISPID) -1;
230 OLECHAR *name = L"FullName";
232 hr = vsSolution->GetIDsOfNames(IID_NULL, &name, 1,
233 LOCALE_USER_DEFAULT, &dispid);
234 ReportHRESULT(hr, "GetIDsOfNames(FullName)");
241 UINT arg = (UINT) -1;
244 params.rgdispidNamedArgs = 0;
246 params.cNamedArgs = 0;
248 VariantInit(&result);
250 memset(&excep, 0, sizeof(excep));
252 hr = vsSolution->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
253 DISPATCH_PROPERTYGET, ¶ms, &result, &excep, &arg);
254 ReportHRESULT(hr, "Invoke(FullName)");
258 fullName = (std::string) (_bstr_t) V_BSTR(&result);
261 VariantClear(&result);
269 //----------------------------------------------------------------------------
270 ///! Get the FullName property from the Solution object, given the IDE object
271 HRESULT GetIDESolutionFullName(
273 std::string& fullName)
275 IDispatchPtr vsSolution;
276 HRESULT hr = GetSolutionObject(vsIDE, vsSolution);
277 ReportHRESULT(hr, "GetSolutionObject");
281 GetSolutionFullName(vsSolution, fullName);
282 ReportHRESULT(hr, "GetSolutionFullName");
289 //----------------------------------------------------------------------------
290 ///! Get all running objects from the Windows running object table.
291 ///! Save them in a map by their display names.
292 HRESULT GetRunningInstances(std::map<std::string, IUnknownPtr>& mrot)
294 // mrot == Map of the Running Object Table
296 IRunningObjectTablePtr runningObjectTable;
297 IEnumMonikerPtr monikerEnumerator;
299 ULONG numFetched = 0;
301 HRESULT hr = GetRunningObjectTable(0, &runningObjectTable);
302 ReportHRESULT(hr, "GetRunningObjectTable");
306 hr = runningObjectTable->EnumRunning(&monikerEnumerator);
307 ReportHRESULT(hr, "EnumRunning");
312 hr = monikerEnumerator->Reset();
313 ReportHRESULT(hr, "Reset");
318 while (S_OK == monikerEnumerator->Next(1, &moniker, &numFetched))
320 std::string runningObjectName;
321 IUnknownPtr runningObjectVal;
324 hr = CreateBindCtx(0, &ctx);
325 ReportHRESULT(hr, "CreateBindCtx");
329 LPOLESTR displayName = 0;
330 hr = moniker->GetDisplayName(ctx, 0, &displayName);
331 ReportHRESULT(hr, "GetDisplayName");
334 runningObjectName = (std::string) (_bstr_t) displayName;
335 CoTaskMemFree(displayName);
338 hr = runningObjectTable->GetObject(moniker, &runningObjectVal);
339 ReportHRESULT(hr, "GetObject");
342 mrot.insert(std::make_pair(runningObjectName, runningObjectVal));
355 //----------------------------------------------------------------------------
356 ///! Do the two file names refer to the same Visual Studio solution? Or are
357 ///! we perhaps looking for any and all solutions?
358 bool FilesSameSolution(
359 const std::string& slnFile,
360 const std::string& slnName)
362 if (slnFile == "ALL" || slnName == "ALL")
367 // Otherwise, make lowercase local copies, convert to Unix slashes, and
368 // see if the resulting strings are the same:
369 std::string s1 = cmSystemTools::LowerCase(slnFile);
370 std::string s2 = cmSystemTools::LowerCase(slnName);
371 cmSystemTools::ConvertToUnixSlashes(s1);
372 cmSystemTools::ConvertToUnixSlashes(s2);
378 //----------------------------------------------------------------------------
379 ///! Find instances of Visual Studio with the given solution file
380 ///! open. Pass "ALL" for slnFile to gather all running instances
381 ///! of Visual Studio.
382 HRESULT FindVisualStudioInstances(
383 const std::string& slnFile,
384 std::vector<IDispatchPtr>& instances)
386 std::map<std::string, IUnknownPtr> mrot;
388 HRESULT hr = GetRunningInstances(mrot);
389 ReportHRESULT(hr, "GetRunningInstances");
393 std::map<std::string, IUnknownPtr>::iterator it;
394 for(it = mrot.begin(); it != mrot.end(); ++it)
396 if (cmSystemTools::StringStartsWith(it->first.c_str(),
397 "!VisualStudio.DTE."))
399 IDispatchPtr disp(it->second);
400 if (disp != (IDispatch*) 0)
403 hr = GetIDESolutionFullName(disp, slnName);
404 ReportHRESULT(hr, "GetIDESolutionFullName");
406 if (FilesSameSolution(slnFile, slnName))
408 instances.push_back(disp);
410 //std::cout << "Found Visual Studio instance." << std::endl;
411 //std::cout << " ROT entry name: " << it->first << std::endl;
412 //std::cout << " ROT entry object: "
413 // << (IUnknown*) it->second << std::endl;
414 //std::cout << " slnFile: " << slnFile << std::endl;
415 //std::cout << " slnName: " << slnName << std::endl;
426 #endif //defined(HAVE_COMDEF_H)
429 //----------------------------------------------------------------------------
430 int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
431 const std::string& slnFile)
435 LogErrorsAsMessages = false;
437 #if defined(HAVE_COMDEF_H)
438 HRESULT hr = CoInitialize(0);
439 ReportHRESULT(hr, "CoInitialize");
443 std::vector<IDispatchPtr> instances;
444 hr = FindVisualStudioInstances(slnFile, instances);
445 ReportHRESULT(hr, "FindVisualStudioInstances");
449 count = static_cast<int>(instances.size());
452 // Force release all COM pointers before CoUninitialize:
465 //----------------------------------------------------------------------------
466 ///! Get all running objects from the Windows running object table.
467 ///! Save them in a map by their display names.
468 int cmCallVisualStudioMacro::CallMacro(
469 const std::string& slnFile,
470 const std::string& macro,
471 const std::string& args,
472 const bool logErrorsAsMessages)
474 int err = 1; // no comdef.h
476 LogErrorsAsMessages = logErrorsAsMessages;
478 #if defined(HAVE_COMDEF_H)
479 err = 2; // error initializing
481 HRESULT hr = CoInitialize(0);
482 ReportHRESULT(hr, "CoInitialize");
486 std::vector<IDispatchPtr> instances;
487 hr = FindVisualStudioInstances(slnFile, instances);
488 ReportHRESULT(hr, "FindVisualStudioInstances");
494 std::vector<IDispatchPtr>::iterator it;
495 for(it = instances.begin(); it != instances.end(); ++it)
497 hr = InstanceCallMacro(*it, macro, args);
498 ReportHRESULT(hr, "InstanceCallMacro");
502 err = 3; // error attempting to call the macro
506 if(0 == instances.size())
508 // no instances to call
510 //cmSystemTools::Message(
511 // "cmCallVisualStudioMacro::CallMacro no instances found to call",
516 // Force release all COM pointers before CoUninitialize:
525 if (LogErrorsAsMessages)
527 cmSystemTools::Message("cmCallVisualStudioMacro::CallMacro is not "
528 "supported on this platform");
532 if (err && LogErrorsAsMessages)
534 std::ostringstream oss;
535 oss << "cmCallVisualStudioMacro::CallMacro failed, err = " << err;
536 cmSystemTools::Message(oss.str().c_str());