summaryrefslogtreecommitdiffstats
path: root/Source/cmCallVisualStudioMacro.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmCallVisualStudioMacro.cxx')
-rw-r--r--Source/cmCallVisualStudioMacro.cxx464
1 files changed, 464 insertions, 0 deletions
diff --git a/Source/cmCallVisualStudioMacro.cxx b/Source/cmCallVisualStudioMacro.cxx
new file mode 100644
index 0000000..635e264
--- /dev/null
+++ b/Source/cmCallVisualStudioMacro.cxx
@@ -0,0 +1,464 @@
+/*=========================================================================
+
+ Program: CMake - Cross-Platform Makefile Generator
+ Module: $RCSfile$
+ Language: C++
+ Date: $Date$
+ Version: $Revision$
+
+ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
+ See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+
+#include "cmCallVisualStudioMacro.h"
+#include "cmSystemTools.h"
+
+
+#if defined(_MSC_VER)
+#define HAVE_COMDEF_H
+#endif
+
+
+#if defined(HAVE_COMDEF_H)
+
+
+#include <comdef.h>
+
+
+//----------------------------------------------------------------------------
+///! Use ReportHRESULT to make a cmSystemTools::Error after calling
+///! a COM method that may have failed.
+#define ReportHRESULT(hr, context) \
+ if (FAILED(hr)) \
+ { \
+ std::ostringstream oss; \
+ oss.flags(std::ios::hex); \
+ oss << context << " failed HRESULT, hr = 0x" << hr << std::endl; \
+ oss.flags(std::ios::dec); \
+ oss << __FILE__ << "(" << __LINE__ << ")"; \
+ cmSystemTools::Error(oss.str().c_str()); \
+ }
+
+
+//----------------------------------------------------------------------------
+///! Using the given instance of Visual Studio, call the named macro
+HRESULT InstanceCallMacro(
+ IDispatch* vsIDE,
+ const std::string& macro,
+ const std::string& args)
+{
+ HRESULT hr = E_POINTER;
+
+ _bstr_t macroName(macro.c_str());
+ _bstr_t macroArgs(args.c_str());
+
+ if (0 != vsIDE)
+ {
+ DISPID dispid = (DISPID) -1;
+ OLECHAR *name = L"ExecuteCommand";
+
+ hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
+ LOCALE_USER_DEFAULT, &dispid);
+ ReportHRESULT(hr, "GetIDsOfNames(ExecuteCommand)");
+
+ if (SUCCEEDED(hr))
+ {
+ VARIANTARG vargs[2];
+ DISPPARAMS params;
+ VARIANT result;
+ EXCEPINFO excep;
+ UINT arg = (UINT) -1;
+
+ // No VariantInit or VariantClear calls are necessary for
+ // these two vargs. They are both local _bstr_t variables
+ // that remain in scope for the duration of the Invoke call.
+ //
+ V_VT(&vargs[1]) = VT_BSTR;
+ V_BSTR(&vargs[1]) = macroName;
+ V_VT(&vargs[0]) = VT_BSTR;
+ V_BSTR(&vargs[0]) = macroArgs;
+
+ params.rgvarg = &vargs[0];
+ params.rgdispidNamedArgs = 0;
+ params.cArgs = sizeof(vargs)/sizeof(vargs[0]);
+ params.cNamedArgs = 0;
+
+ VariantInit(&result);
+
+ memset(&excep, 0, sizeof(excep));
+
+ hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
+ DISPATCH_METHOD, &params, &result, &excep, &arg);
+ ReportHRESULT(hr, "Invoke(ExecuteCommand)");
+
+ VariantClear(&result);
+ }
+ }
+
+ return hr;
+}
+
+
+//----------------------------------------------------------------------------
+///! Get the Solution object from the IDE object
+HRESULT GetSolutionObject(
+ IDispatch* vsIDE,
+ IDispatchPtr& vsSolution)
+{
+ HRESULT hr = E_POINTER;
+
+ if (0 != vsIDE)
+ {
+ DISPID dispid = (DISPID) -1;
+ OLECHAR *name = L"Solution";
+
+ hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
+ LOCALE_USER_DEFAULT, &dispid);
+ ReportHRESULT(hr, "GetIDsOfNames(Solution)");
+
+ if (SUCCEEDED(hr))
+ {
+ DISPPARAMS params;
+ VARIANT result;
+ EXCEPINFO excep;
+ UINT arg = (UINT) -1;
+
+ params.rgvarg = 0;
+ params.rgdispidNamedArgs = 0;
+ params.cArgs = 0;
+ params.cNamedArgs = 0;
+
+ VariantInit(&result);
+
+ memset(&excep, 0, sizeof(excep));
+
+ hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
+ DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
+ ReportHRESULT(hr, "Invoke(Solution)");
+
+ if (SUCCEEDED(hr))
+ {
+ vsSolution = V_DISPATCH(&result);
+ }
+
+ VariantClear(&result);
+ }
+ }
+
+ return hr;
+}
+
+
+//----------------------------------------------------------------------------
+///! Get the FullName property from the Solution object
+HRESULT GetSolutionFullName(
+ IDispatch* vsSolution,
+ std::string& fullName)
+{
+ HRESULT hr = E_POINTER;
+
+ if (0 != vsSolution)
+ {
+ DISPID dispid = (DISPID) -1;
+ OLECHAR *name = L"FullName";
+
+ hr = vsSolution->GetIDsOfNames(IID_NULL, &name, 1,
+ LOCALE_USER_DEFAULT, &dispid);
+ ReportHRESULT(hr, "GetIDsOfNames(FullName)");
+
+ if (SUCCEEDED(hr))
+ {
+ DISPPARAMS params;
+ VARIANT result;
+ EXCEPINFO excep;
+ UINT arg = (UINT) -1;
+
+ params.rgvarg = 0;
+ params.rgdispidNamedArgs = 0;
+ params.cArgs = 0;
+ params.cNamedArgs = 0;
+
+ VariantInit(&result);
+
+ memset(&excep, 0, sizeof(excep));
+
+ hr = vsSolution->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
+ DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
+ ReportHRESULT(hr, "Invoke(FullName)");
+
+ if (SUCCEEDED(hr))
+ {
+ fullName = (std::string) (_bstr_t) V_BSTR(&result);
+ }
+
+ VariantClear(&result);
+ }
+ }
+
+ return hr;
+}
+
+
+//----------------------------------------------------------------------------
+///! Get the FullName property from the Solution object, given the IDE object
+HRESULT GetIDESolutionFullName(
+ IDispatch* vsIDE,
+ std::string& fullName)
+{
+ IDispatchPtr vsSolution;
+ HRESULT hr = GetSolutionObject(vsIDE, vsSolution);
+ ReportHRESULT(hr, "GetSolutionObject");
+
+ if (SUCCEEDED(hr))
+ {
+ GetSolutionFullName(vsSolution, fullName);
+ ReportHRESULT(hr, "GetSolutionFullName");
+ }
+
+ return hr;
+}
+
+
+//----------------------------------------------------------------------------
+///! Get all running objects from the Windows running object table.
+///! Save them in a map by their display names.
+HRESULT GetRunningInstances(std::map<std::string, IUnknownPtr>& mrot)
+{
+ // mrot == Map of the Running Object Table
+
+ IRunningObjectTablePtr runningObjectTable;
+ IEnumMonikerPtr monikerEnumerator;
+ IMonikerPtr moniker;
+ ULONG numFetched = 0;
+
+ HRESULT hr = GetRunningObjectTable(0, &runningObjectTable);
+ ReportHRESULT(hr, "GetRunningObjectTable");
+
+ if(SUCCEEDED(hr))
+ {
+ hr = runningObjectTable->EnumRunning(&monikerEnumerator);
+ ReportHRESULT(hr, "EnumRunning");
+ }
+
+ if(SUCCEEDED(hr))
+ {
+ hr = monikerEnumerator->Reset();
+ ReportHRESULT(hr, "Reset");
+ }
+
+ if(SUCCEEDED(hr))
+ {
+ while (S_OK == monikerEnumerator->Next(1, &moniker, &numFetched))
+ {
+ std::string runningObjectName;
+ IUnknownPtr runningObjectVal;
+ IBindCtxPtr ctx;
+
+ hr = CreateBindCtx(0, &ctx);
+ ReportHRESULT(hr, "CreateBindCtx");
+
+ if(SUCCEEDED(hr))
+ {
+ LPOLESTR displayName = 0;
+ hr = moniker->GetDisplayName(ctx, 0, &displayName);
+ ReportHRESULT(hr, "GetDisplayName");
+ if (displayName)
+ {
+ runningObjectName = (std::string) (_bstr_t) displayName;
+ CoTaskMemFree(displayName);
+ }
+
+ hr = runningObjectTable->GetObject(moniker, &runningObjectVal);
+ ReportHRESULT(hr, "GetObject");
+ if(SUCCEEDED(hr))
+ {
+ mrot.insert(std::make_pair(runningObjectName, runningObjectVal));
+ }
+ }
+
+ numFetched = 0;
+ moniker = 0;
+ }
+ }
+
+ return hr;
+}
+
+
+//----------------------------------------------------------------------------
+///! Do the two file names refer to the same Visual Studio solution? Or are
+///! we perhaps looking for any and all solutions?
+bool FilesSameSolution(
+ const std::string& slnFile,
+ const std::string& slnName)
+{
+ if (slnFile == "ALL" || slnName == "ALL")
+ {
+ return true;
+ }
+
+ // Otherwise, make lowercase local copies, convert to Unix slashes, and
+ // see if the resulting strings are the same:
+ std::string s1 = cmSystemTools::LowerCase(slnFile);
+ std::string s2 = cmSystemTools::LowerCase(slnName);
+ cmSystemTools::ConvertToUnixSlashes(s1);
+ cmSystemTools::ConvertToUnixSlashes(s2);
+
+ return s1 == s2;
+}
+
+
+//----------------------------------------------------------------------------
+///! Find instances of Visual Studio with the given solution file
+///! open. Pass "ALL" for slnFile to gather all running instances
+///! of Visual Studio.
+HRESULT FindVisualStudioInstances(
+ const std::string& slnFile,
+ std::vector<IDispatchPtr>& instances)
+{
+ std::map<std::string, IUnknownPtr> mrot;
+
+ HRESULT hr = GetRunningInstances(mrot);
+ ReportHRESULT(hr, "GetRunningInstances");
+
+ if(SUCCEEDED(hr))
+ {
+ std::map<std::string, IUnknownPtr>::iterator it;
+ for(it = mrot.begin(); it != mrot.end(); ++it)
+ {
+ if (cmSystemTools::StringStartsWith(it->first.c_str(),
+ "!VisualStudio.DTE."))
+ {
+ IDispatchPtr disp(it->second);
+ if (disp != (IDispatch*) 0)
+ {
+ std::string slnName;
+ hr = GetIDESolutionFullName(disp, slnName);
+ ReportHRESULT(hr, "GetIDESolutionFullName");
+
+ if (FilesSameSolution(slnFile, slnName))
+ {
+ instances.push_back(disp);
+
+ //std::cout << "Found Visual Studio instance." << std::endl;
+ //std::cout << " ROT entry name: " << it->first << std::endl;
+ //std::cout << " ROT entry object: " << (IUnknown*) it->second << std::endl;
+ //std::cout << " slnFile: " << slnFile << std::endl;
+ //std::cout << " slnName: " << slnName << std::endl;
+ }
+ }
+ }
+ }
+ }
+
+ return hr;
+}
+
+
+#endif //defined(HAVE_COMDEF_H)
+
+
+//----------------------------------------------------------------------------
+int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
+ const std::string& slnFile)
+{
+ int count = 0;
+
+#if defined(HAVE_COMDEF_H)
+ HRESULT hr = CoInitialize(0);
+ ReportHRESULT(hr, "CoInitialize");
+
+ if(SUCCEEDED(hr))
+ {
+ std::vector<IDispatchPtr> instances;
+ hr = FindVisualStudioInstances(slnFile, instances);
+ ReportHRESULT(hr, "FindVisualStudioInstances");
+
+ if(SUCCEEDED(hr))
+ {
+ count = instances.size();
+ }
+
+ // Force release all COM pointers before CoUninitialize:
+ instances.clear();
+
+ CoUninitialize();
+ }
+#endif
+
+ return count;
+}
+
+
+//----------------------------------------------------------------------------
+///! Get all running objects from the Windows running object table.
+///! Save them in a map by their display names.
+int cmCallVisualStudioMacro::CallMacro(
+ const std::string& slnFile,
+ const std::string& macro,
+ const std::string& args)
+{
+ int err = 1; // no comdef.h
+
+#if defined(HAVE_COMDEF_H)
+ err = 2; // error initializing
+
+ HRESULT hr = CoInitialize(0);
+ ReportHRESULT(hr, "CoInitialize");
+
+ if(SUCCEEDED(hr))
+ {
+ std::vector<IDispatchPtr> instances;
+ hr = FindVisualStudioInstances(slnFile, instances);
+ ReportHRESULT(hr, "FindVisualStudioInstances");
+
+ if(SUCCEEDED(hr))
+ {
+ err = 0; // no error
+
+ std::vector<IDispatchPtr>::iterator it;
+ for(it = instances.begin(); it != instances.end(); ++it)
+ {
+ hr = InstanceCallMacro(*it, macro, args);
+ ReportHRESULT(hr, "InstanceCallMacro");
+
+ if (FAILED(hr))
+ {
+ err = 3; // error attempting to call the macro
+ }
+ }
+
+ if(0 == instances.size())
+ {
+ // no instances to call
+
+ //cmSystemTools::Message(
+ // "cmCallVisualStudioMacro::CallMacro no instances found to call",
+ // "Warning");
+ }
+ }
+
+ // Force release all COM pointers before CoUninitialize:
+ instances.clear();
+
+ CoUninitialize();
+ }
+#else
+ cmSystemTools::Error("cmCallVisualStudioMacro::CallMacro is not "
+ "supported on this platform");
+#endif
+
+ if (err)
+ {
+ std::ostringstream oss;
+ oss << "cmCallVisualStudioMacro::CallMacro failed, err = " << err;
+ cmSystemTools::Error(oss.str().c_str());
+ }
+
+ return err;
+}