summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIyyappa Murugandi <iyyappam@microsoft.com>2016-12-15 02:53:30 (GMT)
committerBrad King <brad.king@kitware.com>2016-12-16 14:58:43 (GMT)
commit18c8278b622e4cb9b155c5dc4ceac93322bed85f (patch)
tree796d86bf7385c3b4f2ea71d090c67d9540c59e15
parentd47bda00b2e46b3fd6f9e050f3f21b5337ac244b (diff)
downloadCMake-18c8278b622e4cb9b155c5dc4ceac93322bed85f.zip
CMake-18c8278b622e4cb9b155c5dc4ceac93322bed85f.tar.gz
CMake-18c8278b622e4cb9b155c5dc4ceac93322bed85f.tar.bz2
VS: Add helper class to interact with Visual Studio Installer
VS 2017 exports a COM component which can be queried to find if VS 2017 is installed and also other components such as VC toolset and Windows SDKs. Add a helper class to interact with this interface.
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/cmVSSetupHelper.cxx395
-rw-r--r--Source/cmVSSetupHelper.h154
3 files changed, 551 insertions, 0 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index d15fdbe..57b7b25 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -710,6 +710,8 @@ if (WIN32)
cmGhsMultiTargetGenerator.h
cmGhsMultiGpj.cxx
cmGhsMultiGpj.h
+ cmVSSetupHelper.cxx
+ cmVSSetupHelper.h
)
# Add a manifest file to executables on Windows to allow for
diff --git a/Source/cmVSSetupHelper.cxx b/Source/cmVSSetupHelper.cxx
new file mode 100644
index 0000000..d675a2c
--- /dev/null
+++ b/Source/cmVSSetupHelper.cxx
@@ -0,0 +1,395 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmVSSetupHelper.h"
+
+#ifndef VSSetupConstants
+#define VSSetupConstants
+/* clang-format off */
+const IID IID_ISetupConfiguration = {
+ 0x42843719, 0xDB4C, 0x46C2,
+ { 0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B }
+};
+const IID IID_ISetupConfiguration2 = {
+ 0x26AAB78C, 0x4A60, 0x49D6,
+ { 0xAF, 0x3B, 0x3C, 0x35, 0xBC, 0x93, 0x36, 0x5D }
+};
+const IID IID_ISetupPackageReference = {
+ 0xda8d8a16, 0xb2b6, 0x4487,
+ { 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5 }
+};
+const IID IID_ISetupHelper = {
+ 0x42b21b78, 0x6192, 0x463e,
+ { 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c }
+};
+const IID IID_IEnumSetupInstances = {
+ 0x6380BCFF, 0x41D3, 0x4B2E,
+ { 0x8B, 0x2E, 0xBF, 0x8A, 0x68, 0x10, 0xC8, 0x48 }
+};
+const IID IID_ISetupInstance2 = {
+ 0x89143C9A, 0x05AF, 0x49B0,
+ { 0xB7, 0x17, 0x72, 0xE2, 0x18, 0xA2, 0x18, 0x5C }
+};
+const IID IID_ISetupInstance = {
+ 0xB41463C3, 0x8866, 0x43B5,
+ { 0xBC, 0x33, 0x2B, 0x06, 0x76, 0xF7, 0xF4, 0x2E }
+};
+const CLSID CLSID_SetupConfiguration = {
+ 0x177F0C4A, 0x1CD3, 0x4DE7,
+ { 0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D }
+};
+/* clang-format on */
+#endif
+
+const WCHAR* VCToolsetComponent =
+ L"Microsoft.VisualStudio.Component.VC.Tools.x86.x64";
+const WCHAR* Win10SDKComponent =
+ L"Microsoft.VisualStudio.Component.Windows10SDK";
+const WCHAR* Win81SDKComponent =
+ L"Microsoft.VisualStudio.Component.Windows81SDK";
+const WCHAR* ComponentType = L"Component";
+
+cmVSSetupAPIHelper::cmVSSetupAPIHelper()
+ : setupConfig(NULL)
+ , setupConfig2(NULL)
+ , setupHelper(NULL)
+ , initializationFailure(false)
+{
+ comInitialized = CoInitializeEx(NULL, 0);
+ if (SUCCEEDED(comInitialized)) {
+ Initialize();
+ } else {
+ initializationFailure = true;
+ }
+}
+
+cmVSSetupAPIHelper::~cmVSSetupAPIHelper()
+{
+ setupHelper = NULL;
+ setupConfig2 = NULL;
+ setupConfig = NULL;
+ if (SUCCEEDED(comInitialized))
+ CoUninitialize();
+}
+
+bool cmVSSetupAPIHelper::IsVS2017Installed()
+{
+ bool ret = false;
+ if (chosenInstanceInfo.VSInstallLocation.compare(L"") == 0) {
+ ret = EnumerateAndChooseVSInstance();
+ }
+
+ return ret;
+}
+
+bool cmVSSetupAPIHelper::IsWin10SDKInstalled()
+{
+ bool isWin10SDKInstalled = false;
+ if (chosenInstanceInfo.VSInstallLocation.compare(L"") == 0) {
+ if (EnumerateAndChooseVSInstance() &&
+ chosenInstanceInfo.VSInstallLocation.compare(L"") != 0) {
+ isWin10SDKInstalled = chosenInstanceInfo.IsWin10SDKInstalled;
+ }
+ } else {
+ isWin10SDKInstalled = chosenInstanceInfo.IsWin10SDKInstalled;
+ }
+
+ return isWin10SDKInstalled;
+}
+
+bool cmVSSetupAPIHelper::IsWin81SDKInstalled()
+{
+ bool isWin81SDKInstalled = false;
+ if (chosenInstanceInfo.VSInstallLocation.compare(L"") == 0) {
+ if (EnumerateAndChooseVSInstance() &&
+ chosenInstanceInfo.VSInstallLocation.compare(L"") != 0) {
+ isWin81SDKInstalled = chosenInstanceInfo.IsWin81SDKInstalled;
+ }
+ } else {
+ isWin81SDKInstalled = chosenInstanceInfo.IsWin81SDKInstalled;
+ }
+
+ return isWin81SDKInstalled;
+}
+
+bool cmVSSetupAPIHelper::CheckInstalledComponent(
+ SmartCOMPtr<ISetupPackageReference> package, bool& bVCToolset,
+ bool& bWin10SDK, bool& bWin81SDK)
+{
+ bool ret = false;
+ bVCToolset = bWin10SDK = bWin81SDK = false;
+ SmartBSTR bstrId;
+ if (FAILED(package->GetId(&bstrId))) {
+ return ret;
+ }
+
+ SmartBSTR bstrType;
+ if (FAILED(package->GetType(&bstrType))) {
+ return ret;
+ }
+
+ std::wstring id = std::wstring(bstrId);
+ std::wstring type = std::wstring(bstrType);
+ if (id.compare(VCToolsetComponent) == 0 &&
+ type.compare(ComponentType) == 0) {
+ bVCToolset = true;
+ ret = true;
+ }
+
+ // Checks for any version of Win10 SDK. The version is appended at the end of
+ // the
+ // component name ex: Microsoft.VisualStudio.Component.Windows10SDK.10240
+ if (id.find(Win10SDKComponent) != std::wstring::npos &&
+ type.compare(ComponentType) == 0) {
+ bWin10SDK = true;
+ ret = true;
+ }
+
+ if (id.compare(Win81SDKComponent) == 0 && type.compare(ComponentType) == 0) {
+ bWin81SDK = true;
+ ret = true;
+ }
+
+ return ret;
+}
+
+// Gather additional info such as if VCToolset, WinSDKs are installed, location
+// of VS and version information.
+bool cmVSSetupAPIHelper::GetVSInstanceInfo(
+ SmartCOMPtr<ISetupInstance2> pInstance, VSInstanceInfo& vsInstanceInfo)
+{
+ bool isVCToolSetInstalled = false;
+ if (pInstance == NULL)
+ return false;
+
+ SmartBSTR bstrId;
+ if (SUCCEEDED(pInstance->GetInstanceId(&bstrId))) {
+ vsInstanceInfo.InstanceId = std::wstring(bstrId);
+ } else {
+ return false;
+ }
+
+ InstanceState state;
+ if (FAILED(pInstance->GetState(&state))) {
+ return false;
+ }
+
+ ULONGLONG ullVersion = 0;
+ SmartBSTR bstrVersion;
+ if (FAILED(pInstance->GetInstallationVersion(&bstrVersion))) {
+ return false;
+ } else {
+ vsInstanceInfo.Version = std::wstring(bstrVersion);
+ if (FAILED(setupHelper->ParseVersion(bstrVersion, &ullVersion))) {
+ vsInstanceInfo.ullVersion = 0;
+ } else {
+ vsInstanceInfo.ullVersion = ullVersion;
+ }
+ }
+
+ // Reboot may have been required before the installation path was created.
+ SmartBSTR bstrInstallationPath;
+ if ((eLocal & state) == eLocal) {
+ if (FAILED(pInstance->GetInstallationPath(&bstrInstallationPath))) {
+ return false;
+ } else {
+ vsInstanceInfo.VSInstallLocation = std::wstring(bstrInstallationPath);
+ }
+ }
+
+ // Reboot may have been required before the product package was registered
+ // (last).
+ if ((eRegistered & state) == eRegistered) {
+ SmartCOMPtr<ISetupPackageReference> product;
+ if (FAILED(pInstance->GetProduct(&product)) || !product) {
+ return false;
+ }
+
+ LPSAFEARRAY lpsaPackages;
+ if (FAILED(pInstance->GetPackages(&lpsaPackages)) ||
+ lpsaPackages == NULL) {
+ return false;
+ }
+
+ int lower = lpsaPackages->rgsabound[0].lLbound;
+ int upper = lpsaPackages->rgsabound[0].cElements + lower;
+
+ IUnknown** ppData = (IUnknown**)lpsaPackages->pvData;
+ for (int i = lower; i < upper; i++) {
+ SmartCOMPtr<ISetupPackageReference> package = NULL;
+ if (FAILED(ppData[i]->QueryInterface(IID_ISetupPackageReference,
+ (void**)&package)) ||
+ package == NULL)
+ continue;
+
+ bool vcToolsetInstalled = false, win10SDKInstalled = false,
+ win81SDkInstalled = false;
+ bool ret = CheckInstalledComponent(package, vcToolsetInstalled,
+ win10SDKInstalled, win81SDkInstalled);
+ if (ret) {
+ isVCToolSetInstalled |= vcToolsetInstalled;
+ vsInstanceInfo.IsWin10SDKInstalled |= win10SDKInstalled;
+ vsInstanceInfo.IsWin81SDKInstalled |= win81SDkInstalled;
+ }
+ }
+
+ SafeArrayDestroy(lpsaPackages);
+ }
+
+ return isVCToolSetInstalled;
+}
+
+bool cmVSSetupAPIHelper::GetVSInstanceInfo(std::string& vsInstallLocation)
+{
+ vsInstallLocation = "";
+ bool isInstalled = false;
+
+ if (chosenInstanceInfo.VSInstallLocation.compare(L"") == 0) {
+ isInstalled = EnumerateAndChooseVSInstance();
+ }
+
+ // Enumerate and choose best VS instance
+ if (chosenInstanceInfo.VSInstallLocation.compare(L"") != 0) {
+ std::string str(chosenInstanceInfo.VSInstallLocation.begin(),
+ chosenInstanceInfo.VSInstallLocation.end());
+ vsInstallLocation = str;
+ isInstalled = true;
+ }
+
+ return isInstalled;
+}
+
+bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
+{
+ bool isVSInstanceExists = false;
+ if (chosenInstanceInfo.VSInstallLocation.compare(L"") != 0) {
+ return true;
+ }
+
+ if (initializationFailure || setupConfig == NULL || setupConfig2 == NULL ||
+ setupHelper == NULL)
+ return false;
+
+ std::vector<VSInstanceInfo> vecVSInstances;
+ SmartCOMPtr<IEnumSetupInstances> enumInstances = NULL;
+ if (FAILED(
+ setupConfig2->EnumInstances((IEnumSetupInstances**)&enumInstances)) ||
+ !enumInstances) {
+ return false;
+ }
+
+ SmartCOMPtr<ISetupInstance> instance;
+ while (SUCCEEDED(enumInstances->Next(1, &instance, NULL)) && instance) {
+ SmartCOMPtr<ISetupInstance2> instance2 = NULL;
+ if (FAILED(
+ instance->QueryInterface(IID_ISetupInstance2, (void**)&instance2)) ||
+ !instance2) {
+ instance = NULL;
+ continue;
+ }
+
+ VSInstanceInfo instanceInfo;
+ bool isInstalled = GetVSInstanceInfo(instance2, instanceInfo);
+ instance = instance2 = NULL;
+
+ if (isInstalled) {
+ vecVSInstances.push_back(instanceInfo);
+ }
+ }
+
+ if (vecVSInstances.size() > 0) {
+ isVSInstanceExists = true;
+ int index = ChooseVSInstance(vecVSInstances);
+ chosenInstanceInfo = vecVSInstances[index];
+ }
+
+ return isVSInstanceExists;
+}
+
+int cmVSSetupAPIHelper::ChooseVSInstance(
+ const std::vector<VSInstanceInfo>& vecVSInstances)
+{
+ if (vecVSInstances.size() == 0)
+ return -1;
+
+ if (vecVSInstances.size() == 1)
+ return 0;
+
+ unsigned int chosenIndex = 0;
+ for (unsigned int i = 1; i < vecVSInstances.size(); i++) {
+ // If the current has Win10 SDK but not the chosen one, then choose the
+ // current VS instance
+ if (!vecVSInstances[chosenIndex].IsWin10SDKInstalled &&
+ vecVSInstances[i].IsWin10SDKInstalled) {
+ chosenIndex = i;
+ continue;
+ }
+
+ // If the chosen one has Win10 SDK but the current one is not, then look at
+ // the next VS instance even the current
+ // instance version may be higher
+ if (vecVSInstances[chosenIndex].IsWin10SDKInstalled &&
+ !vecVSInstances[i].IsWin10SDKInstalled) {
+ continue;
+ }
+
+ // If both chosen one and current one doesn't have Win10 SDK but the
+ // current one has Win8.1 SDK installed,
+ // then choose the current one
+ if (!vecVSInstances[chosenIndex].IsWin10SDKInstalled &&
+ !vecVSInstances[i].IsWin10SDKInstalled &&
+ !vecVSInstances[chosenIndex].IsWin81SDKInstalled &&
+ vecVSInstances[i].IsWin81SDKInstalled) {
+ chosenIndex = i;
+ continue;
+ }
+
+ // If there is no difference in WinSDKs then look for the highest version
+ // of installed VS
+ if ((vecVSInstances[chosenIndex].IsWin10SDKInstalled ==
+ vecVSInstances[i].IsWin10SDKInstalled) &&
+ (vecVSInstances[chosenIndex].IsWin81SDKInstalled ==
+ vecVSInstances[i].IsWin81SDKInstalled) &&
+ vecVSInstances[chosenIndex].Version < vecVSInstances[i].Version) {
+ chosenIndex = i;
+ continue;
+ }
+ }
+
+ return chosenIndex;
+}
+
+bool cmVSSetupAPIHelper::Initialize()
+{
+ if (initializationFailure)
+ return false;
+
+ if (FAILED(comInitialized)) {
+ initializationFailure = true;
+ return false;
+ }
+
+ if (FAILED(setupConfig.CoCreateInstance(CLSID_SetupConfiguration, NULL,
+ IID_ISetupConfiguration,
+ CLSCTX_INPROC_SERVER)) ||
+ setupConfig == NULL) {
+ initializationFailure = true;
+ return false;
+ }
+
+ if (FAILED(setupConfig.QueryInterface(IID_ISetupConfiguration2,
+ (void**)&setupConfig2)) ||
+ setupConfig2 == NULL) {
+ initializationFailure = true;
+ return false;
+ }
+
+ if (FAILED(
+ setupConfig.QueryInterface(IID_ISetupHelper, (void**)&setupHelper)) ||
+ setupHelper == NULL) {
+ initializationFailure = true;
+ return false;
+ }
+
+ initializationFailure = false;
+ return true;
+}
diff --git a/Source/cmVSSetupHelper.h b/Source/cmVSSetupHelper.h
new file mode 100644
index 0000000..d2f514c
--- /dev/null
+++ b/Source/cmVSSetupHelper.h
@@ -0,0 +1,154 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmVSSetupHelper_h
+#define cmVSSetupHelper_h
+
+#ifndef NOMINMAX
+#define NOMINMAX // Undefine min and max defined by windows.h
+#endif
+
+// Published by Visual Studio Setup team
+#include "cmvssetup/Setup.Configuration.h"
+
+#include <string>
+#include <vector>
+
+#include <windows.h>
+
+template <class T>
+class SmartCOMPtr
+{
+public:
+ SmartCOMPtr() { ptr = NULL; }
+ SmartCOMPtr(T* p)
+ {
+ ptr = p;
+ if (ptr != NULL)
+ ptr->AddRef();
+ }
+ SmartCOMPtr(const SmartCOMPtr<T>& sptr)
+ {
+ ptr = sptr.ptr;
+ if (ptr != NULL)
+ ptr->AddRef();
+ }
+ T** operator&() { return &ptr; }
+ T* operator->() { return ptr; }
+ T* operator=(T* p)
+ {
+ if (*this != p) {
+ ptr = p;
+ if (ptr != NULL)
+ ptr->AddRef();
+ }
+ return *this;
+ }
+ operator T*() const { return ptr; }
+ template <class I>
+ HRESULT QueryInterface(REFCLSID rclsid, I** pp)
+ {
+ if (pp != NULL) {
+ return ptr->QueryInterface(rclsid, (void**)pp);
+ } else {
+ return E_FAIL;
+ }
+ }
+ HRESULT CoCreateInstance(REFCLSID clsid, IUnknown* pUnknown,
+ REFIID interfaceId, DWORD dwClsContext = CLSCTX_ALL)
+ {
+ HRESULT hr = ::CoCreateInstance(clsid, pUnknown, dwClsContext, interfaceId,
+ (void**)&ptr);
+ return hr;
+ }
+ ~SmartCOMPtr()
+ {
+ if (ptr != NULL)
+ ptr->Release();
+ }
+
+private:
+ T* ptr;
+};
+
+class SmartBSTR
+{
+public:
+ SmartBSTR() { str = NULL; }
+ SmartBSTR(const SmartBSTR& src)
+ {
+ if (src.str != NULL) {
+ str = ::SysAllocStringByteLen((char*)str, ::SysStringByteLen(str));
+ } else {
+ str = ::SysAllocStringByteLen(NULL, 0);
+ }
+ }
+ SmartBSTR& operator=(const SmartBSTR& src)
+ {
+ if (str != src.str) {
+ ::SysFreeString(str);
+ if (src.str != NULL) {
+ str = ::SysAllocStringByteLen((char*)str, ::SysStringByteLen(str));
+ } else {
+ str = ::SysAllocStringByteLen(NULL, 0);
+ }
+ }
+ return *this;
+ }
+ operator BSTR() const { return str; }
+ BSTR* operator&() throw() { return &str; }
+ ~SmartBSTR() throw() { ::SysFreeString(str); }
+private:
+ BSTR str;
+};
+
+struct VSInstanceInfo
+{
+ std::wstring InstanceId;
+ std::wstring VSInstallLocation;
+ std::wstring Version;
+ ULONGLONG ullVersion;
+ bool IsWin10SDKInstalled;
+ bool IsWin81SDKInstalled;
+
+ VSInstanceInfo()
+ {
+ InstanceId = VSInstallLocation = Version = L"";
+ ullVersion = 0;
+ IsWin10SDKInstalled = IsWin81SDKInstalled = false;
+ }
+};
+
+class cmVSSetupAPIHelper
+{
+public:
+ cmVSSetupAPIHelper();
+ ~cmVSSetupAPIHelper();
+
+ bool IsVS2017Installed();
+ bool GetVSInstanceInfo(std::string& vsInstallLocation);
+ bool IsWin10SDKInstalled();
+ bool IsWin81SDKInstalled();
+
+private:
+ bool Initialize();
+ bool GetVSInstanceInfo(SmartCOMPtr<ISetupInstance2> instance2,
+ VSInstanceInfo& vsInstanceInfo);
+ bool CheckInstalledComponent(SmartCOMPtr<ISetupPackageReference> package,
+ bool& bVCToolset, bool& bWin10SDK,
+ bool& bWin81SDK);
+ int ChooseVSInstance(const std::vector<VSInstanceInfo>& vecVSInstances);
+ bool EnumerateAndChooseVSInstance();
+
+ // COM ptrs to query about VS instances
+ SmartCOMPtr<ISetupConfiguration> setupConfig;
+ SmartCOMPtr<ISetupConfiguration2> setupConfig2;
+ SmartCOMPtr<ISetupHelper> setupHelper;
+ // used to indicate failure in Initialize(), so we don't have to call again
+ bool initializationFailure;
+ // indicated if COM initialization is successful
+ HRESULT comInitialized;
+ // current best instance of VS selected
+ VSInstanceInfo chosenInstanceInfo;
+};
+
+#endif