/*++ Copyright (c) Microsoft Corporation Licensed under the MIT license. Module Name: - VsSetupConfiguration Abstract: - Encapsulates the Visual Studio Setup Configuration COM APIs Author(s): - Charles Willis - October 2020 --*/ #pragma once #include "Setup.Configuration.h" namespace winrt::Microsoft::Terminal::Settings::Model { /// /// See https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration?view=visualstudiosdk-2019 /// class VsSetupConfiguration { typedef wil::com_ptr ComPtrSetupQuery; typedef wil::com_ptr ComPtrSetupHelper; typedef wil::com_ptr ComPtrSetupInstance; typedef wil::com_ptr ComPtrSetupInstance2; typedef wil::com_ptr ComPtrPropertyStore; typedef wil::com_ptr ComPtrPackageReference; typedef wil::com_ptr ComPtrInstanceCatalog; typedef ComPtrPropertyStore ComPtrCustomPropertyStore; typedef ComPtrPropertyStore ComPtrCatalogPropertyStore; public: struct VsSetupInstance { std::wstring ResolvePath(std::wstring_view relativePath) const { return VsSetupConfiguration::ResolvePath(inst.get(), relativePath); } std::wstring GetDevShellModulePath() const { // The path of Microsoft.VisualStudio.DevShell.dll changed in 16.3 if (VersionInRange(L"[16.3,")) { return ResolvePath(L"Common7\\Tools\\Microsoft.VisualStudio.DevShell.dll"); } return ResolvePath(L"Common7\\Tools\\vsdevshell\\Microsoft.VisualStudio.DevShell.dll"); } std::wstring GetDevCmdScriptPath() const { return ResolvePath(L"Common7\\Tools\\VsDevCmd.bat"); } bool VersionInRange(std::wstring_view range) const { return InstallationVersionInRange(query.get(), inst.get(), range); } std::wstring GetVersion() const { return GetInstallationVersion(inst.get()); } std::wstring GetInstallationPath() const { return VsSetupConfiguration::GetInstallationPath(inst.get()); } std::wstring GetInstanceId() const { return VsSetupConfiguration::GetInstanceId(inst.get()); } ComPtrPropertyStore GetInstancePropertyStore() const { ComPtrPropertyStore properties; inst.query_to(&properties); return properties; } ComPtrCustomPropertyStore GetCustomPropertyStore() const { ComPtrSetupInstance2 instance2; inst.query_to(&instance2); ComPtrCustomPropertyStore properties; if (FAILED(instance2->GetProperties(&properties))) { return nullptr; } return properties; } ComPtrCatalogPropertyStore GetCatalogPropertyStore() const { ComPtrInstanceCatalog instanceCatalog; inst.query_to(&instanceCatalog); ComPtrCatalogPropertyStore properties; if (FAILED(instanceCatalog->GetCatalogInfo(&properties))) { return nullptr; } return properties; } std::wstring GetProfileNameSuffix() const { return profileNameSuffix; } private: friend class VsSetupConfiguration; VsSetupInstance(ComPtrSetupQuery pQuery, ComPtrSetupInstance pInstance) : query(std::move(pQuery)), inst(std::move(pInstance)), profileNameSuffix(BuildProfileNameSuffix()) { } ComPtrSetupQuery query; ComPtrSetupInstance inst; std::wstring profileNameSuffix; std::wstring BuildProfileNameSuffix() const { ComPtrCatalogPropertyStore catalogProperties = GetCatalogPropertyStore(); if (catalogProperties != nullptr) { std::wstring suffix; std::wstring productLine{ GetProductLineVersion(catalogProperties.get()) }; suffix.append(productLine); ComPtrCustomPropertyStore customProperties = GetCustomPropertyStore(); if (customProperties != nullptr) { std::wstring nickname{ GetNickname(customProperties.get()) }; if (!nickname.empty()) { suffix.append(L" (" + nickname + L")"); } else { ComPtrPropertyStore instanceProperties = GetInstancePropertyStore(); suffix.append(GetChannelNameSuffixTag(instanceProperties.get())); } } else { ComPtrPropertyStore instanceProperties = GetInstancePropertyStore(); suffix.append(GetChannelNameSuffixTag(instanceProperties.get())); } return suffix; } return GetVersion(); } static std::wstring GetChannelNameSuffixTag(ISetupPropertyStore* instanceProperties) { std::wstring tag; std::wstring channelName{ GetChannelName(instanceProperties) }; if (channelName.empty()) { return channelName; } if (channelName != L"Release") { tag.append(L" [" + channelName + L"]"); } return tag; } static std::wstring GetChannelId(ISetupPropertyStore* instanceProperties) { return GetStringProperty(instanceProperties, L"channelId"); } static std::wstring GetChannelName(ISetupPropertyStore* instanceProperties) { std::wstring channelId{ GetChannelId(instanceProperties) }; if (channelId.empty()) { return channelId; } std::wstring channelName; // channelId is in the format .. size_t pos = channelId.rfind(L"."); if (pos != std::wstring::npos) { channelName.append(channelId.substr(pos + 1)); } return channelName; } static std::wstring GetNickname(ISetupPropertyStore* customProperties) { return GetStringProperty(customProperties, L"nickname"); } static std::wstring GetProductLineVersion(ISetupPropertyStore* customProperties) { return GetStringProperty(customProperties, L"productLineVersion"); } }; static std::vector QueryInstances(); private: static bool InstallationVersionInRange(ISetupConfiguration2* pQuery, ISetupInstance* pInst, std::wstring_view range); static std::wstring ResolvePath(ISetupInstance* pInst, std::wstring_view relativePath); static std::wstring GetInstallationVersion(ISetupInstance* pInst); static std::wstring GetInstallationPath(ISetupInstance* pInst); static std::wstring GetInstanceId(ISetupInstance* pInst); static std::wstring GetStringProperty(ISetupPropertyStore* pProps, std::wstring_view name); }; };