Updated formatting of documentation plus a little reorganization.
[cmake.git] / Source / cmCallVisualStudioMacro.cxx
blob46c296be4f5ec3ce1c2f3118c8f465dab0320af9
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCallVisualStudioMacro.cxx,v $
5 Language: C++
6 Date: $Date: 2008-10-08 18:19:01 $
7 Version: $Revision: 1.5 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
18 #include "cmCallVisualStudioMacro.h"
19 #include "cmSystemTools.h"
22 #if defined(_MSC_VER)
23 #define HAVE_COMDEF_H
24 #endif
27 // Just for this file:
29 static bool LogErrorsAsMessages;
32 #if defined(HAVE_COMDEF_H)
35 #include <comdef.h>
38 //----------------------------------------------------------------------------
39 // Copied from a correct comdef.h to avoid problems with deficient versions
40 // of comdef.h that exist in the wild... Fixes issue #7533.
42 #if ( _MSC_VER >= 1300 )
43 // VS7 and later:
44 #ifdef _NATIVE_WCHAR_T_DEFINED
45 # ifdef _DEBUG
46 # pragma comment(lib, "comsuppwd.lib")
47 # else
48 # pragma comment(lib, "comsuppw.lib")
49 # endif
50 #else
51 # ifdef _DEBUG
52 # pragma comment(lib, "comsuppd.lib")
53 # else
54 # pragma comment(lib, "comsupp.lib")
55 # endif
56 #endif
57 #else
58 // VS6 only had comsupp.lib:
59 # pragma comment(lib, "comsupp.lib")
60 #endif
63 //----------------------------------------------------------------------------
64 ///! Use ReportHRESULT to make a cmSystemTools::Message after calling
65 ///! a COM method that may have failed.
66 #define ReportHRESULT(hr, context) \
67 if (FAILED(hr)) \
68 { \
69 if (LogErrorsAsMessages) \
70 { \
71 std::ostringstream oss; \
72 oss.flags(std::ios::hex); \
73 oss << context << " failed HRESULT, hr = 0x" << hr << std::endl; \
74 oss.flags(std::ios::dec); \
75 oss << __FILE__ << "(" << __LINE__ << ")"; \
76 cmSystemTools::Message(oss.str().c_str()); \
77 } \
81 //----------------------------------------------------------------------------
82 ///! Using the given instance of Visual Studio, call the named macro
83 HRESULT InstanceCallMacro(
84 IDispatch* vsIDE,
85 const std::string& macro,
86 const std::string& args)
88 HRESULT hr = E_POINTER;
90 _bstr_t macroName(macro.c_str());
91 _bstr_t macroArgs(args.c_str());
93 if (0 != vsIDE)
95 DISPID dispid = (DISPID) -1;
96 OLECHAR *name = L"ExecuteCommand";
98 hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
99 LOCALE_USER_DEFAULT, &dispid);
100 ReportHRESULT(hr, "GetIDsOfNames(ExecuteCommand)");
102 if (SUCCEEDED(hr))
104 VARIANTARG vargs[2];
105 DISPPARAMS params;
106 VARIANT result;
107 EXCEPINFO excep;
108 UINT arg = (UINT) -1;
110 // No VariantInit or VariantClear calls are necessary for
111 // these two vargs. They are both local _bstr_t variables
112 // that remain in scope for the duration of the Invoke call.
114 V_VT(&vargs[1]) = VT_BSTR;
115 V_BSTR(&vargs[1]) = macroName;
116 V_VT(&vargs[0]) = VT_BSTR;
117 V_BSTR(&vargs[0]) = macroArgs;
119 params.rgvarg = &vargs[0];
120 params.rgdispidNamedArgs = 0;
121 params.cArgs = sizeof(vargs)/sizeof(vargs[0]);
122 params.cNamedArgs = 0;
124 VariantInit(&result);
126 memset(&excep, 0, sizeof(excep));
128 hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
129 DISPATCH_METHOD, &params, &result, &excep, &arg);
131 std::ostringstream oss;
132 oss << std::endl;
133 oss << "Invoke(ExecuteCommand)" << std::endl;
134 oss << " Macro: " << macro.c_str() << std::endl;
135 oss << " Args: " << args.c_str() << std::endl;
137 if (DISP_E_EXCEPTION == hr)
139 oss << "DISP_E_EXCEPTION EXCEPINFO:" << excep.wCode << std::endl;
140 oss << " wCode: " << excep.wCode << std::endl;
141 oss << " wReserved: " << excep.wReserved << std::endl;
142 if (excep.bstrSource)
144 oss << " bstrSource: " <<
145 (const char*)(_bstr_t)excep.bstrSource << std::endl;
147 if (excep.bstrDescription)
149 oss << " bstrDescription: " <<
150 (const char*)(_bstr_t)excep.bstrDescription << std::endl;
152 if (excep.bstrHelpFile)
154 oss << " bstrHelpFile: " <<
155 (const char*)(_bstr_t)excep.bstrHelpFile << std::endl;
157 oss << " dwHelpContext: " << excep.dwHelpContext << std::endl;
158 oss << " pvReserved: " << excep.pvReserved << std::endl;
159 oss << " pfnDeferredFillIn: " << excep.pfnDeferredFillIn << std::endl;
160 oss << " scode: " << excep.scode << std::endl;
163 std::string exstr(oss.str());
164 ReportHRESULT(hr, exstr.c_str());
166 VariantClear(&result);
170 return hr;
174 //----------------------------------------------------------------------------
175 ///! Get the Solution object from the IDE object
176 HRESULT GetSolutionObject(
177 IDispatch* vsIDE,
178 IDispatchPtr& vsSolution)
180 HRESULT hr = E_POINTER;
182 if (0 != vsIDE)
184 DISPID dispid = (DISPID) -1;
185 OLECHAR *name = L"Solution";
187 hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
188 LOCALE_USER_DEFAULT, &dispid);
189 ReportHRESULT(hr, "GetIDsOfNames(Solution)");
191 if (SUCCEEDED(hr))
193 DISPPARAMS params;
194 VARIANT result;
195 EXCEPINFO excep;
196 UINT arg = (UINT) -1;
198 params.rgvarg = 0;
199 params.rgdispidNamedArgs = 0;
200 params.cArgs = 0;
201 params.cNamedArgs = 0;
203 VariantInit(&result);
205 memset(&excep, 0, sizeof(excep));
207 hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
208 DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
209 ReportHRESULT(hr, "Invoke(Solution)");
211 if (SUCCEEDED(hr))
213 vsSolution = V_DISPATCH(&result);
216 VariantClear(&result);
220 return hr;
224 //----------------------------------------------------------------------------
225 ///! Get the FullName property from the Solution object
226 HRESULT GetSolutionFullName(
227 IDispatch* vsSolution,
228 std::string& fullName)
230 HRESULT hr = E_POINTER;
232 if (0 != vsSolution)
234 DISPID dispid = (DISPID) -1;
235 OLECHAR *name = L"FullName";
237 hr = vsSolution->GetIDsOfNames(IID_NULL, &name, 1,
238 LOCALE_USER_DEFAULT, &dispid);
239 ReportHRESULT(hr, "GetIDsOfNames(FullName)");
241 if (SUCCEEDED(hr))
243 DISPPARAMS params;
244 VARIANT result;
245 EXCEPINFO excep;
246 UINT arg = (UINT) -1;
248 params.rgvarg = 0;
249 params.rgdispidNamedArgs = 0;
250 params.cArgs = 0;
251 params.cNamedArgs = 0;
253 VariantInit(&result);
255 memset(&excep, 0, sizeof(excep));
257 hr = vsSolution->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
258 DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
259 ReportHRESULT(hr, "Invoke(FullName)");
261 if (SUCCEEDED(hr))
263 fullName = (std::string) (_bstr_t) V_BSTR(&result);
266 VariantClear(&result);
270 return hr;
274 //----------------------------------------------------------------------------
275 ///! Get the FullName property from the Solution object, given the IDE object
276 HRESULT GetIDESolutionFullName(
277 IDispatch* vsIDE,
278 std::string& fullName)
280 IDispatchPtr vsSolution;
281 HRESULT hr = GetSolutionObject(vsIDE, vsSolution);
282 ReportHRESULT(hr, "GetSolutionObject");
284 if (SUCCEEDED(hr))
286 GetSolutionFullName(vsSolution, fullName);
287 ReportHRESULT(hr, "GetSolutionFullName");
290 return hr;
294 //----------------------------------------------------------------------------
295 ///! Get all running objects from the Windows running object table.
296 ///! Save them in a map by their display names.
297 HRESULT GetRunningInstances(std::map<std::string, IUnknownPtr>& mrot)
299 // mrot == Map of the Running Object Table
301 IRunningObjectTablePtr runningObjectTable;
302 IEnumMonikerPtr monikerEnumerator;
303 IMonikerPtr moniker;
304 ULONG numFetched = 0;
306 HRESULT hr = GetRunningObjectTable(0, &runningObjectTable);
307 ReportHRESULT(hr, "GetRunningObjectTable");
309 if(SUCCEEDED(hr))
311 hr = runningObjectTable->EnumRunning(&monikerEnumerator);
312 ReportHRESULT(hr, "EnumRunning");
315 if(SUCCEEDED(hr))
317 hr = monikerEnumerator->Reset();
318 ReportHRESULT(hr, "Reset");
321 if(SUCCEEDED(hr))
323 while (S_OK == monikerEnumerator->Next(1, &moniker, &numFetched))
325 std::string runningObjectName;
326 IUnknownPtr runningObjectVal;
327 IBindCtxPtr ctx;
329 hr = CreateBindCtx(0, &ctx);
330 ReportHRESULT(hr, "CreateBindCtx");
332 if(SUCCEEDED(hr))
334 LPOLESTR displayName = 0;
335 hr = moniker->GetDisplayName(ctx, 0, &displayName);
336 ReportHRESULT(hr, "GetDisplayName");
337 if (displayName)
339 runningObjectName = (std::string) (_bstr_t) displayName;
340 CoTaskMemFree(displayName);
343 hr = runningObjectTable->GetObject(moniker, &runningObjectVal);
344 ReportHRESULT(hr, "GetObject");
345 if(SUCCEEDED(hr))
347 mrot.insert(std::make_pair(runningObjectName, runningObjectVal));
351 numFetched = 0;
352 moniker = 0;
356 return hr;
360 //----------------------------------------------------------------------------
361 ///! Do the two file names refer to the same Visual Studio solution? Or are
362 ///! we perhaps looking for any and all solutions?
363 bool FilesSameSolution(
364 const std::string& slnFile,
365 const std::string& slnName)
367 if (slnFile == "ALL" || slnName == "ALL")
369 return true;
372 // Otherwise, make lowercase local copies, convert to Unix slashes, and
373 // see if the resulting strings are the same:
374 std::string s1 = cmSystemTools::LowerCase(slnFile);
375 std::string s2 = cmSystemTools::LowerCase(slnName);
376 cmSystemTools::ConvertToUnixSlashes(s1);
377 cmSystemTools::ConvertToUnixSlashes(s2);
379 return s1 == s2;
383 //----------------------------------------------------------------------------
384 ///! Find instances of Visual Studio with the given solution file
385 ///! open. Pass "ALL" for slnFile to gather all running instances
386 ///! of Visual Studio.
387 HRESULT FindVisualStudioInstances(
388 const std::string& slnFile,
389 std::vector<IDispatchPtr>& instances)
391 std::map<std::string, IUnknownPtr> mrot;
393 HRESULT hr = GetRunningInstances(mrot);
394 ReportHRESULT(hr, "GetRunningInstances");
396 if(SUCCEEDED(hr))
398 std::map<std::string, IUnknownPtr>::iterator it;
399 for(it = mrot.begin(); it != mrot.end(); ++it)
401 if (cmSystemTools::StringStartsWith(it->first.c_str(),
402 "!VisualStudio.DTE."))
404 IDispatchPtr disp(it->second);
405 if (disp != (IDispatch*) 0)
407 std::string slnName;
408 hr = GetIDESolutionFullName(disp, slnName);
409 ReportHRESULT(hr, "GetIDESolutionFullName");
411 if (FilesSameSolution(slnFile, slnName))
413 instances.push_back(disp);
415 //std::cout << "Found Visual Studio instance." << std::endl;
416 //std::cout << " ROT entry name: " << it->first << std::endl;
417 //std::cout << " ROT entry object: "
418 // << (IUnknown*) it->second << std::endl;
419 //std::cout << " slnFile: " << slnFile << std::endl;
420 //std::cout << " slnName: " << slnName << std::endl;
427 return hr;
431 #endif //defined(HAVE_COMDEF_H)
434 //----------------------------------------------------------------------------
435 int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
436 const std::string& slnFile)
438 int count = 0;
440 LogErrorsAsMessages = false;
442 #if defined(HAVE_COMDEF_H)
443 HRESULT hr = CoInitialize(0);
444 ReportHRESULT(hr, "CoInitialize");
446 if(SUCCEEDED(hr))
448 std::vector<IDispatchPtr> instances;
449 hr = FindVisualStudioInstances(slnFile, instances);
450 ReportHRESULT(hr, "FindVisualStudioInstances");
452 if(SUCCEEDED(hr))
454 count = static_cast<int>(instances.size());
457 // Force release all COM pointers before CoUninitialize:
458 instances.clear();
460 CoUninitialize();
462 #else
463 (void)slnFile;
464 #endif
466 return count;
470 //----------------------------------------------------------------------------
471 ///! Get all running objects from the Windows running object table.
472 ///! Save them in a map by their display names.
473 int cmCallVisualStudioMacro::CallMacro(
474 const std::string& slnFile,
475 const std::string& macro,
476 const std::string& args,
477 const bool logErrorsAsMessages)
479 int err = 1; // no comdef.h
481 LogErrorsAsMessages = logErrorsAsMessages;
483 #if defined(HAVE_COMDEF_H)
484 err = 2; // error initializing
486 HRESULT hr = CoInitialize(0);
487 ReportHRESULT(hr, "CoInitialize");
489 if(SUCCEEDED(hr))
491 std::vector<IDispatchPtr> instances;
492 hr = FindVisualStudioInstances(slnFile, instances);
493 ReportHRESULT(hr, "FindVisualStudioInstances");
495 if(SUCCEEDED(hr))
497 err = 0; // no error
499 std::vector<IDispatchPtr>::iterator it;
500 for(it = instances.begin(); it != instances.end(); ++it)
502 hr = InstanceCallMacro(*it, macro, args);
503 ReportHRESULT(hr, "InstanceCallMacro");
505 if (FAILED(hr))
507 err = 3; // error attempting to call the macro
511 if(0 == instances.size())
513 // no instances to call
515 //cmSystemTools::Message(
516 // "cmCallVisualStudioMacro::CallMacro no instances found to call",
517 // "Warning");
521 // Force release all COM pointers before CoUninitialize:
522 instances.clear();
524 CoUninitialize();
526 #else
527 (void)slnFile;
528 (void)macro;
529 (void)args;
530 if (LogErrorsAsMessages)
532 cmSystemTools::Message("cmCallVisualStudioMacro::CallMacro is not "
533 "supported on this platform");
535 #endif
537 if (err && LogErrorsAsMessages)
539 std::ostringstream oss;
540 oss << "cmCallVisualStudioMacro::CallMacro failed, err = " << err;
541 cmSystemTools::Message(oss.str().c_str());
544 return 0;