diff options
Diffstat (limited to 'Source/cmCallVisualStudioMacro.cxx')
-rw-r--r-- | Source/cmCallVisualStudioMacro.cxx | 464 |
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, ¶ms, &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, ¶ms, &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, ¶ms, &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; +} |