1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCallVisualStudioMacro.cxx,v $
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"
27 // Just for this file:
29 static bool LogErrorsAsMessages
;
32 #if defined(HAVE_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 )
44 #ifdef _NATIVE_WCHAR_T_DEFINED
46 # pragma comment(lib, "comsuppwd.lib")
48 # pragma comment(lib, "comsuppw.lib")
52 # pragma comment(lib, "comsuppd.lib")
54 # pragma comment(lib, "comsupp.lib")
58 // VS6 only had comsupp.lib:
59 # pragma comment(lib, "comsupp.lib")
63 //----------------------------------------------------------------------------
64 ///! Use ReportHRESULT to make a cmSystemTools::Message after calling
65 ///! a COM method that may have failed.
66 #define ReportHRESULT(hr, context) \
69 if (LogErrorsAsMessages) \
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()); \
81 //----------------------------------------------------------------------------
82 ///! Using the given instance of Visual Studio, call the named macro
83 HRESULT
InstanceCallMacro(
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());
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)");
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
, ¶ms
, &result
, &excep
, &arg
);
131 std::ostringstream oss
;
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
);
174 //----------------------------------------------------------------------------
175 ///! Get the Solution object from the IDE object
176 HRESULT
GetSolutionObject(
178 IDispatchPtr
& vsSolution
)
180 HRESULT hr
= E_POINTER
;
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)");
196 UINT arg
= (UINT
) -1;
199 params
.rgdispidNamedArgs
= 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
, ¶ms
, &result
, &excep
, &arg
);
209 ReportHRESULT(hr
, "Invoke(Solution)");
213 vsSolution
= V_DISPATCH(&result
);
216 VariantClear(&result
);
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
;
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)");
246 UINT arg
= (UINT
) -1;
249 params
.rgdispidNamedArgs
= 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
, ¶ms
, &result
, &excep
, &arg
);
259 ReportHRESULT(hr
, "Invoke(FullName)");
263 fullName
= (std::string
) (_bstr_t
) V_BSTR(&result
);
266 VariantClear(&result
);
274 //----------------------------------------------------------------------------
275 ///! Get the FullName property from the Solution object, given the IDE object
276 HRESULT
GetIDESolutionFullName(
278 std::string
& fullName
)
280 IDispatchPtr vsSolution
;
281 HRESULT hr
= GetSolutionObject(vsIDE
, vsSolution
);
282 ReportHRESULT(hr
, "GetSolutionObject");
286 GetSolutionFullName(vsSolution
, fullName
);
287 ReportHRESULT(hr
, "GetSolutionFullName");
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
;
304 ULONG numFetched
= 0;
306 HRESULT hr
= GetRunningObjectTable(0, &runningObjectTable
);
307 ReportHRESULT(hr
, "GetRunningObjectTable");
311 hr
= runningObjectTable
->EnumRunning(&monikerEnumerator
);
312 ReportHRESULT(hr
, "EnumRunning");
317 hr
= monikerEnumerator
->Reset();
318 ReportHRESULT(hr
, "Reset");
323 while (S_OK
== monikerEnumerator
->Next(1, &moniker
, &numFetched
))
325 std::string runningObjectName
;
326 IUnknownPtr runningObjectVal
;
329 hr
= CreateBindCtx(0, &ctx
);
330 ReportHRESULT(hr
, "CreateBindCtx");
334 LPOLESTR displayName
= 0;
335 hr
= moniker
->GetDisplayName(ctx
, 0, &displayName
);
336 ReportHRESULT(hr
, "GetDisplayName");
339 runningObjectName
= (std::string
) (_bstr_t
) displayName
;
340 CoTaskMemFree(displayName
);
343 hr
= runningObjectTable
->GetObject(moniker
, &runningObjectVal
);
344 ReportHRESULT(hr
, "GetObject");
347 mrot
.insert(std::make_pair(runningObjectName
, runningObjectVal
));
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")
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
);
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");
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)
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;
431 #endif //defined(HAVE_COMDEF_H)
434 //----------------------------------------------------------------------------
435 int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
436 const std::string
& slnFile
)
440 LogErrorsAsMessages
= false;
442 #if defined(HAVE_COMDEF_H)
443 HRESULT hr
= CoInitialize(0);
444 ReportHRESULT(hr
, "CoInitialize");
448 std::vector
<IDispatchPtr
> instances
;
449 hr
= FindVisualStudioInstances(slnFile
, instances
);
450 ReportHRESULT(hr
, "FindVisualStudioInstances");
454 count
= static_cast<int>(instances
.size());
457 // Force release all COM pointers before CoUninitialize:
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");
491 std::vector
<IDispatchPtr
> instances
;
492 hr
= FindVisualStudioInstances(slnFile
, instances
);
493 ReportHRESULT(hr
, "FindVisualStudioInstances");
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");
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",
521 // Force release all COM pointers before CoUninitialize:
530 if (LogErrorsAsMessages
)
532 cmSystemTools::Message("cmCallVisualStudioMacro::CallMacro is not "
533 "supported on this platform");
537 if (err
&& LogErrorsAsMessages
)
539 std::ostringstream oss
;
540 oss
<< "cmCallVisualStudioMacro::CallMacro failed, err = " << err
;
541 cmSystemTools::Message(oss
.str().c_str());