diff options
author | Iyyappa Murugandi <iyyappam@microsoft.com> | 2016-12-15 02:53:30 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2016-12-16 14:58:43 (GMT) |
commit | 18c8278b622e4cb9b155c5dc4ceac93322bed85f (patch) | |
tree | 796d86bf7385c3b4f2ea71d090c67d9540c59e15 /Source/cmVSSetupHelper.cxx | |
parent | d47bda00b2e46b3fd6f9e050f3f21b5337ac244b (diff) | |
download | CMake-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.
Diffstat (limited to 'Source/cmVSSetupHelper.cxx')
-rw-r--r-- | Source/cmVSSetupHelper.cxx | 395 |
1 files changed, 395 insertions, 0 deletions
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; +} |