packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmCallVisualStudioMacro.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
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 ============================================================================*/
12
13 #include "cmCallVisualStudioMacro.h"
14 #include "cmSystemTools.h"
15
16
17 #if defined(_MSC_VER)
18 #define HAVE_COMDEF_H
19 #endif
20
21
22 // Just for this file:
23 //
24 static bool LogErrorsAsMessages;
25
26
27 #if defined(HAVE_COMDEF_H)
28
29
30 #include <comdef.h>
31
32
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.
36 //
37 #if ( _MSC_VER >= 1300 )
38 // VS7 and later:
39 #ifdef _NATIVE_WCHAR_T_DEFINED
40 # ifdef _DEBUG
41 # pragma comment(lib, "comsuppwd.lib")
42 # else
43 # pragma comment(lib, "comsuppw.lib")
44 # endif
45 #else
46 # ifdef _DEBUG
47 # pragma comment(lib, "comsuppd.lib")
48 # else
49 # pragma comment(lib, "comsupp.lib")
50 # endif
51 #endif
52 #else
53 // VS6 only had comsupp.lib:
54 # pragma comment(lib, "comsupp.lib")
55 #endif
56
57
58 //----------------------------------------------------------------------------
59 ///! Use ReportHRESULT to make a cmSystemTools::Message after calling
60 ///! a COM method that may have failed.
61 #define ReportHRESULT(hr, context) \
62   if (FAILED(hr)) \
63     { \
64     if (LogErrorsAsMessages) \
65       { \
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()); \
72       } \
73     }
74
75
76 //----------------------------------------------------------------------------
77 ///! Using the given instance of Visual Studio, call the named macro
78 HRESULT InstanceCallMacro(
79   IDispatch* vsIDE,
80   const std::string& macro,
81   const std::string& args)
82 {
83   HRESULT hr = E_POINTER;
84
85   _bstr_t macroName(macro.c_str());
86   _bstr_t macroArgs(args.c_str());
87
88   if (0 != vsIDE)
89     {
90     DISPID dispid = (DISPID) -1;
91     OLECHAR *name = L"ExecuteCommand";
92
93     hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
94            LOCALE_USER_DEFAULT, &dispid);
95     ReportHRESULT(hr, "GetIDsOfNames(ExecuteCommand)");
96
97     if (SUCCEEDED(hr))
98       {
99       VARIANTARG vargs[2];
100       DISPPARAMS params;
101       VARIANT result;
102       EXCEPINFO excep;
103       UINT arg = (UINT) -1;
104
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.
108       //
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;
113
114       params.rgvarg = &vargs[0];
115       params.rgdispidNamedArgs = 0;
116       params.cArgs = sizeof(vargs)/sizeof(vargs[0]);
117       params.cNamedArgs = 0;
118
119       VariantInit(&result);
120
121       memset(&excep, 0, sizeof(excep));
122
123       hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
124         DISPATCH_METHOD, &params, &result, &excep, &arg);
125
126       std::ostringstream oss;
127       oss << std::endl;
128       oss << "Invoke(ExecuteCommand)" << std::endl;
129       oss << "  Macro: " << macro.c_str() << std::endl;
130       oss << "  Args: " << args.c_str() << std::endl;
131
132       if (DISP_E_EXCEPTION == hr)
133         {
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)
138           {
139           oss << "  bstrSource: " <<
140             (const char*)(_bstr_t)excep.bstrSource << std::endl;
141           }
142         if (excep.bstrDescription)
143           {
144           oss << "  bstrDescription: " <<
145             (const char*)(_bstr_t)excep.bstrDescription << std::endl;
146           }
147         if (excep.bstrHelpFile)
148           {
149           oss << "  bstrHelpFile: " <<
150             (const char*)(_bstr_t)excep.bstrHelpFile << std::endl;
151           }
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;
156         }
157
158       std::string exstr(oss.str());
159       ReportHRESULT(hr, exstr.c_str());
160
161       VariantClear(&result);
162       }
163     }
164
165   return hr;
166 }
167
168
169 //----------------------------------------------------------------------------
170 ///! Get the Solution object from the IDE object
171 HRESULT GetSolutionObject(
172   IDispatch* vsIDE,
173   IDispatchPtr& vsSolution)
174 {
175   HRESULT hr = E_POINTER;
176
177   if (0 != vsIDE)
178     {
179     DISPID dispid = (DISPID) -1;
180     OLECHAR *name = L"Solution";
181
182     hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
183            LOCALE_USER_DEFAULT, &dispid);
184     ReportHRESULT(hr, "GetIDsOfNames(Solution)");
185
186     if (SUCCEEDED(hr))
187       {
188       DISPPARAMS params;
189       VARIANT result;
190       EXCEPINFO excep;
191       UINT arg = (UINT) -1;
192
193       params.rgvarg = 0;
194       params.rgdispidNamedArgs = 0;
195       params.cArgs = 0;
196       params.cNamedArgs = 0;
197
198       VariantInit(&result);
199
200       memset(&excep, 0, sizeof(excep));
201
202       hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
203         DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
204       ReportHRESULT(hr, "Invoke(Solution)");
205
206       if (SUCCEEDED(hr))
207         {
208         vsSolution = V_DISPATCH(&result);
209         }
210
211       VariantClear(&result);
212       }
213     }
214
215   return hr;
216 }
217
218
219 //----------------------------------------------------------------------------
220 ///! Get the FullName property from the Solution object
221 HRESULT GetSolutionFullName(
222   IDispatch* vsSolution,
223   std::string& fullName)
224 {
225   HRESULT hr = E_POINTER;
226
227   if (0 != vsSolution)
228     {
229     DISPID dispid = (DISPID) -1;
230     OLECHAR *name = L"FullName";
231
232     hr = vsSolution->GetIDsOfNames(IID_NULL, &name, 1,
233            LOCALE_USER_DEFAULT, &dispid);
234     ReportHRESULT(hr, "GetIDsOfNames(FullName)");
235
236     if (SUCCEEDED(hr))
237       {
238       DISPPARAMS params;
239       VARIANT result;
240       EXCEPINFO excep;
241       UINT arg = (UINT) -1;
242
243       params.rgvarg = 0;
244       params.rgdispidNamedArgs = 0;
245       params.cArgs = 0;
246       params.cNamedArgs = 0;
247
248       VariantInit(&result);
249
250       memset(&excep, 0, sizeof(excep));
251
252       hr = vsSolution->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
253         DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
254       ReportHRESULT(hr, "Invoke(FullName)");
255
256       if (SUCCEEDED(hr))
257         {
258         fullName = (std::string) (_bstr_t) V_BSTR(&result);
259         }
260
261       VariantClear(&result);
262       }
263     }
264
265   return hr;
266 }
267
268
269 //----------------------------------------------------------------------------
270 ///! Get the FullName property from the Solution object, given the IDE object
271 HRESULT GetIDESolutionFullName(
272   IDispatch* vsIDE,
273   std::string& fullName)
274 {
275   IDispatchPtr vsSolution;
276   HRESULT hr = GetSolutionObject(vsIDE, vsSolution);
277   ReportHRESULT(hr, "GetSolutionObject");
278
279   if (SUCCEEDED(hr))
280     {
281     GetSolutionFullName(vsSolution, fullName);
282     ReportHRESULT(hr, "GetSolutionFullName");
283     }
284
285   return hr;
286 }
287
288
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)
293 {
294   // mrot == Map of the Running Object Table
295
296   IRunningObjectTablePtr runningObjectTable;
297   IEnumMonikerPtr monikerEnumerator;
298   IMonikerPtr moniker;
299   ULONG numFetched = 0;
300
301   HRESULT hr = GetRunningObjectTable(0, &runningObjectTable);
302   ReportHRESULT(hr, "GetRunningObjectTable");
303
304   if(SUCCEEDED(hr))
305     {
306     hr = runningObjectTable->EnumRunning(&monikerEnumerator);
307     ReportHRESULT(hr, "EnumRunning");
308     }
309
310   if(SUCCEEDED(hr))
311     {
312     hr = monikerEnumerator->Reset();
313     ReportHRESULT(hr, "Reset");
314     }
315
316   if(SUCCEEDED(hr))
317     {
318     while (S_OK == monikerEnumerator->Next(1, &moniker, &numFetched))
319       {
320       std::string runningObjectName;
321       IUnknownPtr runningObjectVal;
322       IBindCtxPtr ctx;
323
324       hr = CreateBindCtx(0, &ctx);
325       ReportHRESULT(hr, "CreateBindCtx");
326
327       if(SUCCEEDED(hr))
328         {
329         LPOLESTR displayName = 0;
330         hr = moniker->GetDisplayName(ctx, 0, &displayName);
331         ReportHRESULT(hr, "GetDisplayName");
332         if (displayName)
333           {
334           runningObjectName = (std::string) (_bstr_t) displayName;
335           CoTaskMemFree(displayName);
336           }
337
338         hr = runningObjectTable->GetObject(moniker, &runningObjectVal);
339         ReportHRESULT(hr, "GetObject");
340         if(SUCCEEDED(hr))
341           {
342           mrot.insert(std::make_pair(runningObjectName, runningObjectVal));
343           }
344         }
345
346       numFetched = 0;
347       moniker = 0;
348       }
349     }
350
351   return hr;
352 }
353
354
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)
361 {
362   if (slnFile == "ALL" || slnName == "ALL")
363     {
364     return true;
365     }
366
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);
373
374   return s1 == s2;
375 }
376
377
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)
385 {
386   std::map<std::string, IUnknownPtr> mrot;
387
388   HRESULT hr = GetRunningInstances(mrot);
389   ReportHRESULT(hr, "GetRunningInstances");
390
391   if(SUCCEEDED(hr))
392     {
393     std::map<std::string, IUnknownPtr>::iterator it;
394     for(it = mrot.begin(); it != mrot.end(); ++it)
395       {
396       if (cmSystemTools::StringStartsWith(it->first.c_str(),
397         "!VisualStudio.DTE."))
398         {
399         IDispatchPtr disp(it->second);
400         if (disp != (IDispatch*) 0)
401           {
402           std::string slnName;
403           hr = GetIDESolutionFullName(disp, slnName);
404           ReportHRESULT(hr, "GetIDESolutionFullName");
405
406           if (FilesSameSolution(slnFile, slnName))
407             {
408             instances.push_back(disp);
409
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;
416             }
417           }
418         }
419       }
420     }
421
422   return hr;
423 }
424
425
426 #endif  //defined(HAVE_COMDEF_H)
427
428
429 //----------------------------------------------------------------------------
430 int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
431   const std::string& slnFile)
432 {
433   int count = 0;
434
435   LogErrorsAsMessages = false;
436
437 #if defined(HAVE_COMDEF_H)
438   HRESULT hr = CoInitialize(0);
439   ReportHRESULT(hr, "CoInitialize");
440
441   if(SUCCEEDED(hr))
442     {
443     std::vector<IDispatchPtr> instances;
444     hr = FindVisualStudioInstances(slnFile, instances);
445     ReportHRESULT(hr, "FindVisualStudioInstances");
446
447     if(SUCCEEDED(hr))
448       {
449       count = static_cast<int>(instances.size());
450       }
451
452     // Force release all COM pointers before CoUninitialize:
453     instances.clear();
454
455     CoUninitialize();
456     }
457 #else
458   (void)slnFile;
459 #endif
460
461   return count;
462 }
463
464
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)
473 {
474   int err = 1; // no comdef.h
475
476   LogErrorsAsMessages = logErrorsAsMessages;
477
478 #if defined(HAVE_COMDEF_H)
479   err = 2; // error initializing
480
481   HRESULT hr = CoInitialize(0);
482   ReportHRESULT(hr, "CoInitialize");
483
484   if(SUCCEEDED(hr))
485     {
486     std::vector<IDispatchPtr> instances;
487     hr = FindVisualStudioInstances(slnFile, instances);
488     ReportHRESULT(hr, "FindVisualStudioInstances");
489
490     if(SUCCEEDED(hr))
491       {
492       err = 0; // no error
493
494       std::vector<IDispatchPtr>::iterator it;
495       for(it = instances.begin(); it != instances.end(); ++it)
496         {
497         hr = InstanceCallMacro(*it, macro, args);
498         ReportHRESULT(hr, "InstanceCallMacro");
499
500         if (FAILED(hr))
501           {
502           err = 3; // error attempting to call the macro
503           }
504         }
505
506       if(0 == instances.size())
507         {
508         // no instances to call
509
510         //cmSystemTools::Message(
511         //  "cmCallVisualStudioMacro::CallMacro no instances found to call",
512         //  "Warning");
513         }
514       }
515
516     // Force release all COM pointers before CoUninitialize:
517     instances.clear();
518
519     CoUninitialize();
520     }
521 #else
522   (void)slnFile;
523   (void)macro;
524   (void)args;
525   if (LogErrorsAsMessages)
526     {
527     cmSystemTools::Message("cmCallVisualStudioMacro::CallMacro is not "
528       "supported on this platform");
529     }
530 #endif
531
532   if (err && LogErrorsAsMessages)
533     {
534     std::ostringstream oss;
535     oss << "cmCallVisualStudioMacro::CallMacro failed, err = " << err;
536     cmSystemTools::Message(oss.str().c_str());
537     }
538
539   return 0;
540 }