├── .config └── tsaoptions.json ├── .gitattributes ├── .gitignore ├── .nuget └── packages.config ├── .vscode └── settings.json ├── .vsconfig ├── .vsts-ci.yml ├── .vsts-compliance.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── SECURITY.md ├── azure-pipelines.yml ├── docker ├── Debug.dockerfile ├── Dockerfile ├── Instances │ ├── 1 │ │ └── state.json │ ├── 2 │ │ └── state.json │ ├── 3 │ │ └── state.json │ ├── 4 │ │ └── state.json │ └── 5 │ │ └── state.json ├── Tests │ ├── .gitignore │ ├── find.tests.ps1 │ ├── legacy.tests.ps1 │ └── vswhere.tests.ps1 ├── docker-compose.debug.yml └── docker-compose.yml ├── inc ├── Common.Debug.props ├── Common.props ├── References.props └── VersionInfo.props ├── pipelines └── templates │ ├── 1es-publish-task.yml │ ├── ado-publish-task.yml │ └── build.yml ├── pkg └── vswhere │ ├── .gitignore │ ├── build │ └── vswhere.props │ ├── tools │ └── VERIFICATION.txt │ ├── vswhere.nuspec │ └── vswhere.signproj ├── src ├── vswhere.lib │ ├── CoInitializer.h │ ├── CommandArgs.cpp │ ├── CommandArgs.h │ ├── CommandParser.cpp │ ├── CommandParser.h │ ├── Console.cpp │ ├── Console.h │ ├── Exceptions.cpp │ ├── Exceptions.h │ ├── Formatter.cpp │ ├── Formatter.h │ ├── Glob.cpp │ ├── Glob.h │ ├── ILegacyProvider.h │ ├── InstanceSelector.cpp │ ├── InstanceSelector.h │ ├── JsonFormatter.cpp │ ├── JsonFormatter.h │ ├── JsonScope.cpp │ ├── JsonScope.h │ ├── LegacyInstance.cpp │ ├── LegacyInstance.h │ ├── LegacyProvider.cpp │ ├── LegacyProvider.h │ ├── Module.cpp │ ├── Module.h │ ├── ResourceManager.cpp │ ├── ResourceManager.h │ ├── SafeArray.h │ ├── Scope.h │ ├── TextFormatter.cpp │ ├── TextFormatter.h │ ├── Utilities.cpp │ ├── Utilities.h │ ├── ValueFormatter.cpp │ ├── ValueFormatter.h │ ├── VersionRange.cpp │ ├── VersionRange.h │ ├── XmlFormatter.cpp │ ├── XmlFormatter.h │ ├── XmlScope.cpp │ ├── XmlScope.h │ ├── packages.config │ ├── resource.h │ ├── stdafx.cpp │ ├── stdafx.h │ ├── targetver.h │ ├── vswhere.lib.rc │ ├── vswhere.lib.vcxproj │ └── vswhere.lib.vcxproj.filters └── vswhere │ ├── Program.cpp │ ├── packages.config │ ├── resource.h │ ├── stdafx.cpp │ ├── stdafx.h │ ├── targetver.h │ ├── vswhere.exe.manifest │ ├── vswhere.vcxproj │ └── vswhere.vcxproj.filters ├── test └── vswhere.test │ ├── CommandArgsTests.cpp │ ├── ExceptionsTests.cpp │ ├── GlobTests.cpp │ ├── InstanceSelectorTests.cpp │ ├── JsonFormatterTests.cpp │ ├── ModuleTests.cpp │ ├── TestConsole.cpp │ ├── TestConsole.h │ ├── TestEnumInstances.h │ ├── TestHelper.cpp │ ├── TestHelper.h │ ├── TestInstance.h │ ├── TestLegacyProvider.cpp │ ├── TestLegacyProvider.h │ ├── TestModule.cpp │ ├── TestModule.h │ ├── TestPackageReference.h │ ├── TestPropertyStore.h │ ├── TextFormatterTests.cpp │ ├── UtilitiesTests.cpp │ ├── ValueFormatterTests.cpp │ ├── VersionRangeTests.cpp │ ├── XmlFormatterTests.cpp │ ├── packages.config │ ├── resource.h │ ├── stdafx.cpp │ ├── stdafx.h │ ├── targetver.h │ ├── vswhere.test.vcxproj │ └── vswhere.test.vcxproj.filters ├── tools ├── test.cmd └── test.ps1 ├── version.json └── vswhere.sln /.config/tsaoptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "codebaseName": "VSWhere", 3 | "instanceUrl": "https://devdiv.visualstudio.com/defaultcollection", 4 | "projectName": "DevDiv", 5 | "areaPath": "DevDiv\\VS Setup", 6 | "iterationPath": "DevDiv", 7 | "allTools": true 8 | } 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /.nuget/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.insertSpaces": true, 3 | "editor.tabSize": 4, 4 | "files.associations": { 5 | "functional": "cpp", 6 | "string": "cpp", 7 | "unordered_map": "cpp" 8 | } 9 | } -------------------------------------------------------------------------------- /.vsconfig: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "components": [ 4 | "Microsoft.VisualStudio.Component.CoreEditor", 5 | "Microsoft.VisualStudio.Component.NuGet", 6 | "Microsoft.Component.MSBuild", 7 | "Microsoft.VisualStudio.Component.TextTemplating", 8 | "Microsoft.VisualStudio.Component.VC.CoreIde", 9 | "Microsoft.Component.VC.Runtime.UCRTSDK", 10 | "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", 11 | "Microsoft.VisualStudio.Component.Graphics.Tools", 12 | "Microsoft.VisualStudio.Component.VC.DiagnosticTools", 13 | "Microsoft.VisualStudio.Component.VC.Redist.14.Latest", 14 | "Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core", 15 | "Microsoft.VisualStudio.Component.Windows10SDK.19041", 16 | "Microsoft.VisualStudio.Component.VC.v141.x86.x64", 17 | "Microsoft.VisualStudio.Workload.NativeDesktop", 18 | "Microsoft.VisualStudio.Component.Git", 19 | "Microsoft.VisualStudio.Component.WinXP" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.vsts-ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 3 | 4 | trigger: 5 | batch: true 6 | branches: 7 | include: 8 | - main 9 | paths: 10 | exclude: 11 | - README.md 12 | tags: 13 | include: 14 | - "*" 15 | 16 | pr: none 17 | 18 | resources: 19 | repositories: 20 | - repository: MicroBuildTemplate 21 | type: git 22 | name: 1ESPipelineTemplates/MicroBuildTemplate 23 | ref: refs/tags/release 24 | 25 | extends: 26 | template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate 27 | parameters: 28 | pool: 29 | name: VSEngSS-MicroBuild2022-1ES 30 | sdl: 31 | sourceAnalysisPool: 32 | name: AzurePipelines-EO 33 | image: 1ESPT-Windows2022 34 | policheck: 35 | enabled: true 36 | binskim: 37 | enabled: true 38 | scanOutputDirectoryOnly: true # BinSkim scans whole source tree but we only need to scan the output dir. 39 | analyzeTargetGlob: +:f|$(Build.ArtifactStagingDirectory)\out\**;-:f|$(Build.ArtifactStagingDirectory)\out\**\*.test.* 40 | tsa: 41 | enabled: true 42 | configFile: $(Build.SourcesDirectory)\.config\tsaoptions.json 43 | onboard: false 44 | 45 | stages: 46 | - stage: Build 47 | jobs: 48 | - job: Build 49 | templateContext: 50 | mb: 51 | signing: 52 | enabled: true 53 | signType: $(SignType) 54 | 55 | steps: 56 | - template: /pipelines/templates/build.yml@self 57 | parameters: 58 | BuildConfiguration: $(BuildConfiguration) 59 | BuildPlatform: $(BuildPlatform) 60 | Sign: true 61 | PublishArtifactTemplate: /pipelines/templates/1es-publish-task.yml@self -------------------------------------------------------------------------------- /.vsts-compliance.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 3 | 4 | trigger: 5 | batch: true 6 | branches: 7 | include: 8 | - main 9 | paths: 10 | exclude: 11 | - README.md 12 | 13 | schedules: 14 | - cron: "0 18 * * Mon" 15 | displayName: Monday 11am PDT (6pm UTC) build 16 | branches: 17 | include: 18 | - main 19 | always: true 20 | 21 | pr: none 22 | 23 | variables: 24 | - group: vssetup-apiscan 25 | 26 | resources: 27 | repositories: 28 | - repository: MicroBuildTemplate 29 | type: git 30 | name: 1ESPipelineTemplates/MicroBuildTemplate 31 | ref: refs/tags/release 32 | 33 | extends: 34 | template: azure-pipelines/MicroBuild.1ES.Unofficial.yml@MicroBuildTemplate 35 | parameters: 36 | pool: 37 | name: VSEngSS-MicroBuild2022-1ES 38 | sdl: 39 | sourceAnalysisPool: 40 | name: AzurePipelines-EO 41 | image: 1ESPT-Windows2022 42 | antimalwareScan: 43 | enabled: true 44 | armory: 45 | enabled: true 46 | binskim: 47 | enabled: true 48 | scanOutputDirectoryOnly: true 49 | analyzeTargetGlob: +:f|$(Build.ArtifactStagingDirectory)\out\**;-:f|$(Build.ArtifactStagingDirectory)\out\**\*.test.* 50 | codeql: 51 | compiled: 52 | enabled: true 53 | credscan: 54 | enabled: true 55 | policheck: 56 | enabled: true 57 | psscriptanalyzer: 58 | enabled: true 59 | prefast: 60 | enabled: true 61 | tsa: 62 | enabled: true 63 | configFile: $(Build.SourcesDirectory)\.config\tsaoptions.json 64 | onboard: false 65 | 66 | stages: 67 | - stage: Compliance 68 | jobs: 69 | - job: Compliance 70 | steps: 71 | - template: /pipelines/templates/build.yml@self 72 | parameters: 73 | BuildConfiguration: $(BuildConfiguration) 74 | BuildPlatform: $(BuildPlatform) 75 | Sign: false 76 | PublishArtifactTemplate: /pipelines/templates/1es-publish-task.yml@self 77 | 78 | - task: CopyFiles@2 79 | displayName: Copy files for API scan 80 | inputs: 81 | SourceFolder: $(Build.SourcesDirectory)\bin\$(BuildConfiguration) 82 | Contents: | 83 | **\*.?(exe|dll|pdb|xml) 84 | !**\*.test.?(exe|dll|pdb|xml) 85 | TargetFolder: $(Build.StagingDirectory)\apiscan-inputs 86 | 87 | - task: APIScan@2 88 | displayName: Run APIScan 89 | inputs: 90 | softwareFolder: $(Build.StagingDirectory)\apiscan-inputs 91 | softwareName: 'Microsoft.VSWhere' 92 | softwareVersionNum: '3' 93 | toolVersion: Latest 94 | env: 95 | AzureServicesAuthConnectionString: runAs=App;AppId=$(ApiScanClientId) 96 | 97 | - task: PublishSecurityAnalysisLogs@3 98 | displayName: Publish 'SDLAnalysis-APIScan' artifact 99 | condition: succeededOrFailed() 100 | inputs: 101 | ArtifactName: SDLAnalysis-APIScan 102 | AllTools: false 103 | APIScan: true 104 | 105 | - task: PostAnalysis@2 106 | displayName: Post Analysis 107 | inputs: 108 | GdnBreakAllTools: false 109 | GdnBreakGdnToolApiScan: true 110 | 111 | - task: TSAUpload@2 112 | displayName: Upload APIScan results to TSA 113 | inputs: 114 | GdnPublishTsaOnboard: false 115 | GdnPublishTsaConfigFile: '$(Build.SourcesDirectory)\.config\tsaoptions.json' 116 | GdnPublishTsaExportedResultsPublishable: true 117 | continueOnError: true 118 | condition: succeededOrFailed() 119 | enabled: true 120 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Code of Conduct 2 | =============== 3 | 4 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | ## Prerequisites 5 | 6 | This project uses the following software. Newer versions may work but backward compatibility must be maintained. 7 | 8 | * [Visual Studio 2017](https://www.visualstudio.com/downloads/) or newer 9 | 10 | ## Coding 11 | 12 | This project uses a GitHub flow model with development and releases based on the `main` branch. You can view current build status in the [README](README.md) document. 13 | 14 | Please follow project code styles including, 15 | 16 | * All requires headers are in the precompiled headers, _stdafx.h_. 17 | * Copyright header including proper file name at top of all non-generated source. 18 | * Tabs are 4 spaces. 19 | 20 | In general, any new code should be stylistically indistinguishable from existing code. 21 | 22 | ## Building 23 | 24 | Before you can build this project from the command line with MSBuild or within Visual Studio, you must restore packages. 25 | 26 | * In Visual Studio, make sure Package Restore is enabled. 27 | * On the command line and assuming _nuget.exe_ is in your `PATH`, in the solution directory run: `nuget restore` 28 | 29 | Note again that to build the full solution in Visual Studio some optional software may be required. 30 | 31 | ## Testing 32 | 33 | All available tests are discovered after a complete build in Test Explorer within Visual Studio. 34 | 35 | On the command line, you can run the following commands from the solution directory. Replace `` with whatever version was downloaded. 36 | 37 | ```batch 38 | nuget install xunit.runner.console -outputdirectory packages 39 | packages\xunit.runner.console.\tools\xunit.runner.console test\VSSetup.PowerShell.Test\bin\Debug\Microsoft.VisualStudio.Setup.PowerShell.Test.dll 40 | ``` 41 | 42 | If your machine supports it, you can install [Docker for Windows][docker], switch to Windows containers, and test in isolated containers for runtime behavior. You can run functional tests and runtime tests together. 43 | 44 | ```batch 45 | tools\test.cmd 46 | ``` 47 | 48 | You can also run tests directly with `docker-compose`: 49 | 50 | ```batch 51 | docker-compose -f docker\docker-compose.yml run test 52 | ``` 53 | 54 | ### Debugging 55 | 56 | You can use the following steps to start an environment for exploratory testing or to run and debug tests. The Visual Studio Remote Debugger will launch by default and should be discoverable on your private network. 57 | 58 | 1. Run: 59 | ```batch 60 | docker-compose -f docker\docker-compose.yml -f docker\docker-compose.debug.yml up -d 61 | 62 | REM Start an interactive shell 63 | docker-compose -f docker\docker-compose.yml -f docker\docker-compose.debug.yml exec test powershell.exe 64 | ``` 65 | 2. Click *Debug -> Attach to Process* 66 | 3. Change *Transport* to "Remote (no authentication)" 67 | 4. Click *Find* 68 | 5. Click *Select* on the container (host name will be the container name) 69 | 6. Select "powershell" under *Available Processes* 70 | 7. Click *Attach* 71 | 8. Run any commands you like in the interactive shell, or run all tests: 72 | ```powershell 73 | Invoke-Pester C:\Tests -EnableExit 74 | ``` 75 | 9. When finished, run: 76 | ```batch 77 | docker-compose -f docker\docker-compose.yml -f docker\docker-compose.debug.yml down 78 | ``` 79 | 80 | If you know the host name (by default, the container name) or IP address (depending on your network configuration for the container), you can type it into the *Qualifier* directory along with port 4022, e.g. "172.22.0.1:4022". 81 | 82 | ## Pull Requests 83 | 84 | We welcome pull requests for both bug fixes and new features that solve a common enough problem to benefit the community. Please note the following requirements. 85 | 86 | 1. Code changes for bug fixes and new features are accompanied by new tests or, only if required, modifications to existing tests. Modifying existing tests when not required may introduce regressions. 87 | 2. All tests must pass. We have automated PR builds that will verify any PRs before they can be merged, but you are encouraged to run all tests in your development environment prior to pushing to your remote. 88 | 89 | Thank you for your contributions! 90 | 91 | [docker]: https://www.docker.com/products/overview 92 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (C) Microsoft Corporation. All rights reserved. 3 | 4 | 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: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | 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. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Visual Studio Locator 2 | ===================== 3 | 4 | ![build status: main](https://devdiv.visualstudio.com/DevDiv/_apis/build/status/Setup/Setup-vswhere-CI?branchName=main&label=main) 5 | [![github release](https://img.shields.io/github/release/Microsoft/vswhere.svg?logo=github&logoColor=white)](https://github.com/Microsoft/vswhere/releases/latest) 6 | [![github releases: all](https://img.shields.io/github/downloads/Microsoft/vswhere/total.svg?logo=github&logoColor=white&label=github)](https://github.com/Microsoft/vswhere/releases) 7 | [![nuget: all](https://img.shields.io/nuget/dt/vswhere.svg?logo=nuget&logoColor=white&label=nuget)](https://nuget.org/packages/vswhere) 8 | [![chocolatey: all](https://img.shields.io/chocolatey/dt/vswhere.svg?label=chocolatey)](https://chocolatey.org/packages/vswhere) 9 | 10 | 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 one installed instance. These changes facilitate a smaller, faster default install complimented by on-demand install of other workloads and components. 11 | 12 | _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. 13 | 14 | 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. 15 | 16 | _vswhere_ is included with the installer as of Visual Studio 2017 version 15.2 and later, and can be found at the following location: `%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe`. The binary may be executed from that location as needed, [installed using popular package managers including WinGet](https://github.com/Microsoft/vswhere/wiki/Installing), or the latest version may be [downloaded from the releases page](https://github.com/Microsoft/vswhere/releases). 17 | 18 | ## Example 19 | 20 | If you wanted to find MSBuild - now installed under the Visual Studio 2017 and newer installation root - you could script a command like the following to run the latest version of MSBuild installed. This example uses the new `-find` parameter in our [latest release](https://github.com/Microsoft/vswhere/releases/latest) that searches selected instances for matching file name patterns. You can tailor what instances you select with parameters like `-version` or `-prerelease` to find specific versions you support, optionally including prereleases. 21 | 22 | ```batch 23 | @echo off 24 | setlocal enabledelayedexpansion 25 | 26 | for /f "usebackq tokens=*" %%i in (`vswhere -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe`) do ( 27 | "%%i" %* 28 | exit /b !errorlevel! 29 | ) 30 | ``` 31 | 32 | You can find more [examples](https://github.com/Microsoft/vswhere/wiki/Examples) in our wiki. 33 | 34 | ## Feedback 35 | 36 | To file issues or suggestions, please use the [Issues](https://github.com/Microsoft/vswhere/issues) page for this project on GitHub. 37 | 38 | ## License 39 | 40 | This project is licensed under the [MIT license](LICENSE.txt). 41 | 42 | ## Code of Conduct 43 | 44 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 45 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 3 | 4 | trigger: none 5 | 6 | pr: 7 | branches: 8 | include: 9 | - main 10 | paths: 11 | exclude: 12 | - README.md 13 | 14 | pool: 15 | vmImage: windows-2022 16 | 17 | variables: 18 | BuildConfiguration: Release 19 | BuildPlatform: x86 20 | 21 | steps: 22 | - template: /pipelines/templates/build.yml@self 23 | parameters: 24 | BuildConfiguration: $(BuildConfiguration) 25 | BuildPlatform: $(BuildPlatform) 26 | Docker: true 27 | PublishArtifactTemplate: /pipelines/templates/ado-publish-task.yml@self -------------------------------------------------------------------------------- /docker/Debug.dockerfile: -------------------------------------------------------------------------------- 1 | #escape=` 2 | 3 | # Copyright (C) Microsoft Corporation. All rights reserved. 4 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 5 | 6 | # Based on latest image cached by Azure Pipelines: https://docs.microsoft.com/azure/devops/pipelines/agents/hosted#software 7 | FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022 8 | SHELL ["powershell.exe", "-ExecutionPolicy", "Bypass", "-Command"] 9 | 10 | ENV INSTALLER_VERSION=1.14.190.31519 ` 11 | INSTALLER_URI=https://download.visualstudio.microsoft.com/download/pr/100516681/d68d54e233c956ff79799fdf63753c54/Microsoft.VisualStudio.Setup.Configuration.msi ` 12 | INSTALLER_HASH=8917aa7b4116e574856d43e8e62862c1d6f25512be54917f2ef95f9cac103810 13 | 14 | # Download and register the query API 15 | RUN $ErrorActionPreference = 'Stop' ; ` 16 | $VerbosePreference = 'Continue' ; ` 17 | $null = New-Item C:\TEMP -ItemType Directory -ea SilentlyContinue; ` 18 | Invoke-WebRequest -Uri $env:INSTALLER_URI -OutFile C:\TEMP\Microsoft.VisualStudio.Setup.Configuration.msi; ` 19 | if ((Get-FileHash -Path C:\TEMP\Microsoft.VisualStudio.Setup.Configuration.msi -Algorithm SHA256).Hash -ne $env:INSTALLER_HASH) { throw 'Download hash does not match' }; ` 20 | Start-Process -Wait -FilePath C:\Windows\System32\msiexec.exe -ArgumentList '/i C:\TEMP\Microsoft.VisualStudio.Setup.Configuration.msi /qn /l*vx C:\TEMP\Microsoft.VisualStudio.Setup.Configuration.log' 21 | 22 | ENTRYPOINT ["powershell.exe", "-ExecutionPolicy", "Bypass"] 23 | CMD ["-NoExit"] 24 | 25 | # Download and install Remote Debugger 26 | RUN $ErrorActionPreference = 'Stop' ; ` 27 | $ProgressPreference = 'SilentlyContinue' ; ` 28 | $VerbosePreference = 'Continue' ; ` 29 | New-Item -Path C:\Downloads -Type Directory | Out-Null ; ` 30 | Invoke-WebRequest -Uri 'https://go.microsoft.com/fwlink/?LinkId=746570&clcid=0x409' -OutFile C:\Downloads\vs_remotetools.exe ; ` 31 | Start-Process -Wait -FilePath C:\Downloads\vs_remotetools.exe -ArgumentList '-q' ; ` 32 | Remove-Item -Path C:\Downloads\vs_remotetools.exe 33 | 34 | # Configure Remote Debugger 35 | EXPOSE 3702 4022 4023 36 | RUN $ErrorActionPreference = 'Stop' ; ` 37 | $VerbosePreference = 'Continue' ; ` 38 | Start-Process -Wait -FilePath 'C:\Program Files\Microsoft Visual Studio 15.0\Common7\IDE\Remote Debugger\x64\msvsmon.exe' -ArgumentList '/prepcomputer', '/private', '/quiet' 39 | 40 | HEALTHCHECK --interval=10s CMD ["powershell.exe", "-ExecutionPolicy", "Bypass", "-Command", "&{ if (Get-Process msvsmon) { exit 0 } else { exit 1 } }"] 41 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | #escape=` 2 | 3 | # Copyright (C) Microsoft Corporation. All rights reserved. 4 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 5 | 6 | # Based on latest image cached by Azure Pipelines: https://docs.microsoft.com/azure/devops/pipelines/agents/hosted#software 7 | FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022 8 | SHELL ["powershell.exe", "-ExecutionPolicy", "Bypass", "-Command"] 9 | 10 | ENV INSTALLER_VERSION=1.14.190.31519 ` 11 | INSTALLER_URI=https://download.visualstudio.microsoft.com/download/pr/100516681/d68d54e233c956ff79799fdf63753c54/Microsoft.VisualStudio.Setup.Configuration.msi ` 12 | INSTALLER_HASH=8917aa7b4116e574856d43e8e62862c1d6f25512be54917f2ef95f9cac103810 13 | 14 | # Download and register the query API 15 | RUN $ErrorActionPreference = 'Stop' ; ` 16 | $VerbosePreference = 'Continue' ; ` 17 | $null = New-Item C:\TEMP -ItemType Directory -ea SilentlyContinue; ` 18 | Invoke-WebRequest -Uri $env:INSTALLER_URI -OutFile C:\TEMP\Microsoft.VisualStudio.Setup.Configuration.msi; ` 19 | if ((Get-FileHash -Path C:\TEMP\Microsoft.VisualStudio.Setup.Configuration.msi -Algorithm SHA256).Hash -ne $env:INSTALLER_HASH) { throw 'Download hash does not match' }; ` 20 | Start-Process -Wait -FilePath C:\Windows\System32\msiexec.exe -ArgumentList '/i C:\TEMP\Microsoft.VisualStudio.Setup.Configuration.msi /qn /l*vx C:\TEMP\Microsoft.VisualStudio.Setup.Configuration.log' 21 | 22 | ENTRYPOINT ["powershell.exe", "-ExecutionPolicy", "Bypass"] 23 | CMD ["-NoExit"] 24 | -------------------------------------------------------------------------------- /docker/Instances/1/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "channelId": "VisualStudio.15.Release/public.d15rel/15.0.26116.0", 3 | "installationName": "VisualStudio/public.d15rel/15.0.26116.0", 4 | "installationVersion": "15.0.26116", 5 | "installationPath": "C:\\VS\\Community", 6 | "installDate": "2017-01-17T03:45:00Z", 7 | "product": { 8 | "id": "Microsoft.VisualStudio.Product.Community", 9 | "version": "15.0.26116.0", 10 | "type": "Product" 11 | }, 12 | "localizedResources": [ 13 | { 14 | "language": "en-us", 15 | "title": "Visual Studio Community 2017", 16 | "description": "Free, fully-featured IDE for students, open-source and individual developers" 17 | }, 18 | { 19 | "language": "de-de", 20 | "title": "Visual Studio Community 2017", 21 | "description": "Kostenlose, voll funktionsfähige IDE für Studenten, Open Source- und einzelne Entwickler." 22 | }, 23 | { 24 | "language": "ja-jp", 25 | "title": "Visual Studio Community 2017", 26 | "description": "学生、オープン ソース、および個々の開発者のための無料で完全な機能を備えた IDE" 27 | } 28 | ], 29 | "launchParams": { 30 | "fileName": "Common7\\IDE\\devenv.exe" 31 | }, 32 | "enginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service", 33 | "properties": { 34 | "nickname": "Community" 35 | }, 36 | "selectedPackages": [ 37 | { 38 | "id": "Microsoft.VisualStudio.Product.Community", 39 | "version": "15.0.26116.0", 40 | "type": "Product", 41 | "selectedState": "IndividuallySelected" 42 | } 43 | ], 44 | "packages": [ 45 | { 46 | "id": "Microsoft.VisualStudio.Workload.ManagedDesktop", 47 | "version": "15.0.26116.0", 48 | "type": "Workload" 49 | }, 50 | { 51 | "id": "Microsoft.VisualStudio.Workload.CoreEditor", 52 | "version": "15.0.26116.0", 53 | "type": "Workload" 54 | }, 55 | { 56 | "id": "Microsoft.VisualStudio.Branding.Community", 57 | "version": "15.0.26116.0", 58 | "type": "Vsix" 59 | }, 60 | { 61 | "id": "Microsoft.VisualStudio.Product.Community", 62 | "version": "15.0.26116.0", 63 | "type": "Product" 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /docker/Instances/2/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "channelId": "VisualStudio.15.Release/public.d15rel/15.0.26117.0", 3 | "installationName": "VisualStudio/public.d15rel/15.0.26117.0", 4 | "installationVersion": "15.0.26117", 5 | "installationPath": "C:\\VS\\Enterprise", 6 | "installDate": "2017-01-18T03:15:00Z", 7 | "product": { 8 | "id": "Microsoft.VisualStudio.Product.Enterprise", 9 | "version": "15.0.26117.0", 10 | "type": "Product" 11 | }, 12 | "localizedResources": [ 13 | { 14 | "language": "en-us", 15 | "title": "Visual Studio Enterprise 2017", 16 | "description": "Microsoft DevOps solution for productivity and coordination across teams of any size" 17 | }, 18 | { 19 | "language": "de-de", 20 | "title": "Visual Studio Enterprise 2017", 21 | "description": "Microsoft DevOps-Lösung für Produktivität und Koordination von Teams beliebiger Größe" 22 | }, 23 | { 24 | "language": "ja-jp", 25 | "title": "Visual Studio Enterprise 2017", 26 | "description": "生産性向上と、さまざまな規模のチーム間の調整のための Microsoft DevOps ソリューション" 27 | } 28 | ], 29 | "launchParams": { 30 | "fileName": "Common7\\IDE\\devenv.exe" 31 | }, 32 | "enginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service", 33 | "properties": { 34 | "nickname": "Enterprise" 35 | }, 36 | "selectedPackages": [ 37 | { 38 | "id": "Microsoft.VisualStudio.Product.Enterprise", 39 | "version": "15.0.26117.0", 40 | "type": "Product", 41 | "selectedState": "IndividuallySelected" 42 | } 43 | ], 44 | "packages": [ 45 | { 46 | "id": "Microsoft.VisualStudio.Workload.ManagedDesktop", 47 | "version": "15.0.26117.0", 48 | "type": "Workload" 49 | }, 50 | { 51 | "id": "Microsoft.VisualStudio.Workload.NativeDesktop", 52 | "version": "15.0.26117.0", 53 | "type": "Workload" 54 | }, 55 | { 56 | "id": "Microsoft.VisualStudio.Workload.CoreEditor", 57 | "version": "15.0.26117.0", 58 | "type": "Workload" 59 | }, 60 | { 61 | "id": "Microsoft.VisualStudio.Branding.Enterprise", 62 | "version": "15.0.26117.0", 63 | "type": "Vsix" 64 | }, 65 | { 66 | "id": "Microsoft.VisualStudio.Product.Enterprise", 67 | "version": "15.0.26117.0", 68 | "type": "Product" 69 | } 70 | ], 71 | "errors": { 72 | "errorLogFilePath": "C:\\TEMP\\dd_setup_201601180315_errors.log", 73 | "failedPackages": [ 74 | { 75 | "id": "Microsoft.VisualStudio.Workload.Office", 76 | "version": "15.0.26009.0", 77 | "type": "Workload", 78 | "logFilePath": "C:\\TEMP\\dd_setup_201601180315_003_Microsoft.VisualStudio.Workload.Office_errors.log", 79 | "description": "Failed to install Microsoft.VisualStudio.Workload.Office" 80 | } 81 | ], 82 | "skippedPackages": [ 83 | { 84 | "id": "Microsoft.VisualStudio.Component.Sharepoint.Tools", 85 | "version": "15.0.260009.0", 86 | "type": "Component" 87 | } 88 | ] 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /docker/Instances/3/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "channelId": "VisualStudio.15.Release/public.d15rel/15.0.26117.0", 3 | "installationName": "VisualStudio/public.d15rel/15.0.26117.0", 4 | "installationVersion": "15.0.26117", 5 | "installationPath": "C:\\VS\\Professional", 6 | "installDate": "2017-01-18T03:30:00Z", 7 | "product": { 8 | "id": "Microsoft.VisualStudio.Product.Professional", 9 | "version": "15.0.26117.0", 10 | "type": "Product" 11 | }, 12 | "localizedResources": [ 13 | { 14 | "language": "en-us", 15 | "title": "Visual Studio Professional 2017", 16 | "description": "Professional developer tools and services for small teams" 17 | }, 18 | { 19 | "language": "de-de", 20 | "title": "Visual Studio Professional 2017", 21 | "description": "Professionelle Entwicklertools und -dienste für kleine Teams" 22 | }, 23 | { 24 | "language": "ja-jp", 25 | "title": "Visual Studio Professional 2017", 26 | "description": "小規模なチーム向けのプロフェッショナルな開発者向けツールおよびサービス" 27 | } 28 | ], 29 | "launchParams": { 30 | "fileName": "Common7\\IDE\\devenv.exe" 31 | }, 32 | "enginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service", 33 | "properties": { 34 | "nickname": "Professional" 35 | }, 36 | "selectedPackages": [ 37 | { 38 | "id": "Microsoft.VisualStudio.Product.Professional", 39 | "version": "15.0.26117.0", 40 | "type": "Product", 41 | "selectedState": "IndividuallySelected" 42 | } 43 | ], 44 | "packages": [ 45 | { 46 | "id": "Microsoft.VisualStudio.Workload.ManagedDesktop", 47 | "version": "15.0.26117.0", 48 | "type": "Workload" 49 | }, 50 | { 51 | "id": "Microsoft.VisualStudio.Workload.CoreEditor", 52 | "version": "15.0.26117.0", 53 | "type": "Workload" 54 | }, 55 | { 56 | "id": "Microsoft.VisualStudio.Branding.Professional", 57 | "version": "15.0.26117.0", 58 | "type": "Vsix" 59 | }, 60 | { 61 | "id": "Microsoft.VisualStudio.Product.Professional", 62 | "version": "15.0.26117.0", 63 | "type": "Product" 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /docker/Instances/4/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "channelId": "VisualStudio.15.Release/public.d15rel/15.0.26117.0", 3 | "installationName": "VisualStudio/public.d15rel/15.0.26117.0", 4 | "installationVersion": "15.0.26117", 5 | "installationPath": "C:\\BuildTools", 6 | "installDate": "2017-01-18T03:45:00Z", 7 | "product": { 8 | "id": "Microsoft.VisualStudio.Product.BuildTools", 9 | "version": "15.0.26117.0", 10 | "type": "Product" 11 | }, 12 | "localizedResources": [ 13 | { 14 | "language": "en-us", 15 | "title": "Visual Studio Build Tools 2017", 16 | "description": "The Visual Studio Build Tools allows you to build native and managed MSBuild-based applications without requiring the Visual Studio IDE. There are options to install the Visual C++ compilers and libraries, MFC, ATL, and C++/CLI support." 17 | }, 18 | { 19 | "language": "de-de", 20 | "title": "Visual Studio-Buildtools 2017", 21 | "description": "Die Visual Studio-Buildtools ermöglichen Ihnen die Erstellung nativer und verwalteter MSBuild-basierter Anwendungen, ohne dass die Visual Studio-IDE erforderlich ist. Es stehen Optionen zur Installation von Visual C++-Compilern und -Bibliotheken, MFC, ATL sowie C++/CLI-Unterstützung zur Verfügung." 22 | }, 23 | { 24 | "language": "ja-jp", 25 | "title": "Visual Studio Build Tools 2017", 26 | "description": "Visual Studio Build Tools では、Visual Studio IDE を必要とせずに、MSBuild ベースのネイティブ マネージド アプリケーションをビルドできます。また、Visual C++ のコンパイラやライブラリ、MFC、ATL、および C++/CLI サポートをインストールするオプションも用意されています。" 27 | } 28 | ], 29 | "enginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service", 30 | "selectedPackages": [ 31 | { 32 | "id": "Microsoft.VisualStudio.Product.BuildTools", 33 | "version": "15.0.26117.0", 34 | "type": "Product", 35 | "selectedState": "IndividuallySelected" 36 | } 37 | ], 38 | "packages": [ 39 | { 40 | "id": "Microsoft.VisualStudio.Workload.MSBuildTools", 41 | "version": "15.0.26117.0", 42 | "type": "Workload" 43 | }, 44 | { 45 | "id": "Microsoft.VisualStudio.Product.BuildTools", 46 | "version": "15.0.26117.0", 47 | "type": "Product" 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /docker/Instances/5/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "channelId": "VisualStudio.15.Preview", 3 | "installationName": "VisualStudioPreview/15.3.0-pre.3.0+26612.0.d15rel", 4 | "installationVersion": "15.3.26612.0", 5 | "installationPath": "C:\\VS\\Preview", 6 | "installDate": "2017-04-17T10:00:00Z", 7 | "catalogInfo": { 8 | "id": "VisualStudioPreview/15.3.0-pre.3.0+26612.0.d15rel", 9 | "buildBranch": "d15rel", 10 | "buildVersion": "15.3.26612.0", 11 | "manifestName": "VisualStudioPreview", 12 | "manifestType": "installer", 13 | "productDisplayVersion": "15.3.0 Preview 3.0 [26612.0.d15rel]", 14 | "productLineVersion": "2017", 15 | "productMilestone": "Preview", 16 | "productMilestoneIsPreRelease": "True", 17 | "productName": "Visual Studio", 18 | "productPatchVersion": "0", 19 | "productPreReleaseMilestoneSuffix": "3.0", 20 | "productSemanticVersion": "15.3.0-pre.3.0+26612.0.d15rel" 21 | }, 22 | "product": { 23 | "id": "Microsoft.VisualStudio.Product.Enterprise", 24 | "version": "15.0.26612.0", 25 | "type": "Product" 26 | }, 27 | "localizedResources": [ 28 | { 29 | "language": "en-us", 30 | "title": "Visual Studio Enterprise 2017", 31 | "description": "Microsoft DevOps solution for productivity and coordination across teams of any size" 32 | }, 33 | { 34 | "language": "de-de", 35 | "title": "Visual Studio Enterprise 2017", 36 | "description": "Microsoft DevOps-Lösung für Produktivität und Koordination von Teams beliebiger Größe" 37 | }, 38 | { 39 | "language": "ja-jp", 40 | "title": "Visual Studio Enterprise 2017", 41 | "description": "生産性向上と、さまざまな規模のチーム間の調整のための Microsoft DevOps ソリューション" 42 | } 43 | ], 44 | "launchParams": { 45 | "fileName": "Common7\\IDE\\devenv.exe" 46 | }, 47 | "enginePath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\resources\\app\\ServiceHub\\Services\\Microsoft.VisualStudio.Setup.Service", 48 | "properties": { 49 | "nickname": "Preview" 50 | }, 51 | "selectedPackages": [ 52 | { 53 | "id": "Microsoft.VisualStudio.Product.Enterprise", 54 | "version": "15.0.26117.0", 55 | "type": "Product", 56 | "selectedState": "IndividuallySelected" 57 | } 58 | ], 59 | "packages": [ 60 | { 61 | "id": "Microsoft.VisualStudio.Workload.ManagedDesktop", 62 | "version": "15.0.26117.0", 63 | "type": "Workload" 64 | }, 65 | { 66 | "id": "Microsoft.VisualStudio.Workload.NativeDesktop", 67 | "version": "15.0.26117.0", 68 | "type": "Workload" 69 | }, 70 | { 71 | "id": "Microsoft.VisualStudio.Workload.CoreEditor", 72 | "version": "15.0.26117.0", 73 | "type": "Workload" 74 | }, 75 | { 76 | "id": "Microsoft.VisualStudio.Branding.Enterprise", 77 | "version": "15.0.26117.0", 78 | "type": "Vsix" 79 | }, 80 | { 81 | "id": "Microsoft.VisualStudio.Product.Enterprise", 82 | "version": "15.0.26117.0", 83 | "type": "Product" 84 | } 85 | ] 86 | } 87 | -------------------------------------------------------------------------------- /docker/Tests/.gitignore: -------------------------------------------------------------------------------- 1 | [Rr]esults.xml 2 | -------------------------------------------------------------------------------- /docker/Tests/find.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (C) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 3 | 4 | # Instances and results are sorted for consistency. 5 | Describe 'vswhere -sort -find' { 6 | BeforeAll { 7 | # Always write to 32-bit registry key. 8 | $key = New-Item -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\Setup\Reboot -Force 9 | $null = $key | New-ItemProperty -Name 3 -Value 1 -Force 10 | 11 | $files = @( 12 | 'MSBuild\15.0\Bin\MSBuild.exe' 13 | 'MSBuild\15.0\Bin\MSBuild.exe.config' 14 | 'MSBuild\15.0\Bin\amd64\MSBuild.exe' 15 | 'MSBuild\15.0\Bin\amd64\MSBuild.exe.config' 16 | ) 17 | # Populate each instance with files to find. 18 | $instances = C:\bin\vswhere.exe -all -prerelease -products * -format json | ConvertFrom-Json 19 | foreach ($file in $files) { 20 | $filePath = Join-Path -Path $instances.installationPath -ChildPath $file 21 | $null = New-Item -Path $filePath -ItemType 'File' -Value '1' -Force 22 | } 23 | } 24 | 25 | Context 'msbuild\15.0\bin\msbuild.exe' { 26 | It 'returns 2 matches' { 27 | $files = C:\bin\vswhere.exe -sort -find 'msbuild\15.0\bin\msbuild.exe' 28 | 29 | $files.Count | Should Be 2 30 | $files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe' 31 | $files[1] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe' 32 | } 33 | 34 | It '-format json returns 2 matches' { 35 | $files = C:\bin\vswhere.exe -sort -find 'msbuild\15.0\bin\msbuild.exe' -format json | ConvertFrom-Json 36 | 37 | $files.Count | Should Be 2 38 | $files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe' 39 | $files[1] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe' 40 | } 41 | 42 | It '-format xml returns 2 matches' { 43 | $doc = [xml](C:\bin\vswhere.exe -sort -find 'msbuild\15.0\bin\msbuild.exe' -format xml) 44 | 45 | $doc.files.file.Count | Should Be 2 46 | $doc.files.file[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe' 47 | $doc.files.file[1] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe' 48 | } 49 | } 50 | 51 | Context 'msbuild\**\msbuild.exe' { 52 | It 'returns 4 matches' { 53 | $files = C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.exe' 54 | 55 | $files.Count | Should Be 4 56 | $files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe' 57 | $files[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe' 58 | $files[2] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\amd64\MSBuild.exe' 59 | $files[3] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe' 60 | } 61 | 62 | It '-format json returns 4 matches' { 63 | $files = C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.exe' -format json | ConvertFrom-Json 64 | 65 | $files.Count | Should Be 4 66 | $files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe' 67 | $files[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe' 68 | $files[2] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\amd64\MSBuild.exe' 69 | $files[3] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe' 70 | } 71 | 72 | It '-format xml returns 4 matches' { 73 | $doc = [xml](C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.exe' -format xml) 74 | 75 | $doc.files.file.Count | Should Be 4 76 | $doc.files.file[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe' 77 | $doc.files.file[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe' 78 | $doc.files.file[2] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\amd64\MSBuild.exe' 79 | $doc.files.file[3] | Should Be 'C:\VS\Community\MSBuild\15.0\Bin\MSBuild.exe' 80 | } 81 | } 82 | 83 | Context 'msbuild\**\msbuild.* -latest' { 84 | It 'returns 4 matches' { 85 | $files = C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.*' -latest 86 | 87 | $files.Count | Should Be 4 88 | $files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe' 89 | $files[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe.config' 90 | $files[2] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe' 91 | $files[3] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe.config' 92 | } 93 | 94 | It '-format json returns 4 matches' { 95 | $files = C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.*' -latest -format json | ConvertFrom-Json 96 | 97 | $files.Count | Should Be 4 98 | $files[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe' 99 | $files[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe.config' 100 | $files[2] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe' 101 | $files[3] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe.config' 102 | } 103 | 104 | It '-format xml returns 4 matches' { 105 | $doc = [xml](C:\bin\vswhere.exe -sort -find 'msbuild\**\msbuild.*' -latest -format xml) 106 | 107 | $doc.files.file.Count | Should Be 4 108 | $doc.files.file[0] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe' 109 | $doc.files.file[1] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\amd64\MSBuild.exe.config' 110 | $doc.files.file[2] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe' 111 | $doc.files.file[3] | Should Be 'C:\VS\Enterprise\MSBuild\15.0\Bin\MSBuild.exe.config' 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /docker/Tests/legacy.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (C) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 3 | 4 | Describe 'vswhere -legacy' { 5 | BeforeAll { 6 | # Always write to 32-bit registry key. 7 | $key = New-Item -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\Setup\Reboot -Force 8 | $null = $key | New-ItemProperty -Name 3 -Value 1 -Force 9 | } 10 | 11 | BeforeEach { 12 | # Make sure localized values are returned consistently across machines. 13 | $enu = [System.Globalization.CultureInfo]::GetCultureInfo('en-US') 14 | 15 | [System.Globalization.CultureInfo]::CurrentCulture = $enu 16 | [System.Globalization.CultureInfo]::CurrentUICulture = $enu 17 | } 18 | 19 | AfterEach { 20 | # Make sure the registry is cleaned up. 21 | Remove-Item HKLM:\Software\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 -Force -ErrorAction 'SilentlyContinue' 22 | } 23 | 24 | Context 'no legacy' { 25 | It 'returns 2 instances' { 26 | $instances = C:\bin\vswhere.exe -legacy -format json | ConvertFrom-Json 27 | $instances.Count | Should Be 2 28 | } 29 | } 30 | 31 | Context 'has legacy' { 32 | BeforeEach { 33 | New-Item HKLM:\Software\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 -Force | ForEach-Object { 34 | foreach ($version in '10.0', '14.0') { 35 | $_ | New-ItemProperty -Name $version -Value "C:\VisualStudio\$version" -Force 36 | } 37 | } 38 | } 39 | 40 | It 'returns 4 instances' { 41 | $instances = C:\bin\vswhere.exe -legacy -format json | ConvertFrom-Json 42 | $instances.Count | Should Be 4 43 | } 44 | 45 | It '-version "10.0" returns 4 instances' { 46 | $instances = C:\bin\vswhere.exe -legacy -version '10.0' -format json | ConvertFrom-Json 47 | $instances.Count | Should Be 4 48 | } 49 | 50 | It '-version "14.0" returns 3 instances' { 51 | $instances = C:\bin\vswhere.exe -legacy -version '14.0' -format json | ConvertFrom-Json 52 | $instances.Count | Should Be 3 53 | } 54 | 55 | It '-version "[10.0,15.0)" returns 2 instances' { 56 | $instances = C:\bin\vswhere.exe -legacy -version '[10.0,15.0)' -format json | ConvertFrom-Json 57 | $instances.Count | Should Be 2 58 | } 59 | } 60 | 61 | Context 'no instances' { 62 | BeforeEach { 63 | New-Item HKLM:\Software\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 -Force | ForEach-Object { 64 | foreach ($version in '10.0', '14.0') { 65 | $_ | New-ItemProperty -Name $version -Value "C:\VisualStudio\$version" -Force 66 | } 67 | } 68 | 69 | Rename-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Classes\CLSID\{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}\InprocServer32' -Name '(Default)' -NewName '_Default' 70 | } 71 | 72 | AfterEach { 73 | Rename-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Classes\CLSID\{177F0C4A-1CD3-4DE7-A32C-71DBBB9FA36D}\InprocServer32' -Name '_Default' -NewName '(Default)' 74 | } 75 | 76 | It 'returns 2 instances' { 77 | $instances = C:\bin\vswhere.exe -legacy -format json | ConvertFrom-Json 78 | $instances.Count | Should Be 2 79 | } 80 | 81 | It '-latest returns latest instance' { 82 | $instances = C:\bin\vswhere.exe -legacy -latest -format json | ConvertFrom-Json 83 | $instances.Count | Should Be 1 84 | $instances[0].instanceId | Should Be 'VisualStudio.14.0' 85 | $instances[0].installationPath | Should Be 'C:\VisualStudio\14.0' 86 | } 87 | 88 | It '-version is supported' { 89 | $instances = C:\bin\vswhere.exe -legacy -latest -format json | ConvertFrom-Json 90 | $instances.Count | Should Be 1 91 | $instances[0].instanceId | Should Be 'VisualStudio.14.0' 92 | $instances[0].installationPath | Should Be 'C:\VisualStudio\14.0' 93 | } 94 | } 95 | 96 | Context '-legacy' { 97 | It '-products "any" is not supported' { 98 | C:\bin\vswhere.exe -legacy -products any 99 | $LASTEXITCODE | Should Be 87 100 | } 101 | 102 | It '-requires "any" is not supported' { 103 | C:\bin\vswhere.exe -legacy -requires any 104 | $LASTEXITCODE | Should Be 87 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /docker/docker-compose.debug.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 3 | 4 | version: "2.1" 5 | services: 6 | test: 7 | image: microsoft/vssetup:debug 8 | build: 9 | context: . 10 | dockerfile: Debug.dockerfile 11 | network_mode: nat 12 | expose: 13 | - "3702/udp" 14 | - "4022-4023" 15 | command: > 16 | -Command "&'C:\Program Files\Microsoft Visual Studio 15.0\Common7\IDE\Remote Debugger\x64\msvsmon.exe' /silent /noauth /anyuser" 17 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 3 | 4 | version: "2.1" 5 | services: 6 | test: 7 | image: microsoft/vssetup:test 8 | build: . 9 | volumes: 10 | - ../bin/${CONFIGURATION:-Debug}:C:/bin:ro 11 | - ./Instances:C:/ProgramData/Microsoft/VisualStudio/Packages/_Instances:ro 12 | - ./Tests:C:/Tests 13 | - C:/VS/Community 14 | - C:/VS/Professional 15 | - C:/VS/Enterprise 16 | - C:/BuildTools 17 | - C:/VS/Preview 18 | network_mode: none 19 | command: -Command Invoke-Pester C:\Tests -EnableExit 20 | -------------------------------------------------------------------------------- /inc/Common.Debug.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | $(SolutionDir)bin\$(Configuration)\ 7 | 8 | 9 | 10 | stdcpp14 11 | MultiThreadedDebug 12 | Guard 13 | ProgramDatabase 14 | /ZH:SHA_256 %(AdditionalOptions) 15 | 16 | 17 | shell32.lib 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /inc/Common.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | $(SolutionDir)bin\$(Configuration)\ 7 | 8 | 9 | 10 | stdcpp14 11 | MultiThreaded 12 | Guard 13 | ProgramDatabase 14 | /ZH:SHA_256 %(AdditionalOptions) 15 | 16 | 17 | shell32.lib 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /inc/References.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | $(SolutionDir)src\vswhere.lib;$(IncludePath) 7 | $(OutDir);$(LibraryPath) 8 | 9 | 10 | 11 | vswhere.lib;vswhere.lib.res;%(AdditionalDependencies) 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /inc/VersionInfo.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Microsoft Corporation 7 | Visual Studio Setup 8 | Copyright (C) Microsoft Corporation. All rights reserved. 9 | 10 | 11 | 12 | $(IntermediateOutputPath);%(AdditionalIncludeDirectories) 13 | 14 | 15 | $(IntermediateOutputPath);%(AdditionalIncludeDirectories) 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /pipelines/templates/1es-publish-task.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: path 3 | type: string 4 | 5 | - name: artifactName 6 | type: string 7 | 8 | - name: displayName 9 | type: string 10 | default: 'Publish artifact' 11 | 12 | - name: condition 13 | type: string 14 | default: succeeded() 15 | 16 | steps: 17 | - task: 1ES.PublishPipelineArtifact@1 18 | displayName: ${{ parameters.displayName }} 19 | condition: ${{ parameters.condition }} 20 | inputs: 21 | targetPath: ${{ parameters.path }} 22 | artifactName: ${{ parameters.artifactName }} -------------------------------------------------------------------------------- /pipelines/templates/ado-publish-task.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: path 3 | type: string 4 | 5 | - name: artifactName 6 | type: string 7 | 8 | - name: displayName 9 | type: string 10 | default: 'Publish artifact' 11 | 12 | - name: condition 13 | type: string 14 | default: succeeded() 15 | 16 | steps: 17 | - task: PublishBuildArtifacts@1 18 | displayName: ${{ parameters.displayName }} 19 | condition: ${{ parameters.condition }} 20 | inputs: 21 | PathtoPublish: ${{ parameters.path }} 22 | ArtifactName: ${{ parameters.artifactName }} -------------------------------------------------------------------------------- /pipelines/templates/build.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 3 | 4 | parameters: 5 | BuildConfiguration: Release 6 | BuildPlatform: x86 7 | Docker: false 8 | Sign: false 9 | PublishArtifactTemplate: /pipelines/template/1es-publish-task.yml@self 10 | 11 | steps: 12 | - powershell: | 13 | dotnet tool install --tool-path "${env:AGENT_TOOLSDIRECTORY}\nbgv" nbgv 14 | $version = & "${env:AGENT_TOOLSDIRECTORY}\nbgv\nbgv.exe" get-version --variable SemVer1 15 | & "${env:AGENT_TOOLSDIRECTORY}\nbgv\nbgv.exe" cloud --version $version 16 | displayName: Set cloud build version 17 | 18 | - task: NuGetToolInstaller@0 19 | displayName: Install nuget 20 | 21 | - task: NuGetCommand@2 22 | displayName: Restore packages 23 | 24 | - task: VSBuild@1 25 | displayName: Build 26 | inputs: 27 | configuration: ${{ parameters.BuildConfiguration }} 28 | platform: ${{ parameters.BuildPlatform }} 29 | maximumCpuCount: true 30 | env: 31 | TreatWarningsAsErrors: true 32 | 33 | - task: VSTest@2 34 | displayName: Functional tests 35 | inputs: 36 | configuration: ${{ parameters.BuildConfiguration }} 37 | platform: ${{ parameters.BuildPlatform }} 38 | testAssemblyVer2: | 39 | bin\${{ parameters.BuildConfiguration }}\*.test.dll 40 | runInParallel: true 41 | codeCoverageEnabled: true 42 | testRunTitle: Functional tests (${{ parameters.BuildConfiguration }}|${{ parameters.BuildPlatform }}) 43 | 44 | - ${{ if eq(parameters.Docker, 'true') }}: 45 | # Make sure service images are rebuilt if Dockerfiles changed. 46 | - task: DockerCompose@1 47 | displayName: Build images 48 | inputs: 49 | dockerComposeFile: docker/docker-compose.yml 50 | action: Build services 51 | projectName: microsoft-vswhere 52 | env: 53 | CONFIGURATION: ${{ parameters.BuildConfiguration }} 54 | 55 | - task: DockerCompose@1 56 | displayName: Runtime tests 57 | inputs: 58 | dockerComposeFile: docker/docker-compose.yml 59 | action: Run a specific service 60 | serviceName: test 61 | containerCommand: -Command Invoke-Pester C:\Tests -EnableExit -OutputFile C:\Tests\Results.xml -OutputFormat NUnitXml 62 | detached: false 63 | projectName: microsoft-vswhere 64 | env: 65 | CONFIGURATION: ${{ parameters.BuildConfiguration }} 66 | 67 | - task: PublishTestResults@2 68 | displayName: Publish test results 69 | inputs: 70 | buildConfiguration: ${{ parameters.BuildConfiguration }} 71 | buildPlatform: ${{ parameters.BuildPlatform }} 72 | testRunTitle: Runtime tests (${{ parameters.BuildConfiguration }}|${{ parameters.BuildPlatform }}) 73 | testResultsFormat: NUnit 74 | testResultsFiles: '**\*Results.xml' 75 | searchFolder: $(Build.SourcesDirectory)\docker\Tests 76 | mergeTestResults: true 77 | condition: succeededOrFailed() 78 | 79 | - script: | 80 | choco pack pkg\vswhere\vswhere.nuspec --out "bin\${{ parameters.BuildConfiguration }}" --version "%NBGV_NuGetPackageVersion%" "Configuration=${{ parameters.BuildConfiguration }}" "CommitId=$(Build.SourceVersion)" "Tag=$(Build.BuildNumber)" 81 | displayName: Package 82 | workingDirectory: $(Build.SourcesDirectory) 83 | 84 | - ${{ if eq(parameters.Sign, 'true') }}: 85 | - task: VSBuild@1 86 | displayName: Sign package 87 | inputs: 88 | solution: pkg\vswhere\vswhere.signproj 89 | configuration: ${{ parameters.BuildConfiguration }} 90 | platform: ${{ parameters.BuildPlatform }} 91 | 92 | - task: CopyFiles@2 93 | displayName: Copy build artifacts 94 | inputs: 95 | SourceFolder: $(Build.SourcesDirectory) 96 | Contents: | 97 | bin\${{ parameters.BuildConfiguration }}\** 98 | TargetFolder: $(Build.ArtifactStagingDirectory)\out 99 | 100 | - template: ${{ parameters.PublishArtifactTemplate }} 101 | parameters: 102 | displayName: Publish build artifacts 103 | path: $(Build.ArtifactStagingDirectory)\out 104 | artifactName: drop 105 | -------------------------------------------------------------------------------- /pkg/vswhere/.gitignore: -------------------------------------------------------------------------------- 1 | !build 2 | -------------------------------------------------------------------------------- /pkg/vswhere/build/vswhere.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildThisFileDirectory)..\tools\ 5 | 6 | 7 | -------------------------------------------------------------------------------- /pkg/vswhere/tools/VERIFICATION.txt: -------------------------------------------------------------------------------- 1 | VERIFICATION 2 | Verification is intended to assist the Chocolatey moderators and community 3 | in verifying that this package's contents are trustworthy. 4 | 5 | tools\vswhere.exe is produced by us from the same repository as this package: https://github.com/Microsoft/vswhere 6 | -------------------------------------------------------------------------------- /pkg/vswhere/vswhere.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | vswhere 5 | $Version$ 6 | Visual Studio Locator 7 | Microsoft 8 | Microsoft 9 | false 10 | https://go.microsoft.com/fwlink/?linkid=839265 11 | https://github.com/Microsoft/vswhere/tree/$CommitId$/LICENSE.txt 12 | https://github.com/Microsoft/vswhere 13 | Locate Visual Studio 2017 and newer installations 14 | Locate Visual Studio 2017 and newer installations 15 | © Microsoft Corporation. All rights reserved. 16 | vs vs2017 visualstudio 17 | true 18 | https://github.com/Microsoft/vswhere/tree/$CommitId$ 19 | https://github.com/Microsoft/vswhere/tree/$CommitId$/pkg/vswhere 20 | https://github.com/Microsoft/vswhere/wiki 21 | https://github.com/Microsoft/vswhere/issues 22 | https://github.com/Microsoft/vswhere/releases/tag/$Tag$ 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /pkg/vswhere/vswhere.signproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | ..\..\ 7 | $(SolutionDir)\ 8 | $(SolutionDir)bin\$(BuildConfiguration)\ 9 | $(OutDir)\ 10 | $(SolutionDir)obj\$(BuildConfiguration)\ 11 | $(IntermediateOutputPath)\ 12 | 13 | PrepareForBuild; 14 | $(SignDependsOn); 15 | 16 | 17 | 18 | 19 | 20 | 21 | NuGet 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 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}. 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/vswhere.lib/CoInitializer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class CoInitializer 9 | { 10 | public: 11 | CoInitializer() 12 | { 13 | auto hr = ::CoInitialize(NULL); 14 | if (FAILED(hr)) 15 | { 16 | throw win32_error(hr); 17 | } 18 | } 19 | 20 | ~CoInitializer() 21 | { 22 | ::CoUninitialize(); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/vswhere.lib/CommandArgs.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class Console; 9 | 10 | class CommandArgs 11 | { 12 | public: 13 | CommandArgs() noexcept : 14 | m_all(false), 15 | m_productsAll(false), 16 | m_requiresAny(false), 17 | m_latest(false), 18 | m_legacy(false), 19 | m_sort(false), 20 | m_prerelease(false), 21 | m_includePackages(false), 22 | m_nocolor(false), 23 | m_nologo(false), 24 | m_utf8(false), 25 | m_help(false) 26 | { 27 | } 28 | 29 | CommandArgs(const CommandArgs& obj) : 30 | m_applicationPath(obj.m_applicationPath), 31 | m_all(obj.m_all), 32 | m_productsAll(obj.m_productsAll), 33 | m_products(obj.m_products), 34 | m_requires(obj.m_requires), 35 | m_requiresPattern(obj.m_requiresPattern), 36 | m_version(obj.m_version), 37 | m_latest(obj.m_latest), 38 | m_legacy(obj.m_legacy), 39 | m_path(obj.m_path), 40 | m_sort(obj.m_sort), 41 | m_prerelease(obj.m_prerelease), 42 | m_format(obj.m_format), 43 | m_property(obj.m_property), 44 | m_includePackages(obj.m_includePackages), 45 | m_find(obj.m_find), 46 | m_nocolor(obj.m_nocolor), 47 | m_nologo(obj.m_nologo), 48 | m_utf8(obj.m_utf8), 49 | m_help(obj.m_help) 50 | { 51 | } 52 | 53 | const std::wstring& get_ApplicationPath() const noexcept 54 | { 55 | return m_applicationPath; 56 | } 57 | 58 | const bool get_All() const noexcept 59 | { 60 | return m_all; 61 | } 62 | 63 | const std::vector& get_Products() const noexcept 64 | { 65 | if (!m_productsAll && m_products.empty()) 66 | { 67 | return s_Products; 68 | } 69 | 70 | return m_products; 71 | } 72 | 73 | const std::vector& get_Requires() const noexcept 74 | { 75 | return m_requires; 76 | } 77 | 78 | const std::vector& get_RequiresPattern() const noexcept 79 | { 80 | return m_requiresPattern; 81 | } 82 | 83 | const bool get_RequiresAny() const noexcept 84 | { 85 | return m_requiresAny; 86 | } 87 | 88 | const std::wstring& get_Version() const noexcept 89 | { 90 | return m_version; 91 | } 92 | 93 | const bool get_Latest() const noexcept 94 | { 95 | return m_latest; 96 | } 97 | 98 | const bool get_Legacy() const noexcept 99 | { 100 | return m_legacy; 101 | } 102 | 103 | const std::wstring& get_Path() const noexcept 104 | { 105 | return m_path; 106 | } 107 | 108 | const bool get_Sort() const noexcept 109 | { 110 | return m_sort; 111 | } 112 | 113 | const bool get_Prerelease() const noexcept 114 | { 115 | return m_prerelease; 116 | } 117 | 118 | const std::wstring& get_Format() const noexcept 119 | { 120 | if (m_format.empty()) 121 | { 122 | return s_Format; 123 | } 124 | 125 | return m_format; 126 | } 127 | 128 | const std::wstring& get_Property() const noexcept 129 | { 130 | return m_property; 131 | } 132 | 133 | const bool get_IncludePackages() const noexcept 134 | { 135 | return m_includePackages; 136 | } 137 | 138 | const std::wstring& get_Find() const noexcept 139 | { 140 | return m_find; 141 | } 142 | 143 | const bool get_Color() const noexcept 144 | { 145 | return !m_nocolor; 146 | } 147 | 148 | const bool get_Logo() const noexcept 149 | { 150 | return !m_nologo; 151 | } 152 | 153 | const bool get_UTF8() const noexcept 154 | { 155 | return m_utf8; 156 | } 157 | 158 | const bool get_Help() const noexcept 159 | { 160 | return m_help; 161 | } 162 | 163 | 164 | void Parse(_In_ LPCWSTR wszCommandLine); 165 | void Parse(_In_ int argc, _In_ LPCWSTR argv[]); 166 | void Usage(_In_ Console& console) const; 167 | 168 | static std::wregex ParseRegex(_In_ const std::wstring& pattern) noexcept; 169 | 170 | private: 171 | static const std::vector s_Products; 172 | static const std::wstring s_Format; 173 | 174 | void Parse(_In_ std::vector args); 175 | 176 | std::wstring m_applicationPath; 177 | bool m_all; 178 | bool m_productsAll; 179 | std::vector m_products; 180 | std::vector m_requires; 181 | std::vector m_requiresPattern; 182 | bool m_requiresAny; 183 | std::wstring m_version; 184 | bool m_latest; 185 | bool m_legacy; 186 | std::wstring m_path; 187 | bool m_sort; 188 | bool m_prerelease; 189 | std::wstring m_format; 190 | std::wstring m_property; 191 | bool m_includePackages; 192 | std::wstring m_find; 193 | bool m_nocolor; 194 | bool m_nologo; 195 | bool m_utf8; 196 | bool m_help; 197 | }; 198 | -------------------------------------------------------------------------------- /src/vswhere.lib/CommandParser.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | vector CommandParser::Parse(_In_ LPCWSTR wszCommandLine) 11 | { 12 | _ASSERT(wszCommandLine && *wszCommandLine); 13 | 14 | int argc = 0; 15 | auto argv = ::CommandLineToArgvW(wszCommandLine, &argc); 16 | 17 | if (!argv) 18 | { 19 | throw win32_error(); 20 | } 21 | 22 | // Make sure the argument array is released when it falls out of scope. 23 | unique_ptr args(&argv, [](_In_opt_ LPWSTR** ppwsz) 24 | { 25 | if (ppwsz) 26 | { 27 | ::LocalFree(*ppwsz); 28 | } 29 | }); 30 | 31 | return Parse(argc, const_cast(*args)); 32 | } 33 | 34 | vector CommandParser::Parse(_In_ int argc, _In_ LPCWSTR argv[]) 35 | { 36 | vector tokens; 37 | 38 | // Parse program path from first argument. 39 | if (argc < 1) 40 | { 41 | // TODO: Provide localized error message. 42 | throw win32_error(ERROR_INVALID_PARAMETER, "missing program argument"); 43 | } 44 | 45 | m_path = argv[0]; 46 | 47 | // Parse remaining arguments. 48 | for (auto i = 1; i < argc; ++i) 49 | { 50 | auto arg = argv[i]; 51 | 52 | if (!arg || !*arg) 53 | { 54 | // TODO: Provide localized error message. 55 | throw win32_error(ERROR_INVALID_PARAMETER, "empty argument"); 56 | } 57 | 58 | if (L'-' == arg[0] || L'/' == arg[0]) 59 | { 60 | tokens.push_back({ Token::eParameter, &arg[1] }); 61 | } 62 | else 63 | { 64 | tokens.push_back({ Token::eArgument, arg }); 65 | } 66 | } 67 | 68 | return tokens; 69 | } 70 | -------------------------------------------------------------------------------- /src/vswhere.lib/CommandParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class CommandParser 9 | { 10 | public: 11 | struct Token 12 | { 13 | enum { eNone, eParameter, eArgument } Type; 14 | std::wstring Value; 15 | }; 16 | 17 | CommandParser() noexcept 18 | { 19 | } 20 | 21 | CommandParser(const CommandParser& obj) : 22 | m_path(obj.m_path) 23 | { 24 | } 25 | 26 | const std::wstring& get_Path() const noexcept 27 | { 28 | return m_path; 29 | } 30 | 31 | std::vector Parse(_In_ LPCWSTR wszCommandLine); 32 | std::vector Parse(_In_ int argc, _In_ LPCWSTR argv[]); 33 | 34 | private: 35 | std::wstring m_path; 36 | }; 37 | -------------------------------------------------------------------------------- /src/vswhere.lib/Console.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | void Console::Initialize() noexcept 11 | { 12 | if (!m_fInitialized) 13 | { 14 | if (m_args.get_UTF8()) 15 | { 16 | static_cast(::_setmode(_fileno(stdout), _O_U8TEXT)); 17 | } 18 | else if (IsConsole(stdout)) 19 | { 20 | static_cast(::_setmode(_fileno(stdout), _O_WTEXT)); 21 | } 22 | else 23 | { 24 | char sz[10]; 25 | ::sprintf_s(sz, ".%u", ::GetConsoleCP()); 26 | 27 | ::setlocale(LC_CTYPE, sz); 28 | } 29 | 30 | m_fColorSupported = IsVirtualTerminal(stdout); 31 | m_fInitialized = true; 32 | } 33 | } 34 | 35 | LPCWSTR Console::Color(_In_ LPCWSTR wzColor) const 36 | { 37 | if (IsColorSupported()) 38 | { 39 | return wzColor; 40 | } 41 | 42 | return L""; 43 | } 44 | 45 | LPCWSTR Console::ResetColor() const 46 | { 47 | if (IsColorSupported()) 48 | { 49 | return L"\033[0m"; 50 | } 51 | 52 | return L""; 53 | } 54 | 55 | void __cdecl Console::Write(_In_ LPCWSTR wzFormat, ...) 56 | { 57 | va_list args; 58 | 59 | va_start(args, wzFormat); 60 | Write(wzFormat, args); 61 | va_end(args); 62 | } 63 | 64 | void __cdecl Console::Write(_In_ const std::wstring& value) 65 | { 66 | Write(value.c_str(), NULL); 67 | } 68 | 69 | void __cdecl Console::WriteLine(_In_opt_ LPCWSTR wzFormat, ...) 70 | { 71 | if (wzFormat) 72 | { 73 | va_list args; 74 | 75 | va_start(args, wzFormat); 76 | Write(wzFormat, args); 77 | va_end(args); 78 | } 79 | 80 | Write(L"\n", NULL); 81 | } 82 | 83 | void __cdecl Console::WriteLine(_In_ const std::wstring& value) 84 | { 85 | Write(L"%ls\n", value.c_str()); 86 | } 87 | 88 | void Console::Write(_In_ LPCWSTR wzFormat, va_list args) 89 | { 90 | _ASSERTE(m_fInitialized); 91 | ::_vwprintf_p(wzFormat, args); 92 | } 93 | 94 | bool Console::IsConsole(_In_ FILE* f) noexcept 95 | { 96 | auto fno = ::_fileno(f); 97 | auto hFile = (HANDLE)::_get_osfhandle(fno); 98 | auto dwType = ::GetFileType(hFile); 99 | 100 | dwType &= ~FILE_TYPE_REMOTE; 101 | 102 | if (FILE_TYPE_CHAR != dwType) 103 | { 104 | return false; 105 | } 106 | 107 | DWORD dwMode; 108 | if (!::GetConsoleMode(hFile, &dwMode)) 109 | { 110 | return false; 111 | } 112 | 113 | return true; 114 | } 115 | 116 | bool Console::IsVirtualTerminal(_In_ FILE* f) noexcept 117 | { 118 | auto fno = ::_fileno(f); 119 | auto hFile = (HANDLE)::_get_osfhandle(fno); 120 | 121 | DWORD dwMode; 122 | if (::GetConsoleMode(hFile, &dwMode)) 123 | { 124 | return 0 != ::SetConsoleMode(hFile, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); 125 | } 126 | 127 | return false; 128 | } -------------------------------------------------------------------------------- /src/vswhere.lib/Console.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class Console 9 | { 10 | public: 11 | Console(_In_ const CommandArgs& args) : 12 | m_args(args), 13 | m_fInitialized(false), 14 | m_fColorSupported(false) 15 | { 16 | } 17 | 18 | Console(_In_ const Console& obj) : 19 | m_args(obj.m_args), 20 | m_fInitialized(obj.m_fInitialized), 21 | m_fColorSupported(obj.m_fColorSupported) 22 | { 23 | } 24 | 25 | virtual void Initialize() noexcept; 26 | 27 | LPCWSTR Color(_In_ LPCWSTR wzColor) const; 28 | LPCWSTR ResetColor() const; 29 | 30 | void __cdecl Write(_In_ LPCWSTR wzFormat, ...); 31 | void __cdecl Write(_In_ const std::wstring& value); 32 | void __cdecl WriteLine(_In_opt_ LPCWSTR wzFormat = NULL, ...); 33 | void __cdecl WriteLine(_In_ const std::wstring& value); 34 | 35 | virtual bool IsColorSupported() const 36 | { 37 | _ASSERTE(m_fInitialized); 38 | return m_fColorSupported && m_args.get_Color(); 39 | } 40 | 41 | protected: 42 | virtual void Write(_In_ LPCWSTR wzFormat, va_list args); 43 | 44 | const CommandArgs& Args() const noexcept 45 | { 46 | return m_args; 47 | } 48 | 49 | bool m_fInitialized; 50 | 51 | private: 52 | bool static IsConsole(_In_ FILE* f) noexcept; 53 | bool static IsVirtualTerminal(_In_ FILE* f) noexcept; 54 | 55 | const CommandArgs& m_args; 56 | bool m_fColorSupported; 57 | }; -------------------------------------------------------------------------------- /src/vswhere.lib/Exceptions.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | wstring win32_error::format_message(_In_ int code) 11 | { 12 | const size_t max = 65536; 13 | 14 | wstring err(max, wstring::value_type()); 15 | if (auto ch = ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, code, 0, const_cast(err.c_str()), err.size(), NULL)) 16 | { 17 | err.resize(ch); 18 | return err; 19 | } 20 | 21 | return L"unknown error"; 22 | } 23 | -------------------------------------------------------------------------------- /src/vswhere.lib/Exceptions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class win32_error : 9 | public std::system_error 10 | { 11 | public: 12 | win32_error() : 13 | win32_error(::GetLastError()) 14 | { 15 | } 16 | 17 | win32_error(_In_ int code, _In_ const std::string message = "") : 18 | system_error(code, std::system_category(), message), 19 | m_message(format_message(code)) 20 | { 21 | } 22 | 23 | win32_error(_In_ int code, _In_ const std::wstring message) : 24 | system_error(code, std::system_category(), to_string(message)), 25 | m_message(message) 26 | { 27 | } 28 | 29 | const wchar_t* wwhat() const 30 | { 31 | return m_message.c_str(); 32 | } 33 | 34 | private: 35 | static std::wstring format_message(_In_ int code); 36 | 37 | std::wstring m_message; 38 | }; 39 | -------------------------------------------------------------------------------- /src/vswhere.lib/Formatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class Formatter 9 | { 10 | public: 11 | typedef std::function(CommandArgs&, Console&)> FormatterFactory; 12 | typedef std::map, ci_less> FormatterMap; 13 | 14 | static std::unique_ptr Create(_In_ const std::wstring& type, _In_ CommandArgs& args, _In_ Console& console); 15 | static FormatterMap Formatters; 16 | 17 | Formatter(_In_ const Formatter& obj) : 18 | m_args(obj.m_args), 19 | m_console(obj.m_console), 20 | m_properties(obj.m_properties) 21 | { 22 | } 23 | 24 | void Write(_In_ const std::wstring& root, _In_ const std::wstring& name, _In_ const std::vector values); 25 | void Write(_In_ ISetupInstance* pInstance); 26 | void Write(_In_ std::vector instances); 27 | void WriteFiles(_In_ std::vector instances); 28 | 29 | virtual bool ShowLogo() const 30 | { 31 | return true; 32 | } 33 | 34 | virtual bool SupportsPackages() const 35 | { 36 | return false; 37 | } 38 | 39 | static const LPCWSTR ColorName; 40 | static const LPCWSTR ColorValue; 41 | 42 | protected: 43 | typedef std::function PropertyFunction; 44 | typedef std::vector> PropertyArray; 45 | 46 | static const std::wstring empty_wstring; 47 | 48 | Formatter(_In_ CommandArgs& args, _In_ Console& console); 49 | 50 | static std::wstring FormatDateISO8601(_In_ const FILETIME& value); 51 | 52 | virtual void StartDocument() {} 53 | virtual void StartArray(_In_opt_ const std::wstring& name = empty_wstring) {} 54 | virtual void StartObject(_In_opt_ const std::wstring& name = empty_wstring) {} 55 | virtual void WriteProperty(_In_ const std::wstring& name, _In_ const std::wstring& value) {} 56 | virtual void EndObject() {} 57 | virtual void EndArray() {} 58 | virtual void EndDocument() {} 59 | 60 | virtual void WriteProperty(_In_ const std::wstring& name, _In_ bool value) 61 | { 62 | WriteProperty(name, std::to_wstring(value)); 63 | } 64 | 65 | virtual void WriteProperty(_In_ const std::wstring& name, _In_ long long value) 66 | { 67 | WriteProperty(name, std::to_wstring(value)); 68 | } 69 | 70 | virtual std::wstring FormatDate(_In_ const FILETIME& value); 71 | 72 | CommandArgs& Args() const noexcept 73 | { 74 | return m_args; 75 | } 76 | 77 | Console& Console() const noexcept 78 | { 79 | return m_console; 80 | } 81 | 82 | private: 83 | static bool PropertyEqual(_In_ const std::wstring& name, _In_ PropertyArray::const_reference property); 84 | static HRESULT GetStringProperty(_In_ std::function pfn, _Out_ VARIANT* pvt); 85 | 86 | static const std::wstring s_delims; 87 | static ci_equal s_comparer; 88 | 89 | HRESULT GetInstanceId(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtInstanceId); 90 | HRESULT GetInstallDate(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtInstallDate); 91 | HRESULT GetInstallationName(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtInstallationName); 92 | HRESULT GetInstallationPath(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtInstallationPath); 93 | HRESULT GetInstallationVersion(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtInstallationVersion); 94 | HRESULT GetProductId(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtProductId); 95 | HRESULT GetProductPath(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtProductPath); 96 | HRESULT GetState(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtState); 97 | HRESULT GetIsComplete(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtIsComplete); 98 | HRESULT GetIsLaunchable(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtIsLaunchable); 99 | HRESULT GetIsPrerelease(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtIsPrerelease); 100 | HRESULT GetIsRebootRequired(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtIsRebootRequired); 101 | HRESULT GetDisplayName(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtDisplayName); 102 | HRESULT GetDescription(_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtDescription); 103 | 104 | void WriteInternal(_In_ ISetupInstance* pInstance); 105 | void WritePackage(_In_ ISetupPackageReference* pPackage); 106 | void WritePackages(_In_ ISetupInstance* pInstance); 107 | void WriteProperty(_In_ const std::wstring& name, _In_ const variant_t& value); 108 | bool WriteProperties(_In_ ISetupInstance* pInstance); 109 | bool WriteProperties(_In_ ISetupPropertyStore* pProperties, _In_opt_ const std::wstring& prefix = empty_wstring); 110 | 111 | CommandArgs& m_args; 112 | ::Console& m_console; 113 | PropertyArray m_properties; 114 | }; 115 | -------------------------------------------------------------------------------- /src/vswhere.lib/Glob.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | using namespace std::filesystem; 10 | 11 | std::wstring rtrim(const std::wstring& value); 12 | 13 | Glob::Glob(_In_ const wstring& root, _In_ const wstring& pattern) : 14 | m_root(rtrim(root)) 15 | { 16 | wstring value; 17 | wstring value_raw; 18 | wstring accumulator = L"^\\\\"; 19 | bool found_globstar = false; 20 | bool found_wildcard = false; 21 | 22 | wchar_t lastch = 0; 23 | for (auto ch : pattern) 24 | { 25 | switch (ch) 26 | { 27 | // Append directories to root until wildcard found, then accumulate to optimize match. 28 | case L'/': 29 | case L'\\': 30 | if (lastch == L'/' || lastch == L'\\') 31 | { 32 | // Ignore subsequent directory separators. 33 | continue; 34 | } 35 | 36 | if (value.length()) 37 | { 38 | if (found_globstar) 39 | { 40 | // Replace globstar with any character match plus directory separator. 41 | accumulator += L"(.*\\\\)?"; 42 | } 43 | else if (value_raw.length() == 1 && value_raw == L".") 44 | { 45 | // Ignore current directory references. 46 | value.clear(); 47 | value_raw.clear(); 48 | 49 | continue; 50 | } 51 | else if (value_raw.length() == 2 && value_raw == L"..") 52 | { 53 | // Block parent directory references. 54 | ThrowError(pattern); 55 | } 56 | else if (found_wildcard) 57 | { 58 | // A single star (asterisk) searches the any subdirectory not including the current directory. 59 | if (value.length() == 1 && value[0] == L'*') 60 | { 61 | accumulator += L"[^\\]*"; 62 | } 63 | else 64 | { 65 | accumulator += value; 66 | } 67 | } 68 | else 69 | { 70 | m_root /= value_raw; 71 | } 72 | 73 | value.clear(); 74 | value_raw.clear(); 75 | } 76 | 77 | if (found_wildcard) 78 | { 79 | if (found_globstar) 80 | { 81 | found_globstar = false; 82 | } 83 | else 84 | { 85 | // Only match directory separator if we didn't find a globstar. 86 | accumulator += L"\\\\"; 87 | } 88 | } 89 | 90 | break; 91 | 92 | case L'*': 93 | if (found_globstar) 94 | { 95 | ThrowError(pattern); 96 | } 97 | 98 | found_wildcard = true; 99 | 100 | if (value.length() == 1 && value[0] == L'*') 101 | { 102 | found_globstar = true; 103 | value += L'*'; 104 | } 105 | else if (value.length() > 0) 106 | { 107 | if (lastch == L'*') 108 | { 109 | // Invalid trailing globstar after non-separator. 110 | ThrowError(pattern); 111 | } 112 | 113 | value += L"[^\\]*"; 114 | } 115 | else 116 | { 117 | value += L'*'; 118 | } 119 | break; 120 | 121 | // Convert single character match. 122 | case L'?': 123 | if (found_globstar) 124 | { 125 | ThrowError(pattern); 126 | } 127 | 128 | found_wildcard = true; 129 | value += L"[^\\]"; 130 | break; 131 | 132 | default: 133 | if (found_globstar || IsInvalid(ch)) 134 | { 135 | // Invalid leading globstar before non-separator or invalid character. 136 | ThrowError(pattern); 137 | } 138 | else if (found_wildcard) 139 | { 140 | // A single star (asterisk) searches the any subdirectory not including the current directory. 141 | if (value.length() == 1 && value[0] == L'*') 142 | { 143 | accumulator += L"[^\\]*"; 144 | } 145 | else 146 | { 147 | accumulator += value; 148 | } 149 | 150 | value.clear(); 151 | } 152 | 153 | if (RequiresEscape(ch)) 154 | { 155 | value += L'\\'; 156 | } 157 | 158 | value += ch; 159 | value_raw += ch; 160 | 161 | break; 162 | } 163 | 164 | lastch = ch; 165 | } 166 | 167 | // Do not skip the directory separator: verify it as part of the regex. 168 | m_root_length = m_root.native().length(); 169 | 170 | if (value.length()) 171 | { 172 | if (found_globstar && value.length() == 2) 173 | { 174 | // Match anything after the directory separator for a trailing globstar. 175 | accumulator += L".*"; 176 | } 177 | else 178 | { 179 | accumulator += value; 180 | } 181 | } 182 | 183 | accumulator += L"$"; 184 | 185 | #ifdef _DEBUG 186 | m_pattern = accumulator; 187 | #endif 188 | 189 | try 190 | { 191 | m_re = wregex(accumulator, wregex::extended | wregex::icase | wregex::nosubs | wregex::optimize); 192 | } 193 | catch (const regex_error& ex) 194 | { 195 | _RPTN(_CRT_ERROR, "regex parse error: %s", ex.what()); 196 | ThrowError(pattern); 197 | } 198 | } 199 | 200 | const vector Glob::Entries(_In_ bool sort) const 201 | { 202 | vector entries; 203 | 204 | const auto& root = m_root.native(); 205 | for (const auto& entry : recursive_directory_iterator(m_root)) 206 | { 207 | const auto& path = entry.path().native(); 208 | if (Match(path)) 209 | { 210 | entries.push_back(path); 211 | } 212 | } 213 | 214 | if (sort) 215 | { 216 | static ci_less less; 217 | std::sort(entries.begin(), entries.end(), less); 218 | } 219 | 220 | return entries; 221 | } 222 | 223 | bool Glob::Match(_In_ const wstring& value) const 224 | { 225 | if (m_root_length < value.length()) 226 | { 227 | return regex_match(value.begin() + m_root_length, value.end(), m_re); 228 | } 229 | 230 | return false; 231 | } 232 | 233 | bool Glob::IsInvalid(_In_ const wchar_t ch) 234 | { 235 | return ch < 32 || ch == 124; 236 | } 237 | 238 | bool Glob::RequiresEscape(_In_ const wchar_t ch) 239 | { 240 | static const wchar_t escape[] = 241 | { 242 | L'.', 243 | L'(', 244 | L')', 245 | L'$', 246 | L'[', 247 | L']', 248 | L'{', 249 | L'}', 250 | L'+', 251 | }; 252 | static const size_t escape_len = sizeof(escape) / sizeof(*escape); 253 | 254 | for (unsigned char i = 0; i < escape_len; ++i) 255 | { 256 | if (ch == escape[i]) 257 | { 258 | return true; 259 | } 260 | } 261 | 262 | return false; 263 | } 264 | 265 | void Glob::ThrowError(_In_ const wstring& pattern) 266 | { 267 | auto message = ResourceManager::FormatString(IDS_E_INVALIDPATTERN, pattern.c_str()); 268 | throw win32_error(ERROR_INVALID_PARAMETER, message); 269 | } 270 | 271 | std::wstring rtrim(const std::wstring& value) 272 | { 273 | if (value.back() != L'\\' && value.back() != L'/') 274 | { 275 | return value; 276 | } 277 | 278 | wstring copy(value); 279 | wstring::reverse_iterator it = copy.rbegin(); 280 | while (it != copy.rend() && (*it == L'\\' || *it == L'/')) 281 | { 282 | it++; 283 | } 284 | 285 | copy.erase(it.base(), copy.end()); 286 | return copy; 287 | } -------------------------------------------------------------------------------- /src/vswhere.lib/Glob.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class Glob 9 | { 10 | public: 11 | Glob(_In_ const std::wstring& root, _In_ const std::wstring& pattern); 12 | Glob(_In_ const Glob& obj) noexcept : 13 | m_root(obj.m_root), 14 | m_root_length(obj.m_root_length), 15 | #ifdef _DEBUG 16 | m_pattern(obj.m_pattern), 17 | #endif 18 | m_re(obj.m_re) 19 | { 20 | } 21 | 22 | const std::wstring& Root() const noexcept 23 | { 24 | return m_root.native(); 25 | } 26 | 27 | const std::vector Entries(_In_ bool sort = false) const; 28 | bool Match(_In_ const std::wstring& value) const; 29 | 30 | private: 31 | static bool IsInvalid(_In_ const wchar_t ch); 32 | static bool RequiresEscape(_In_ const wchar_t ch); 33 | static void ThrowError(_In_ const std::wstring& pattern); 34 | 35 | std::filesystem::path m_root; 36 | size_t m_root_length; 37 | std::wregex m_re; 38 | 39 | #ifdef _DEBUG 40 | public: 41 | const std::wstring& Pattern() const noexcept 42 | { 43 | return m_pattern; 44 | } 45 | 46 | private: 47 | std::wstring m_pattern; 48 | #endif 49 | }; 50 | -------------------------------------------------------------------------------- /src/vswhere.lib/ILegacyProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class ILegacyProvider 9 | { 10 | public: 11 | virtual bool HasLegacyInstances() const = 0; 12 | virtual bool TryGetLegacyInstance(_In_ LPCWSTR wzVersion, _Out_ ISetupInstance** ppInstance) const = 0; 13 | }; 14 | -------------------------------------------------------------------------------- /src/vswhere.lib/InstanceSelector.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class InstanceSelector 9 | { 10 | public: 11 | InstanceSelector(_In_ const CommandArgs& args, _In_opt_ ISetupHelper* pHelper = NULL) : 12 | InstanceSelector(args, LegacyProvider::Instance, pHelper) 13 | { 14 | } 15 | 16 | InstanceSelector(_In_ const CommandArgs& args, _In_ ILegacyProvider& provider, _In_opt_ ISetupHelper* pHelper = NULL); 17 | InstanceSelector(_In_ const InstanceSelector& obj) : 18 | m_args(obj.m_args), 19 | m_provider(obj.m_provider), 20 | m_helper(obj.m_helper), 21 | m_ullMinimumVersion(obj.m_ullMinimumVersion), 22 | m_ullMaximumVersion(obj.m_ullMaximumVersion) 23 | { 24 | } 25 | 26 | bool Less(const ISetupInstancePtr& a, const ISetupInstancePtr& b) const; 27 | std::vector Select(_In_opt_ IEnumSetupInstances* pEnum) const; 28 | 29 | private: 30 | static ci_equal s_comparer; 31 | 32 | static std::wstring GetId(_In_ ISetupPackageReference* pPackageReference); 33 | bool IsMatch(_In_ ISetupInstance* pInstance) const; 34 | bool IsProductMatch(_In_ ISetupInstance2* pInstance) const; 35 | bool IsWorkloadMatch(_In_ ISetupInstance2* pInstance) const; 36 | bool IsVersionMatch(_In_ ISetupInstance* pInstance) const; 37 | bool HasVersionRange() const 38 | { 39 | return m_ullMinimumVersion != 0 && m_ullMaximumVersion != 0; 40 | } 41 | 42 | const CommandArgs& m_args; 43 | const ILegacyProvider& m_provider; 44 | ULONGLONG m_ullMinimumVersion; 45 | ULONGLONG m_ullMaximumVersion; 46 | ISetupHelperPtr m_helper; 47 | }; 48 | -------------------------------------------------------------------------------- /src/vswhere.lib/JsonFormatter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | const LPCWSTR JsonFormatter::ColorBool = L"\033[38;2;86;156;214m"; 11 | const LPCWSTR JsonFormatter::ColorNumber = L"\033[38;2;181;206;168m"; 12 | 13 | wstring JsonFormatter::Escape(_In_ const wstring& value) 14 | { 15 | wstring buffer; 16 | wstring::size_type pos = 0; 17 | wstring::size_type last = 0; 18 | 19 | while ((pos = value.find_first_of(L"\"\\", last)) != wstring::npos) 20 | { 21 | buffer.append(value, last, pos - last); 22 | buffer.push_back(L'\\'); 23 | buffer.push_back(value[pos]); 24 | 25 | last = ++pos; 26 | } 27 | 28 | buffer += value.substr(last); 29 | return buffer; 30 | } 31 | 32 | void JsonFormatter::StartArray(_In_opt_ const std::wstring& name) 33 | { 34 | StartScope(JsonScope::Type::array, name); 35 | } 36 | 37 | void JsonFormatter::StartObject(_In_opt_ const wstring& name) 38 | { 39 | StartScope(JsonScope::Type::object, name); 40 | } 41 | 42 | void JsonFormatter::WriteProperty(_In_ const wstring& name, _In_ const wstring& value) 43 | { 44 | StartProperty(name); 45 | 46 | auto escaped = Escape(value); 47 | Console().Write(L"%ls\"%ls\"%ls", Console().Color(ColorValue), escaped.c_str(), Console().ResetColor()); 48 | } 49 | 50 | void JsonFormatter::WriteProperty(_In_ const wstring& name, _In_ bool value) 51 | { 52 | StartProperty(name); 53 | Console().Write(L"%ls%ls%ls", Console().Color(ColorBool), value ? L"true" : L"false", Console().ResetColor()); 54 | } 55 | 56 | void JsonFormatter::WriteProperty(_In_ const wstring& name, _In_ long long value) 57 | { 58 | StartProperty(name); 59 | Console().Write(L"%ls%I64d%ls", Console().Color(ColorNumber), value, Console().ResetColor()); 60 | } 61 | 62 | void JsonFormatter::EndObject() 63 | { 64 | EndScope(); 65 | } 66 | 67 | void JsonFormatter::EndArray() 68 | { 69 | EndScope(); 70 | } 71 | 72 | void JsonFormatter::EndDocument() 73 | { 74 | Console().WriteLine(); 75 | } 76 | 77 | wstring JsonFormatter::FormatDate(_In_ const FILETIME& value) 78 | { 79 | return FormatDateISO8601(value); 80 | } 81 | 82 | void JsonFormatter::Push() 83 | { 84 | m_padding += std::wstring(padding_size, L' '); 85 | } 86 | 87 | void JsonFormatter::Pop() 88 | { 89 | if (m_padding.size() > 0) 90 | { 91 | m_padding.resize(m_padding.size() - padding_size); 92 | } 93 | } 94 | 95 | void JsonFormatter::StartScope(_In_ JsonScope::Type type, _In_ const std::wstring& name) 96 | { 97 | JsonScope* pParent = nullptr; 98 | if (m_scopes.size()) 99 | { 100 | auto& top = m_scopes.top(); 101 | top.StartScope(); 102 | 103 | pParent = ⊤ 104 | } 105 | 106 | m_scopes.push(JsonScope(pParent, Console(), m_padding, type, name)); 107 | 108 | // Always write the root scope. 109 | if (m_scopes.size() == 1) 110 | { 111 | m_scopes.top().WriteStart(); 112 | } 113 | 114 | Push(); 115 | } 116 | 117 | void JsonFormatter::StartProperty(_In_ const std::wstring& name) 118 | { 119 | m_scopes.top().StartProperty(); 120 | 121 | Console().Write(L"\n%ls", m_padding.c_str()); 122 | if (m_scopes.top().IsObject()) 123 | { 124 | Console().Write(L"%ls\"%ls\"%ls: ", Console().Color(ColorName), name.c_str(), Console().ResetColor()); 125 | } 126 | } 127 | 128 | void JsonFormatter::EndScope() 129 | { 130 | Pop(); 131 | 132 | m_scopes.top().WriteEnd(); 133 | m_scopes.pop(); 134 | } 135 | -------------------------------------------------------------------------------- /src/vswhere.lib/JsonFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class JsonFormatter : 9 | public Formatter 10 | { 11 | public: 12 | static std::unique_ptr Create(_In_ CommandArgs& args, _In_ ::Console& console) 13 | { 14 | return std::unique_ptr(new JsonFormatter(args, console)); 15 | } 16 | 17 | JsonFormatter(_In_ CommandArgs& args, _In_ ::Console& console) : 18 | Formatter(args, console) 19 | { 20 | } 21 | 22 | JsonFormatter(_In_ const JsonFormatter& obj) : 23 | Formatter(obj), 24 | m_padding(obj.m_padding), 25 | m_scopes(obj.m_scopes) 26 | { 27 | } 28 | 29 | static std::wstring Escape(_In_ const std::wstring& value); 30 | 31 | bool ShowLogo() const override 32 | { 33 | return false; 34 | } 35 | 36 | bool SupportsPackages() const override 37 | { 38 | return true; 39 | } 40 | 41 | static const LPCWSTR ColorBool; 42 | static const LPCWSTR ColorNumber; 43 | 44 | protected: 45 | void StartArray(_In_opt_ const std::wstring& name = empty_wstring) override; 46 | void StartObject(_In_opt_ const std::wstring& name = empty_wstring) override; 47 | void WriteProperty(_In_ const std::wstring& name, _In_ const std::wstring& value) override; 48 | void WriteProperty(_In_ const std::wstring& name, _In_ bool value) override; 49 | void WriteProperty(_In_ const std::wstring& name, _In_ long long value) override; 50 | void EndObject() override; 51 | void EndArray() override; 52 | void EndDocument() override; 53 | std::wstring FormatDate(_In_ const FILETIME& value) override; 54 | 55 | private: 56 | static const size_t padding_size = 2; 57 | 58 | void Push(); 59 | void Pop(); 60 | 61 | void StartScope(_In_ JsonScope::Type type, _In_ const std::wstring& name); 62 | void StartProperty(_In_ const std::wstring& name); 63 | void EndScope(); 64 | 65 | std::wstring m_padding; 66 | std::stack m_scopes; 67 | }; 68 | -------------------------------------------------------------------------------- /src/vswhere.lib/JsonScope.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | void JsonScope::StartScope() 11 | { 12 | WriteSeparator(); 13 | } 14 | 15 | void JsonScope::StartProperty() 16 | { 17 | // Delay writing the parent scope until we write a property. 18 | WriteStart(); 19 | 20 | WriteSeparator(); 21 | RequireSeparator(); 22 | } 23 | 24 | void JsonScope::WriteStartImpl() 25 | { 26 | bool writeKey = false; 27 | 28 | WriteSeparator(); 29 | 30 | if (Parent()) 31 | { 32 | if (Parent()->IsObject()) 33 | { 34 | writeKey = true; 35 | } 36 | 37 | // Write new line if not the root scope. 38 | Console().WriteLine(); 39 | } 40 | 41 | if (writeKey && Name().length()) 42 | { 43 | Console().Write( 44 | L"%ls%ls\"%ls\"%ls: %lc", 45 | Padding().c_str(), 46 | Console().Color(JsonFormatter::ColorName), 47 | Name().c_str(), 48 | Console().ResetColor(), 49 | StartChar()); 50 | } 51 | else 52 | { 53 | Console().Write(L"%ls%lc", Padding().c_str(), StartChar()); 54 | } 55 | } 56 | 57 | void JsonScope::WriteEndImpl() 58 | { 59 | if (m_requireSep) 60 | { 61 | // Write new line and padding only if elements were written. 62 | // This keeps empty arrays and objects looking like [] and {}. 63 | Console().Write(L"\n%ls", Padding().c_str()); 64 | } 65 | 66 | Console().Write(L"%lc", EndChar()); 67 | } 68 | 69 | void JsonScope::RequireSeparator() noexcept 70 | { 71 | m_requireSep = true; 72 | 73 | if (auto p = Parent()) 74 | { 75 | p->RequireSeparator(); 76 | } 77 | } 78 | 79 | void JsonScope::WriteSeparator() 80 | { 81 | if (m_requireSep) 82 | { 83 | Console().Write(L","); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/vswhere.lib/JsonScope.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class JsonScope : 9 | public Scope 10 | { 11 | public: 12 | typedef enum { array, object } Type; 13 | 14 | JsonScope(_In_opt_ JsonScope* pParent, _In_ ::Console& console, _In_ const std::wstring& padding, _In_ Type type, _In_ const std::wstring& name) : 15 | Scope(pParent, console, padding, name), 16 | m_type(type), 17 | m_requireSep(false) 18 | { 19 | } 20 | 21 | JsonScope(_In_ const JsonScope& obj) : 22 | Scope(obj), 23 | m_type(obj.m_type), 24 | m_requireSep(obj.m_requireSep) 25 | { 26 | } 27 | 28 | bool IsArray() const noexcept 29 | { 30 | return m_type == Type::array; 31 | } 32 | 33 | bool IsObject() const noexcept 34 | { 35 | return m_type == Type::object; 36 | } 37 | 38 | void StartScope(); 39 | void StartProperty(); 40 | 41 | protected: 42 | void WriteStartImpl() override; 43 | void WriteEndImpl() override; 44 | 45 | private: 46 | void RequireSeparator() noexcept; 47 | void WriteSeparator(); 48 | 49 | wchar_t StartChar() const noexcept 50 | { 51 | if (m_type == Type::array) 52 | { 53 | return L'['; 54 | } 55 | 56 | return L'{'; 57 | } 58 | 59 | wchar_t EndChar() const noexcept 60 | { 61 | if (m_type == Type::array) 62 | { 63 | return L']'; 64 | } 65 | 66 | return L'}'; 67 | } 68 | 69 | const Type m_type; 70 | bool m_requireSep; 71 | }; 72 | -------------------------------------------------------------------------------- /src/vswhere.lib/LegacyInstance.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | template 11 | inline HRESULT NotImplemented(_Out_opt_ T* p) 12 | { 13 | if (!p) 14 | { 15 | return E_POINTER; 16 | } 17 | 18 | *p = NULL; 19 | return E_NOTFOUND; 20 | } 21 | 22 | template<> 23 | inline HRESULT NotImplemented(_Out_opt_ LPFILETIME pft) 24 | { 25 | if (!pft) 26 | { 27 | return E_POINTER; 28 | } 29 | 30 | ::SecureZeroMemory(pft, sizeof(FILETIME)); 31 | return E_NOTFOUND; 32 | } 33 | 34 | STDMETHODIMP LegacyInstance::GetInstanceId( 35 | _Out_ BSTR* pbstrInstanceId 36 | ) 37 | { 38 | if (!pbstrInstanceId) 39 | { 40 | return E_POINTER; 41 | } 42 | 43 | *pbstrInstanceId = NULL; 44 | 45 | // Let exceptions throw since we're not a "true" COM object. 46 | wstring instanceId(L"VisualStudio."); 47 | instanceId += m_version; 48 | 49 | *pbstrInstanceId = ::SysAllocString(instanceId.c_str()); 50 | if (!pbstrInstanceId) 51 | { 52 | return E_OUTOFMEMORY; 53 | } 54 | 55 | return S_OK; 56 | } 57 | 58 | STDMETHODIMP LegacyInstance::GetInstallDate( 59 | _Out_ LPFILETIME pInstallDate 60 | ) 61 | { 62 | return NotImplemented(pInstallDate); 63 | } 64 | 65 | STDMETHODIMP LegacyInstance::GetInstallationName( 66 | _Out_ BSTR* pbstrInstallationName 67 | ) 68 | { 69 | return NotImplemented(pbstrInstallationName); 70 | } 71 | 72 | STDMETHODIMP LegacyInstance::GetInstallationPath( 73 | _Out_ BSTR* pbstrInstallationPath 74 | ) 75 | { 76 | if (!pbstrInstallationPath) 77 | { 78 | return E_POINTER; 79 | } 80 | 81 | *pbstrInstallationPath = ::SysAllocString(m_path.c_str()); 82 | if (!pbstrInstallationPath) 83 | { 84 | return E_OUTOFMEMORY; 85 | } 86 | 87 | return S_OK; 88 | } 89 | 90 | STDMETHODIMP LegacyInstance::GetInstallationVersion( 91 | _Out_ BSTR* pbstrInstallationVersion 92 | ) 93 | { 94 | if (!pbstrInstallationVersion) 95 | { 96 | return E_POINTER; 97 | } 98 | 99 | *pbstrInstallationVersion = ::SysAllocString(m_version.c_str()); 100 | if (!pbstrInstallationVersion) 101 | { 102 | return E_OUTOFMEMORY; 103 | } 104 | 105 | return S_OK; 106 | } 107 | 108 | STDMETHODIMP LegacyInstance::GetDisplayName( 109 | _In_ LCID lcid, 110 | _Out_ BSTR* pbstrDisplayName 111 | ) 112 | { 113 | return NotImplemented(pbstrDisplayName); 114 | } 115 | 116 | STDMETHODIMP LegacyInstance::GetDescription( 117 | _In_ LCID lcid, 118 | _Out_ BSTR* pbstrDescription 119 | ) 120 | { 121 | return NotImplemented(pbstrDescription); 122 | } 123 | 124 | STDMETHODIMP LegacyInstance::ResolvePath( 125 | _In_opt_z_ LPCOLESTR pwszRelativePath, 126 | _Out_ BSTR* pbstrAbsolutePath 127 | ) 128 | { 129 | return NotImplemented(pbstrAbsolutePath); 130 | } 131 | -------------------------------------------------------------------------------- /src/vswhere.lib/LegacyInstance.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class LegacyInstance : 9 | public ISetupInstance 10 | { 11 | public: 12 | LegacyInstance(_In_ LPCWSTR wzVersion, _In_ LPCWSTR wzPath) : 13 | m_version(wzVersion), 14 | m_path(wzPath), 15 | m_ulRef(1) 16 | { 17 | } 18 | 19 | // IUnknown 20 | STDMETHODIMP QueryInterface( 21 | _In_ REFIID riid, 22 | _Outptr_ LPVOID *ppvObject) 23 | { 24 | if (!ppvObject) 25 | { 26 | return E_POINTER; 27 | } 28 | 29 | HRESULT hr = S_OK; 30 | if (riid == __uuidof(ISetupInstance)) 31 | { 32 | AddRef(); 33 | *ppvObject = static_cast(this); 34 | } 35 | else if (riid == __uuidof(IUnknown)) 36 | { 37 | AddRef(); 38 | *ppvObject = static_cast(this); 39 | } 40 | else 41 | { 42 | hr = E_NOINTERFACE; 43 | } 44 | 45 | return hr; 46 | } 47 | 48 | STDMETHODIMP_(ULONG) AddRef(void) 49 | { 50 | return ::InterlockedIncrement(&m_ulRef); 51 | } 52 | 53 | STDMETHODIMP_(ULONG) Release(void) 54 | { 55 | auto ulRef = ::InterlockedDecrement(&m_ulRef); 56 | if (!ulRef) 57 | { 58 | delete this; 59 | } 60 | 61 | return ulRef; 62 | } 63 | 64 | public: 65 | // SetupInstance 66 | STDMETHOD(GetInstanceId)( 67 | _Out_ BSTR* pbstrInstanceId 68 | ); 69 | 70 | STDMETHOD(GetInstallDate)( 71 | _Out_ LPFILETIME pInstallDate 72 | ); 73 | 74 | STDMETHOD(GetInstallationName)( 75 | _Out_ BSTR* pbstrInstallationName 76 | ); 77 | 78 | STDMETHOD(GetInstallationPath)( 79 | _Out_ BSTR* pbstrInstallationPath 80 | ); 81 | 82 | STDMETHOD(GetInstallationVersion)( 83 | _Out_ BSTR* pbstrInstallationVersion 84 | ); 85 | 86 | STDMETHOD(GetDisplayName)( 87 | _In_ LCID lcid, 88 | _Out_ BSTR* pbstrDisplayName 89 | ); 90 | 91 | STDMETHOD(GetDescription)( 92 | _In_ LCID lcid, 93 | _Out_ BSTR* pbstrDescription 94 | ); 95 | 96 | STDMETHOD(ResolvePath)( 97 | _In_opt_z_ LPCOLESTR pwszRelativePath, 98 | _Out_ BSTR* pbstrAbsolutePath 99 | ); 100 | 101 | private: 102 | const std::wstring m_version; 103 | const std::wstring m_path; 104 | ULONG m_ulRef; 105 | }; 106 | -------------------------------------------------------------------------------- /src/vswhere.lib/LegacyProvider.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | ILegacyProvider& LegacyProvider::Instance = LegacyProvider(); 11 | 12 | bool LegacyProvider::HasLegacyInstances() const 13 | { 14 | return NULL != m_hKey; 15 | } 16 | 17 | bool LegacyProvider::TryGetLegacyInstance(_In_ LPCWSTR wzVersion, _Out_ ISetupInstance** ppInstance) const 18 | { 19 | _ASSERT(ppInstance); 20 | 21 | *ppInstance = NULL; 22 | 23 | if (!m_hKey) 24 | { 25 | return false; 26 | } 27 | 28 | DWORD dwType = 0; 29 | DWORD cbData = 0; 30 | 31 | auto err = ::RegQueryValueExW(m_hKey, wzVersion, NULL, &dwType, NULL, &cbData); 32 | if (ERROR_FILE_NOT_FOUND == err || REG_SZ != dwType) 33 | { 34 | return false; 35 | } 36 | else if (ERROR_SUCCESS != err) 37 | { 38 | throw win32_error(err); 39 | } 40 | 41 | wstring sz(cbData / sizeof(wstring::value_type), wstring::value_type()); 42 | auto lpData = reinterpret_cast(const_cast(sz.c_str())); 43 | 44 | err = ::RegQueryValueExW(m_hKey, wzVersion, NULL, NULL, lpData, &cbData); 45 | if (ERROR_SUCCESS != err) 46 | { 47 | throw win32_error(err); 48 | } 49 | 50 | *ppInstance = new (nothrow) LegacyInstance(wzVersion, sz.c_str()); 51 | if (!*ppInstance) 52 | { 53 | throw win32_error(E_OUTOFMEMORY); 54 | } 55 | 56 | return true; 57 | } 58 | -------------------------------------------------------------------------------- /src/vswhere.lib/LegacyProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class LegacyProvider : 9 | public ILegacyProvider 10 | { 11 | public: 12 | static ILegacyProvider& Instance; 13 | 14 | LegacyProvider() : 15 | m_hKey(NULL) 16 | { 17 | auto err = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &m_hKey); 18 | if (ERROR_SUCCESS != err && ERROR_FILE_NOT_FOUND != err) 19 | { 20 | throw win32_error(err); 21 | } 22 | } 23 | 24 | LegacyProvider(const LegacyProvider& obj) = delete; 25 | 26 | ~LegacyProvider() 27 | { 28 | if (m_hKey) 29 | { 30 | ::CloseHandle(m_hKey); 31 | m_hKey = NULL; 32 | } 33 | } 34 | 35 | bool HasLegacyInstances() const override; 36 | bool TryGetLegacyInstance(_In_ LPCWSTR wzVersion, _Out_ ISetupInstance** ppInstance) const override; 37 | 38 | private: 39 | HKEY m_hKey; 40 | }; 41 | -------------------------------------------------------------------------------- /src/vswhere.lib/Module.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | void Module::FromIUnknown(_In_opt_ const IUnknown* pUnk) noexcept 11 | { 12 | typedef struct IUnknownVtbl 13 | { 14 | HRESULT(STDMETHODCALLTYPE *QueryInterface)(ISetupConfiguration*, REFIID, LPVOID*); 15 | ULONG(STDMETHODCALLTYPE *AddRef)(ISetupConfiguration*); 16 | ULONG(STDMETHODCALLTYPE *Release)(ISetupConfiguration*); 17 | } IUnknownVtbl; 18 | 19 | if (pUnk) 20 | { 21 | auto pVtbl = reinterpret_cast(pUnk); 22 | ::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(pVtbl->QueryInterface), &m_hModule); 23 | } 24 | } 25 | 26 | const wstring& Module::get_Path() noexcept 27 | { 28 | if (m_hModule && m_path.empty()) 29 | { 30 | m_path.resize(MAX_PATH); 31 | ::GetModuleFileNameW(m_hModule, const_cast(m_path.data()), m_path.size()); 32 | } 33 | 34 | return m_path; 35 | } 36 | 37 | const wstring& Module::get_FileVersion() noexcept 38 | { 39 | auto& path = get_Path(); 40 | if (path.empty()) 41 | { 42 | return m_fileVersion; 43 | } 44 | 45 | DWORD dwHandle = 0; 46 | DWORD cbVersionInfo = ::GetFileVersionInfoSizeW(path.c_str(), &dwHandle); 47 | if (!cbVersionInfo) 48 | { 49 | return m_fileVersion; 50 | } 51 | 52 | vector buffer(cbVersionInfo); 53 | if (!::GetFileVersionInfoW(path.c_str(), 0, buffer.size(), buffer.data())) 54 | { 55 | return m_fileVersion; 56 | } 57 | 58 | VS_FIXEDFILEINFO* pFileInfo = NULL; 59 | UINT cbFileInfo = 0; 60 | if (!::VerQueryValueW(buffer.data(), L"\\", reinterpret_cast(&pFileInfo), &cbFileInfo)) 61 | { 62 | return m_fileVersion; 63 | } 64 | 65 | auto cch = _scwprintf( 66 | L"%u.%u.%u.%u", 67 | (pFileInfo->dwFileVersionMS >> 16) & 0xffff, 68 | pFileInfo->dwFileVersionMS & 0xffff, 69 | (pFileInfo->dwFileVersionLS >> 16) & 0xffff, 70 | pFileInfo->dwFileVersionLS & 0xffff); 71 | if (!cch) 72 | { 73 | return m_fileVersion; 74 | } 75 | 76 | m_fileVersion.resize(++cch); 77 | swprintf_s( 78 | const_cast(m_fileVersion.c_str()), 79 | m_fileVersion.size(), 80 | L"%u.%u.%u.%u", 81 | (pFileInfo->dwFileVersionMS >> 16) & 0xffff, 82 | pFileInfo->dwFileVersionMS & 0xffff, 83 | (pFileInfo->dwFileVersionLS >> 16) & 0xffff, 84 | pFileInfo->dwFileVersionLS & 0xffff); 85 | 86 | // Trim terminating null character. 87 | m_fileVersion.resize(--cch); 88 | 89 | return m_fileVersion; 90 | } 91 | -------------------------------------------------------------------------------- /src/vswhere.lib/Module.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class Module 9 | { 10 | public: 11 | Module() : 12 | m_hModule(NULL) 13 | { 14 | } 15 | 16 | Module(_In_ const Module& obj) : 17 | m_hModule(obj.m_hModule), 18 | m_path(obj.m_path), 19 | m_fileVersion(obj.m_fileVersion) 20 | { 21 | } 22 | 23 | ~Module() 24 | { 25 | if (m_hModule) 26 | { 27 | ::FreeLibrary(m_hModule); 28 | } 29 | } 30 | 31 | // Avoid throwing non-catastrophic exceptions since information is for diagnostics only. 32 | void FromIUnknown(_In_opt_ const IUnknown* pUnk) noexcept; 33 | const std::wstring& get_Path() noexcept; 34 | const std::wstring& get_FileVersion() noexcept; 35 | 36 | private: 37 | HMODULE m_hModule; 38 | std::wstring m_path; 39 | std::wstring m_fileVersion; 40 | }; 41 | -------------------------------------------------------------------------------- /src/vswhere.lib/ResourceManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | HINSTANCE ResourceManager::s_hInstance = ::GetModuleHandleW(NULL); 11 | 12 | wstring ResourceManager::GetString(_In_ DWORD nID) 13 | { 14 | LPCWSTR wsz = NULL; 15 | 16 | auto ch = ::LoadStringW(s_hInstance, nID, (LPWSTR)&wsz, 0); 17 | if (!ch) 18 | { 19 | throw win32_error(); 20 | } 21 | 22 | return wstring(wsz, wsz + ch); 23 | } 24 | 25 | wstring ResourceManager::FormatString(_In_ DWORD nID, _In_ va_list args) 26 | { 27 | auto fmt = GetString(nID); 28 | 29 | auto ch = _vscwprintf_p(fmt.c_str(), args); 30 | if (0 > ch) 31 | { 32 | throw win32_error(ERROR_INVALID_PARAMETER); 33 | } 34 | 35 | wstring wsz; 36 | wsz.resize(++ch); 37 | 38 | if (0 > _vswprintf_p(&wsz[0], ch, fmt.c_str(), args)) 39 | { 40 | throw win32_error(ERROR_INVALID_PARAMETER); 41 | } 42 | 43 | // trim the terminating null 44 | wsz.resize(ch - 1); 45 | 46 | return wsz; 47 | } 48 | -------------------------------------------------------------------------------- /src/vswhere.lib/ResourceManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class ResourceManager 9 | { 10 | public: 11 | static void SetInstance(_In_ HINSTANCE hInstance) 12 | { 13 | s_hInstance = hInstance; 14 | } 15 | 16 | static std::wstring GetString(_In_ DWORD nID); 17 | static std::wstring __cdecl FormatString(_In_ DWORD nID, ...) 18 | { 19 | va_list args; 20 | 21 | va_start(args, nID); 22 | auto wsz = FormatString(nID, args); 23 | va_end(args); 24 | 25 | return wsz; 26 | } 27 | 28 | private: 29 | ResourceManager() {} 30 | ResourceManager(const ResourceManager& obj) {} 31 | ResourceManager(ResourceManager&& obj) noexcept {} 32 | 33 | static std::wstring FormatString(_In_ DWORD nID, _In_ va_list args); 34 | 35 | static HINSTANCE s_hInstance; 36 | }; 37 | -------------------------------------------------------------------------------- /src/vswhere.lib/SafeArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | // Simple container for a single-dimension SAFEARRAY. 9 | template 10 | class SafeArray 11 | { 12 | public: 13 | SafeArray(_In_ const LPSAFEARRAY psa) : 14 | m_psa(psa) 15 | { 16 | Lock(); 17 | } 18 | 19 | SafeArray(_In_ const SafeArray& obj) : 20 | m_psa(obj.m_psa) 21 | { 22 | Lock(); 23 | } 24 | 25 | ~SafeArray() 26 | { 27 | Unlock(); 28 | Destroy(); 29 | } 30 | 31 | const std::vector<_Element>& Elements() const 32 | { 33 | return m_elements; 34 | } 35 | 36 | private: 37 | void Lock() 38 | { 39 | if (m_psa) 40 | { 41 | auto hr = ::SafeArrayLock(m_psa); 42 | if (FAILED(hr)) 43 | { 44 | throw win32_error(hr); 45 | } 46 | 47 | auto pvData = (_Element*)m_psa->pvData; 48 | auto celt = m_psa->rgsabound[0].cElements; 49 | 50 | m_elements.assign(pvData, pvData + celt); 51 | } 52 | } 53 | 54 | void Unlock() 55 | { 56 | if (m_psa) 57 | { 58 | ::SafeArrayUnlock(m_psa); 59 | } 60 | } 61 | 62 | void Destroy() 63 | { 64 | if (m_psa) 65 | { 66 | ::SafeArrayDestroy(m_psa); 67 | } 68 | } 69 | 70 | LPSAFEARRAY m_psa; 71 | std::vector<_Element> m_elements; 72 | }; 73 | -------------------------------------------------------------------------------- /src/vswhere.lib/Scope.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | template 9 | class Scope 10 | { 11 | public: 12 | Scope(_In_opt_ _Type* pParent, _In_ ::Console& console, _In_ const std::wstring& padding, _In_ const std::wstring& name) : 13 | m_pParent(pParent), 14 | m_console(console), 15 | m_padding(padding), 16 | m_name(name), 17 | m_writeStart(true), 18 | m_writeEnd(false) 19 | { 20 | } 21 | 22 | Scope(_In_opt_ _Type* pParent, _In_ ::Console& console, _In_ std::wstring& padding, _In_ std::wstring::const_pointer name) : 23 | m_pParent(pParent), 24 | m_console(console), 25 | m_padding(padding), 26 | m_name(name), 27 | m_writeStart(true), 28 | m_writeEnd(false) 29 | { 30 | } 31 | 32 | Scope(_In_ const Scope& obj) : 33 | m_pParent(obj.m_pParent), 34 | m_console(obj.m_console), 35 | m_padding(obj.m_padding), 36 | m_name(obj.m_name), 37 | m_writeStart(obj.m_writeStart), 38 | m_writeEnd(obj.m_writeEnd) 39 | { 40 | } 41 | 42 | void WriteStart() 43 | { 44 | if (m_writeStart) 45 | { 46 | // Write the parent scope first (may have already been written to console). 47 | if (m_pParent) 48 | { 49 | m_pParent->WriteStart(); 50 | } 51 | 52 | WriteStartImpl(); 53 | m_writeStart = false; 54 | m_writeEnd = true; 55 | } 56 | } 57 | 58 | void WriteEnd() 59 | { 60 | if (m_writeEnd) 61 | { 62 | WriteEndImpl(); 63 | } 64 | } 65 | 66 | protected: 67 | _Type* Parent() const noexcept 68 | { 69 | return m_pParent; 70 | } 71 | 72 | ::Console& Console() const noexcept 73 | { 74 | return m_console; 75 | } 76 | 77 | const std::wstring& Padding() const noexcept 78 | { 79 | return m_padding; 80 | } 81 | 82 | const std::wstring& Name() const noexcept 83 | { 84 | return m_name; 85 | } 86 | 87 | virtual void WriteStartImpl() = 0; 88 | virtual void WriteEndImpl() = 0; 89 | 90 | private: 91 | _Type* m_pParent; 92 | ::Console& m_console; 93 | const std::wstring m_padding; 94 | const std::wstring m_name; 95 | bool m_writeStart; 96 | bool m_writeEnd; 97 | }; 98 | -------------------------------------------------------------------------------- /src/vswhere.lib/TextFormatter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | void TextFormatter::StartArray(_In_opt_ const std::wstring& name) 11 | { 12 | m_first = true; 13 | } 14 | 15 | void TextFormatter::StartObject(_In_opt_ const wstring& name) 16 | { 17 | if (!m_first && m_scopes.empty()) 18 | { 19 | Console().WriteLine(); 20 | } 21 | 22 | m_first = false; 23 | 24 | if (!m_scopes.empty()) 25 | { 26 | m_scopes.push(m_scopes.top() + name + L"_"); 27 | } 28 | else if (!name.empty()) 29 | { 30 | m_scopes.push(name + L"_"); 31 | } 32 | else 33 | { 34 | m_scopes.push(name); 35 | } 36 | } 37 | 38 | void TextFormatter::WriteProperty(_In_ const std::wstring& name, _In_ const std::wstring& value) 39 | { 40 | wstring prefix = L""; 41 | if (!m_scopes.empty()) 42 | { 43 | prefix = m_scopes.top(); 44 | } 45 | 46 | Console().Write(L"%ls%ls%ls%ls: ", Console().Color(ColorName), prefix.c_str(), name.c_str(), Console().ResetColor()); 47 | Console().WriteLine(L"%ls%ls%ls", Console().Color(ColorValue), value.c_str(), Console().ResetColor()); 48 | } 49 | 50 | void TextFormatter::EndObject() 51 | { 52 | m_scopes.pop(); 53 | } -------------------------------------------------------------------------------- /src/vswhere.lib/TextFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class TextFormatter : 9 | public Formatter 10 | { 11 | public: 12 | static std::unique_ptr Create(_In_ CommandArgs& args, _In_ ::Console& console) 13 | { 14 | return std::unique_ptr(new TextFormatter(args, console)); 15 | } 16 | 17 | TextFormatter(_In_ CommandArgs& args, _In_ ::Console& console) : 18 | Formatter(args, console), 19 | m_first(false) 20 | { 21 | } 22 | 23 | TextFormatter(_In_ const TextFormatter& obj) : 24 | Formatter(obj), 25 | m_first(obj.m_first), 26 | m_scopes(obj.m_scopes) 27 | { 28 | } 29 | 30 | protected: 31 | void StartArray(_In_opt_ const std::wstring& name = empty_wstring) override; 32 | void StartObject(_In_opt_ const std::wstring& name = empty_wstring) override; 33 | void WriteProperty(_In_ const std::wstring& name, _In_ const std::wstring& value) override; 34 | void EndObject() override; 35 | 36 | private: 37 | bool m_first; 38 | std::stack m_scopes; 39 | }; 40 | -------------------------------------------------------------------------------- /src/vswhere.lib/Utilities.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | std::string to_string(const std::wstring& value) { 9 | if (value.empty()) return std::string(); 10 | int size_needed = WideCharToMultiByte(CP_UTF8, 0, value.data(), (int)value.size(), NULL, 0, NULL, NULL); 11 | std::string result(size_needed, 0); 12 | WideCharToMultiByte(CP_UTF8, 0, value.data(), (int)value.size(), result.data(), size_needed, NULL, NULL); 13 | return result; 14 | } 15 | -------------------------------------------------------------------------------- /src/vswhere.lib/Utilities.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | std::string to_string(const std::wstring& value); 9 | 10 | struct ci_equal 11 | { 12 | bool operator()(const std::wstring& lhs, const std::wstring& rhs) const 13 | { 14 | return CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lhs.c_str(), lhs.size(), rhs.c_str(), rhs.size()); 15 | } 16 | }; 17 | 18 | struct ci_less 19 | { 20 | bool operator()(const std::wstring& lhs, const std::wstring& rhs) const 21 | { 22 | return CSTR_EQUAL > ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lhs.c_str(), lhs.size(), rhs.c_str(), rhs.size()); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/vswhere.lib/ValueFormatter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | void ValueFormatter::WriteProperty(_In_ const std::wstring& name, _In_ const std::wstring& value) 11 | { 12 | Console().WriteLine(value); 13 | } 14 | -------------------------------------------------------------------------------- /src/vswhere.lib/ValueFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class ValueFormatter : 9 | public Formatter 10 | { 11 | public: 12 | static std::unique_ptr Create(_In_ CommandArgs& args, _In_ ::Console& console) 13 | { 14 | return std::unique_ptr(new ValueFormatter(args, console)); 15 | } 16 | 17 | ValueFormatter(_In_ CommandArgs& args, _In_ ::Console& console) : 18 | Formatter(args, console) 19 | { 20 | } 21 | 22 | ValueFormatter(_In_ const ValueFormatter& obj) : 23 | Formatter(obj) 24 | { 25 | } 26 | 27 | bool ShowLogo() const override 28 | { 29 | return false; 30 | } 31 | 32 | protected: 33 | void WriteProperty(_In_ const std::wstring& name, _In_ const std::wstring& value) override; 34 | }; 35 | -------------------------------------------------------------------------------- /src/vswhere.lib/VersionRange.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class VersionRange : 9 | public ISetupHelper 10 | { 11 | public: 12 | VersionRange() noexcept : 13 | m_ulRef(1) 14 | { 15 | } 16 | 17 | private: 18 | ~VersionRange() 19 | { 20 | } 21 | 22 | public: 23 | // IUnknown 24 | STDMETHODIMP QueryInterface( 25 | _In_ REFIID iid, 26 | _Out_ LPVOID* ppUnk 27 | ) 28 | { 29 | HRESULT hr = S_OK; 30 | IUnknown* pUnk = NULL; 31 | 32 | if (!ppUnk) 33 | { 34 | return E_POINTER; 35 | } 36 | 37 | *ppUnk = NULL; 38 | if (iid == __uuidof(ISetupHelper)) 39 | { 40 | pUnk = static_cast(this); 41 | } 42 | else if (iid == IID_IUnknown) 43 | { 44 | pUnk = static_cast(this); 45 | } 46 | else 47 | { 48 | hr = E_NOINTERFACE; 49 | } 50 | 51 | if (pUnk) 52 | { 53 | pUnk->AddRef(); 54 | *ppUnk = pUnk; 55 | } 56 | 57 | return hr; 58 | } 59 | 60 | STDMETHODIMP_(ULONG) AddRef() 61 | { 62 | return ::InterlockedIncrement(&m_ulRef); 63 | } 64 | 65 | STDMETHODIMP_(ULONG) Release() 66 | { 67 | ULONG ulRef = ::InterlockedDecrement(&m_ulRef); 68 | if (ulRef == 0) 69 | { 70 | delete this; 71 | } 72 | 73 | return ulRef; 74 | } 75 | 76 | public: 77 | // ISetupHelper 78 | STDMETHOD(ParseVersion)( 79 | _In_ LPCOLESTR pwszVersion, 80 | _Out_ PULONGLONG pullVersion 81 | ) noexcept; 82 | 83 | STDMETHOD(ParseVersionRange)( 84 | _In_ LPCOLESTR pwszVersionRange, 85 | _Out_ PULONGLONG pullMinVersion, 86 | _Out_ PULONGLONG pullMaxVersion 87 | ); 88 | 89 | static const ULONGLONG MinVersion = 0; 90 | static const ULONGLONG MaxVersion = 18446744073709551615; 91 | 92 | private: 93 | static HRESULT ParseVersionString( 94 | _In_ const LPCWSTR pwszVersionBegin, 95 | _In_ const LPCWSTR pwszVersionEnd, 96 | _Out_ PULONGLONG pullVersion 97 | ); 98 | 99 | static LPWSTR Trim( 100 | _In_ const LPCWSTR pwszValue, 101 | _Out_ LPWSTR* ppwszEnd 102 | ) noexcept; 103 | 104 | ULONG m_ulRef; 105 | }; 106 | -------------------------------------------------------------------------------- /src/vswhere.lib/XmlFormatter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | const LPCWSTR XmlFormatter::ColorTag = L"\033[38;2;86;156;214m"; 11 | 12 | void XmlFormatter::StartDocument() 13 | { 14 | Console().WriteLine( 15 | L"%1$ls%4$ls", 16 | Console().Color(ColorTag), 17 | Console().Color(ColorName), 18 | Console().Color(ColorValue), 19 | Console().ResetColor()); 20 | } 21 | 22 | void XmlFormatter::StartArray(_In_opt_ const wstring& name) 23 | { 24 | StartScope(name, L"instances"); 25 | } 26 | 27 | void XmlFormatter::StartObject(_In_opt_ const wstring& name) 28 | { 29 | StartScope(name, L"instance"); 30 | } 31 | 32 | void XmlFormatter::WriteProperty(_In_ const wstring& name, _In_ const wstring& value) 33 | { 34 | m_scopes.top().WriteStart(); 35 | 36 | Console().WriteLine( 37 | L"%1$ls%4$ls<%2$ls>%5$ls%3$ls%4$ls%5$ls", 38 | m_padding.c_str(), 39 | name.c_str(), 40 | value.c_str(), 41 | Console().Color(ColorTag), 42 | Console().ResetColor()); 43 | } 44 | 45 | void XmlFormatter::EndObject() 46 | { 47 | EndScope(); 48 | } 49 | 50 | void XmlFormatter::EndArray() 51 | { 52 | EndScope(); 53 | } 54 | 55 | wstring XmlFormatter::FormatDate(_In_ const FILETIME& value) 56 | { 57 | return FormatDateISO8601(value); 58 | } 59 | 60 | void XmlFormatter::Push() 61 | { 62 | m_padding += std::wstring(padding_size, L' '); 63 | } 64 | 65 | void XmlFormatter::Pop() 66 | { 67 | if (m_padding.size() > 0) 68 | { 69 | m_padding.resize(m_padding.size() - padding_size); 70 | } 71 | } 72 | 73 | void XmlFormatter::StartScope(_In_opt_ const wstring& name, _In_ std::wstring::const_pointer fallback) 74 | { 75 | XmlScope* pParent = nullptr; 76 | if (m_scopes.size()) 77 | { 78 | pParent = &m_scopes.top(); 79 | } 80 | 81 | if (name.empty()) 82 | { 83 | m_scopes.push(XmlScope(pParent, Console(), m_padding, fallback)); 84 | } 85 | else 86 | { 87 | m_scopes.push(XmlScope(pParent, Console(), m_padding, name)); 88 | } 89 | 90 | // Always write the root scope. 91 | if (m_scopes.size() == 1) 92 | { 93 | m_scopes.top().WriteStart(); 94 | } 95 | 96 | Push(); 97 | } 98 | 99 | void XmlFormatter::EndScope() 100 | { 101 | Pop(); 102 | 103 | m_scopes.top().WriteEnd(); 104 | m_scopes.pop(); 105 | } 106 | -------------------------------------------------------------------------------- /src/vswhere.lib/XmlFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class XmlFormatter : 9 | public Formatter 10 | { 11 | public: 12 | static std::unique_ptr Create(_In_ CommandArgs& args, _In_ ::Console& console) 13 | { 14 | return std::unique_ptr(new XmlFormatter(args, console)); 15 | } 16 | 17 | XmlFormatter(_In_ CommandArgs& args, _In_ ::Console& console) : 18 | Formatter(args, console) 19 | { 20 | } 21 | 22 | XmlFormatter(_In_ const XmlFormatter& obj) : 23 | Formatter(obj), 24 | m_padding(obj.m_padding), 25 | m_scopes(obj.m_scopes) 26 | { 27 | } 28 | 29 | bool ShowLogo() const override 30 | { 31 | return false; 32 | } 33 | 34 | bool SupportsPackages() const override 35 | { 36 | return true; 37 | } 38 | 39 | static const LPCWSTR ColorTag; 40 | 41 | protected: 42 | void StartDocument() override; 43 | void StartArray(_In_opt_ const std::wstring& name = empty_wstring) override; 44 | void StartObject(_In_opt_ const std::wstring& name = empty_wstring) override; 45 | void WriteProperty(_In_ const std::wstring& name, _In_ const std::wstring& value) override; 46 | void EndObject() override; 47 | void EndArray() override; 48 | std::wstring FormatDate(_In_ const FILETIME& value) override; 49 | 50 | private: 51 | static const size_t padding_size = 2; 52 | 53 | void Push(); 54 | void Pop(); 55 | 56 | void StartScope(_In_opt_ const std::wstring& name, _In_ std::wstring::const_pointer fallback); 57 | void EndScope(); 58 | 59 | std::wstring m_padding; 60 | std::stack m_scopes; 61 | }; 62 | -------------------------------------------------------------------------------- /src/vswhere.lib/XmlScope.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | void XmlScope::WriteStartImpl() 11 | { 12 | Console().Write(Padding()); 13 | Console().WriteLine(L"%ls<%ls>%ls", Console().Color(XmlFormatter::ColorTag), Name().c_str(), Console().ResetColor()); 14 | } 15 | 16 | void XmlScope::WriteEndImpl() 17 | { 18 | Console().Write(Padding()); 19 | Console().WriteLine(L"%ls%ls", Console().Color(XmlFormatter::ColorTag), Name().c_str(), Console().ResetColor()); 20 | } 21 | -------------------------------------------------------------------------------- /src/vswhere.lib/XmlScope.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class XmlScope : 9 | public Scope 10 | { 11 | public: 12 | XmlScope(_In_opt_ XmlScope* pParent, _In_ ::Console& console, _In_ const std::wstring& padding, _In_ const std::wstring& name) : 13 | Scope(pParent, console, padding, name) 14 | { 15 | } 16 | 17 | XmlScope(_In_opt_ XmlScope* pParent, _In_ ::Console& console, _In_ std::wstring& padding, _In_ std::wstring::const_pointer name) : 18 | Scope(pParent, console, padding, name) 19 | { 20 | } 21 | 22 | XmlScope(_In_ const XmlScope& obj) : 23 | Scope(obj) 24 | { 25 | } 26 | 27 | protected: 28 | void WriteStartImpl() override; 29 | void WriteEndImpl() override; 30 | }; 31 | -------------------------------------------------------------------------------- /src/vswhere.lib/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/vswhere.lib/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/vswhere/f1ecd0826a66cdfc843eec5bfd651b085d5328df/src/vswhere.lib/resource.h -------------------------------------------------------------------------------- /src/vswhere.lib/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | -------------------------------------------------------------------------------- /src/vswhere.lib/stdafx.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN 11 | 12 | // Windows headers 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // CRT header files 19 | #include 20 | 21 | // COM support header files 22 | #include 23 | #include 24 | 25 | // STL headers 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | // Project headers 42 | #include 43 | _COM_SMARTPTR_TYPEDEF(IEnumSetupInstances, __uuidof(IEnumSetupInstances)); 44 | _COM_SMARTPTR_TYPEDEF(ISetupHelper, __uuidof(ISetupHelper)); 45 | _COM_SMARTPTR_TYPEDEF(ISetupInstance, __uuidof(ISetupInstance)); 46 | _COM_SMARTPTR_TYPEDEF(ISetupInstance2, __uuidof(ISetupInstance2)); 47 | _COM_SMARTPTR_TYPEDEF(ISetupPackageReference, __uuidof(ISetupPackageReference)); 48 | _COM_SMARTPTR_TYPEDEF(ISetupPropertyStore, __uuidof(ISetupPropertyStore)); 49 | _COM_SMARTPTR_TYPEDEF(ISetupInstanceCatalog, __uuidof(ISetupInstanceCatalog)); 50 | 51 | #include "Utilities.h" 52 | #include "Exceptions.h" 53 | #include "CoInitializer.h" 54 | #include "CommandParser.h" 55 | #include "CommandArgs.h" 56 | #include "Console.h" 57 | #include "Formatter.h" 58 | #include "Glob.h" 59 | #include "ILegacyProvider.h" 60 | #include "LegacyProvider.h" 61 | #include "LegacyInstance.h" 62 | #include "InstanceSelector.h" 63 | #include "Scope.h" 64 | #include "JsonScope.h" 65 | #include "JsonFormatter.h" 66 | #include "Module.h" 67 | #include "resource.h" 68 | #include "ResourceManager.h" 69 | #include "SafeArray.h" 70 | #include "TextFormatter.h" 71 | #include "ValueFormatter.h" 72 | #include "VersionRange.h" 73 | #include "XmlScope.h" 74 | #include "XmlFormatter.h" 75 | -------------------------------------------------------------------------------- /src/vswhere.lib/targetver.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #ifdef _WIN32_WINNT 11 | #undef _WIN32_WINNT 12 | #endif 13 | 14 | #define _WIN32_WINNT 0x601 15 | 16 | #include 17 | -------------------------------------------------------------------------------- /src/vswhere.lib/vswhere.lib.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | 4 | #pragma code_page(65001) 5 | 6 | #include "resource.h" 7 | 8 | #define APSTUDIO_READONLY_SYMBOLS 9 | ///////////////////////////////////////////////////////////////////////////// 10 | // 11 | // Generated from the TEXTINCLUDE 2 resource. 12 | // 13 | #include "winres.h" 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | #undef APSTUDIO_READONLY_SYMBOLS 17 | 18 | ///////////////////////////////////////////////////////////////////////////// 19 | // English (United States) resources 20 | 21 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 22 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 23 | 24 | #ifdef APSTUDIO_INVOKED 25 | ///////////////////////////////////////////////////////////////////////////// 26 | // 27 | // TEXTINCLUDE 28 | // 29 | 30 | 1 TEXTINCLUDE 31 | BEGIN 32 | "resource.h\0" 33 | END 34 | 35 | 2 TEXTINCLUDE 36 | BEGIN 37 | "#include ""winres.h""\r\n" 38 | "\0" 39 | END 40 | 41 | 3 TEXTINCLUDE 42 | BEGIN 43 | "\r\n" 44 | "\0" 45 | END 46 | 47 | #endif // APSTUDIO_INVOKED 48 | 49 | ///////////////////////////////////////////////////////////////////////////// 50 | // 51 | // String Table 52 | // 53 | 54 | STRINGTABLE 55 | BEGIN 56 | IDS_E_ARGREQUIRED "Argument required for: %1$ls" 57 | IDS_E_ARGEXPECTED "Argument expected" 58 | IDS_E_UNKNOWNPARAM "Unknown parameter: %1$ls" 59 | IDS_E_INVALIDFORMAT "Unsupported format: %1$ls" 60 | IDS_E_INVALIDVERSION "The version ""%1$ls"" is not a valid version range" 61 | IDS_E_UNEXPECTEDDATE "The install dates of two instances should not be the same" 62 | IDS_E_UNKNOWN "Unknown error" 63 | IDS_E_LEGACY "The ""legacy"" parameter cannot be specified with either the ""products"" or ""requires"" parameter" 64 | IDS_E_ARGINCOMPAT "The ""%1$ls"" parameter cannot be specified with the ""%2$ls"" parameter" 65 | IDS_E_INVALIDPATTERN "The pattern ""%1$ls"" is invalid" 66 | IDS_E_UNSUPPORTEDARG "Unsupported argument ""%1$ls"" for parameter ""%2$ls""" 67 | IDS_E_PATHINCOMPATIBLE "The ""path"" parameter cannot be specified with other selection parameters" 68 | END 69 | 70 | STRINGTABLE 71 | BEGIN 72 | IDS_ERROR "Error" 73 | IDS_PROGRAMINFO "Visual Studio Locator version %1$ls" 74 | IDS_PROGRAMINFOEX "Visual Studio Locator version %1$ls [query version %2$ls]" 75 | IDS_COPYRIGHT "Copyright (C) Microsoft Corporation. All rights reserved." 76 | IDS_USAGE "Usage: %1$ls [options]\ 77 | \n\ 78 | \nOptions:\ 79 | \n -all Finds instances in complete, launchable, and incomplete states. By default, only instances\ 80 | \n in a complete state - no errors or reboot required - are searched.\ 81 | \n -prerelease Also searches prereleases. By default, only releases are searched.\ 82 | \n -products arg One or more product IDs to find. Defaults to Community, Professional, and Enterprise.\ 83 | \n Specify ""*"" by itself to search all product instances installed.\ 84 | \n See https://aka.ms/vs/workloads for a list of product IDs.\ 85 | \n -requires arg One or more workload or component IDs required when finding instances.\ 86 | \n All specified IDs must be installed unless -requiresAny is specified.\ 87 | \n You can specify wildcards including ""?"" to match any one character,\ 88 | \n or ""*"" to match zero or more of any characters.\ 89 | \n See https://aka.ms/vs/workloads for a list of workload and component IDs.\ 90 | \n -requiresAny Find instances with any one or more workload or components IDs passed to -requires.\ 91 | \n -version arg A version range for instances to find. Example: [15.0,16.0) will find versions 15.*.\ 92 | \n See https://aka.ms/vswhere/versions for more information about versions.\ 93 | \n -latest Return only the newest version and last installed.\ 94 | \n -sort Sorts the instances from newest version and last installed to oldest.\ 95 | \n When used with ""find"", first instances are sorted then files are sorted lexicographically.\ 96 | \n -legacy Also searches Visual Studio 2015 and older products. Information is limited.\ 97 | \n This option cannot be used with either -products or -requires.\ 98 | \n -path Gets the instance for the current path. Not compatible with any other selection option.\ 99 | \n -format arg Return information about instances found in a format described below.\ 100 | \n -property arg The name of a property to return. Defaults to ""value"" format.\ 101 | \n Use delimiters ""."", ""/"", or ""_"" to separate object and property names.\ 102 | \n Example: ""properties.nickname"" will return the ""nickname"" property under ""properties"".\ 103 | \n -include arg One or more extra properties to include, as described below.\ 104 | \n -find arg Returns matching file paths under the installation path. Defaults to ""value"" format.\ 105 | \n The following patterns are supported:\ 106 | \n ? Matches any one character except ""\\"".\ 107 | \n * Matches zero or more characters except ""\\"".\ 108 | \n ** Searches the current directory and subdirectories for the remaining search pattern.\ 109 | \n -nocolor Do not print instances formatted with colors. By default, colors suitable for dark-themed\ 110 | \n terminals (common) are output when printing to a terminal but not to a pipe.\ 111 | \n -nologo Do not show logo information. Some formats noted below will not show a logo anyway.\ 112 | \n -utf8 Use UTF-8 encoding (recommended for JSON).\ 113 | \n -?, -h, -help Display this help message.\ 114 | \n\ 115 | \n\ 116 | \nExtra properties:\ 117 | \n packages Return an array of packages installed in this instance.\ 118 | \n Supported only by the ""json"" and ""xml"" formats.\ 119 | \n\ 120 | \nFormats:" 121 | IDS_FORMAT_TEXT " %1$-13ls Colon-delimited properties in separate blocks for each instance (default)." 122 | IDS_FORMAT_JSON " %1$-13ls An array of JSON objects for each instance (no logo)." 123 | IDS_FORMAT_VALUE " %1$-13ls A single property specified by the -property parameter (no logo, no color)." 124 | IDS_FORMAT_XML " %1$-13ls An XML data set containing instances (no logo)." 125 | END 126 | 127 | #endif // English (United States) resources 128 | ///////////////////////////////////////////////////////////////////////////// 129 | 130 | #ifndef APSTUDIO_INVOKED 131 | ///////////////////////////////////////////////////////////////////////////// 132 | // 133 | // Generated from the TEXTINCLUDE 3 resource. 134 | // 135 | 136 | 137 | ///////////////////////////////////////////////////////////////////////////// 138 | #endif // not APSTUDIO_INVOKED 139 | -------------------------------------------------------------------------------- /src/vswhere.lib/vswhere.lib.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | Header Files 50 | 51 | 52 | Header Files 53 | 54 | 55 | Header Files 56 | 57 | 58 | Header Files 59 | 60 | 61 | Header Files 62 | 63 | 64 | Header Files 65 | 66 | 67 | Header Files 68 | 69 | 70 | Header Files 71 | 72 | 73 | Header Files 74 | 75 | 76 | Header Files 77 | 78 | 79 | Header Files 80 | 81 | 82 | Header Files 83 | 84 | 85 | Header Files 86 | 87 | 88 | Header Files 89 | 90 | 91 | Header Files 92 | 93 | 94 | Header Files 95 | 96 | 97 | 98 | 99 | Source Files 100 | 101 | 102 | Source Files 103 | 104 | 105 | Source Files 106 | 107 | 108 | Source Files 109 | 110 | 111 | Source Files 112 | 113 | 114 | Source Files 115 | 116 | 117 | Source Files 118 | 119 | 120 | Source Files 121 | 122 | 123 | Source Files 124 | 125 | 126 | Source Files 127 | 128 | 129 | Source Files 130 | 131 | 132 | Source Files 133 | 134 | 135 | Source Files 136 | 137 | 138 | Source Files 139 | 140 | 141 | Source Files 142 | 143 | 144 | Source Files 145 | 146 | 147 | Source Files 148 | 149 | 150 | Source Files 151 | 152 | 153 | Source Files 154 | 155 | 156 | Source Files 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | Resource Files 165 | 166 | 167 | -------------------------------------------------------------------------------- /src/vswhere/Program.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | void GetEnumerator(_In_ const CommandArgs& args, _In_ ISetupConfigurationPtr& query, _In_ IEnumSetupInstancesPtr& e); 11 | wstring GetFullPath(_In_ const wstring& path); 12 | void WriteLogo(_In_ const CommandArgs& args, _In_ Console& console, _In_ Module& module); 13 | 14 | int wmain(_In_ int argc, _In_ LPCWSTR argv[]) 15 | { 16 | CommandArgs args; 17 | Console console(args); 18 | Module queryModule; 19 | 20 | try 21 | { 22 | CoInitializer init; 23 | 24 | // Create the query object early to print version in logo. 25 | ISetupConfigurationPtr query; 26 | auto hr = query.CreateInstance(__uuidof(SetupConfiguration)); 27 | if (FAILED(hr)) 28 | { 29 | if (REGDB_E_CLASSNOTREG != hr) 30 | { 31 | throw win32_error(hr); 32 | } 33 | } 34 | 35 | // Try to get information about the query module for later. 36 | queryModule.FromIUnknown(static_cast(query)); 37 | 38 | args.Parse(argc, argv); 39 | console.Initialize(); 40 | 41 | if (args.get_Help()) 42 | { 43 | WriteLogo(args, console, queryModule); 44 | args.Usage(console); 45 | 46 | return ERROR_SUCCESS; 47 | } 48 | 49 | // Attempt to get the ISetupHelper. 50 | ISetupHelperPtr helper; 51 | if (query) 52 | { 53 | query->QueryInterface(&helper); 54 | } 55 | // Fall back to a copy of the current implementation. 56 | else 57 | { 58 | helper.Attach(new VersionRange); 59 | } 60 | 61 | vector instances; 62 | if (args.get_Path().empty()) 63 | { 64 | IEnumSetupInstancesPtr e; 65 | GetEnumerator(args, query, e); 66 | 67 | InstanceSelector selector(args, helper); 68 | instances = std::move(selector.Select(e)); 69 | } 70 | else 71 | { 72 | auto path = GetFullPath(args.get_Path()); 73 | 74 | ISetupInstancePtr instance; 75 | hr = query->GetInstanceForPath(path.c_str(), &instance); 76 | if (SUCCEEDED(hr)) 77 | { 78 | instances.push_back(instance); 79 | } 80 | } 81 | 82 | // Create the formatter and optionally show the logo. 83 | auto formatter = Formatter::Create(args.get_Format(), args, console); 84 | if (formatter->ShowLogo()) 85 | { 86 | WriteLogo(args, console, queryModule); 87 | } 88 | 89 | if (args.get_Find().length()) 90 | { 91 | formatter->WriteFiles(instances); 92 | } 93 | else 94 | { 95 | formatter->Write(instances); 96 | } 97 | 98 | return ERROR_SUCCESS; 99 | } 100 | catch (const system_error& ex) 101 | { 102 | // Make sure the console is initialized even for parsing errors. 103 | console.Initialize(); 104 | 105 | const auto code = ex.code().value(); 106 | if (ERROR_INVALID_PARAMETER == code) 107 | { 108 | WriteLogo(args, console, queryModule); 109 | } 110 | 111 | console.Write(L"%ls 0x%x: ", ResourceManager::GetString(IDS_ERROR).c_str(), code); 112 | 113 | const auto* err = dynamic_cast(&ex); 114 | if (err) 115 | { 116 | console.WriteLine(L"%ls", err->wwhat()); 117 | } 118 | else 119 | { 120 | console.WriteLine(L"%hs", ex.what()); 121 | } 122 | 123 | return ex.code().value(); 124 | } 125 | catch (const exception& ex) 126 | { 127 | console.WriteLine(L"%ls: %hs", ResourceManager::GetString(IDS_ERROR).c_str(), ex.what()); 128 | } 129 | catch (...) 130 | { 131 | console.WriteLine(L"%ls: %ls", ResourceManager::GetString(IDS_ERROR).c_str(), ResourceManager::GetString(IDS_E_UNKNOWN).c_str()); 132 | } 133 | 134 | return E_FAIL; 135 | } 136 | 137 | void GetEnumerator(_In_ const CommandArgs& args, _In_ ISetupConfigurationPtr& query, _In_ IEnumSetupInstancesPtr& e) 138 | { 139 | if (!query) 140 | { 141 | return; 142 | } 143 | 144 | // If all instances are requested, try to get the proper enumerator; otherwise, fall back to original enumerator. 145 | if (args.get_All()) 146 | { 147 | ISetupConfiguration2Ptr query2; 148 | 149 | auto hr = query->QueryInterface(&query2); 150 | if (SUCCEEDED(hr)) 151 | { 152 | hr = query2->EnumAllInstances(&e); 153 | if (FAILED(hr)) 154 | { 155 | throw win32_error(hr); 156 | } 157 | } 158 | } 159 | 160 | if (!e) 161 | { 162 | auto hr = query->EnumInstances(&e); 163 | if (FAILED(hr)) 164 | { 165 | throw win32_error(hr); 166 | } 167 | } 168 | } 169 | 170 | wstring GetFullPath(_In_ const wstring& path) 171 | { 172 | DWORD ret = 0; 173 | wstring fullPath; 174 | 175 | for (;;) 176 | { 177 | ret = ::GetFullPathNameW(path.c_str(), fullPath.capacity(), const_cast(fullPath.c_str()), NULL); 178 | 179 | if (ret == 0) 180 | { 181 | throw win32_error(); 182 | } 183 | // If buffer too small, return value contains required character count including terminating null. 184 | else if (ret >= fullPath.capacity()) 185 | { 186 | fullPath.resize(ret, L'\0'); 187 | } 188 | else 189 | { 190 | break; 191 | } 192 | } 193 | 194 | return fullPath; 195 | } 196 | 197 | void WriteLogo(_In_ const CommandArgs& args, _In_ Console& console, _In_ Module& module) 198 | { 199 | if (args.get_Logo()) 200 | { 201 | const auto& version = module.get_FileVersion(); 202 | const auto nID = version.empty() ? IDS_PROGRAMINFO : IDS_PROGRAMINFOEX; 203 | 204 | console.WriteLine(ResourceManager::FormatString(nID, NBGV_INFORMATIONAL_VERSION, version.c_str())); 205 | console.WriteLine(ResourceManager::GetString(IDS_COPYRIGHT)); 206 | console.WriteLine(); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/vswhere/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/vswhere/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by vswhere.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /src/vswhere/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | -------------------------------------------------------------------------------- /src/vswhere/stdafx.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN 11 | 12 | // Windows headers 13 | #include 14 | 15 | // STL headers 16 | #include 17 | 18 | // Project headers 19 | #include 20 | #include 21 | 22 | _COM_SMARTPTR_TYPEDEF(ISetupConfiguration, __uuidof(ISetupConfiguration)); 23 | _COM_SMARTPTR_TYPEDEF(ISetupConfiguration2, __uuidof(ISetupConfiguration2)); 24 | -------------------------------------------------------------------------------- /src/vswhere/targetver.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #ifdef _WIN32_WINNT 11 | #undef _WIN32_WINNT 12 | #endif 13 | 14 | #define _WIN32_WINNT 0x601 15 | 16 | #include 17 | -------------------------------------------------------------------------------- /src/vswhere/vswhere.exe.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | true 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/vswhere/vswhere.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;manifest 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | 37 | 38 | 39 | Resource Files 40 | 41 | 42 | -------------------------------------------------------------------------------- /test/vswhere.test/ExceptionsTests.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 10 | 11 | TEST_CLASS(ExceptionsTests) 12 | { 13 | public: 14 | TEST_METHOD_INITIALIZE(Initialize) 15 | { 16 | ::SetThreadLocale(1033); 17 | } 18 | 19 | TEST_METHOD(win32_error_last_error) 20 | { 21 | ::SetLastError(ERROR_NOT_FOUND); 22 | 23 | win32_error sut; 24 | Assert::AreEqual(ERROR_NOT_FOUND, sut.code().value()); 25 | } 26 | 27 | TEST_METHOD(win32_error_explicit) 28 | { 29 | win32_error sut(ERROR_NOT_FOUND); 30 | Assert::AreEqual(ERROR_NOT_FOUND, sut.code().value()); 31 | Assert::AreEqual("Element not found.", sut.what()); 32 | Assert::AreEqual(L"Element not found.\r\n", sut.wwhat()); 33 | } 34 | 35 | TEST_METHOD(win32_error_custom_message) 36 | { 37 | win32_error sut(ERROR_NOT_FOUND, L"property not found"); 38 | Assert::AreEqual(ERROR_NOT_FOUND, sut.code().value()); 39 | Assert::AreEqual("property not found: Element not found.", sut.what()); 40 | Assert::AreEqual(L"property not found", sut.wwhat()); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /test/vswhere.test/ModuleTests.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 10 | 11 | // Wrap something cheap and easy to create. 12 | _COM_SMARTPTR_TYPEDEF(IBindCtx, __uuidof(IBindCtx)); 13 | 14 | TEST_CLASS(ModuleTests) 15 | { 16 | public: 17 | TEST_METHOD(FromIUnknown_NULL) 18 | { 19 | Module sut; 20 | sut.FromIUnknown(NULL); 21 | 22 | Assert::AreEqual(0, sut.get_Path().length()); 23 | Assert::AreEqual(0, sut.get_FileVersion().length()); 24 | } 25 | 26 | TEST_METHOD(FromIUnknown_Not_NULL) 27 | { 28 | IBindCtxPtr binder; 29 | Assert::AreEqual(S_OK, ::CreateBindCtx(0, &binder)); 30 | 31 | Module sut; 32 | sut.FromIUnknown(binder); 33 | 34 | Assert::AreNotEqual(0, sut.get_Path().length()); 35 | Assert::AreNotEqual(0, sut.get_FileVersion().length()); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /test/vswhere.test/TestConsole.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | void TestConsole::Write(_In_ LPCWSTR wzFormat, va_list args) 11 | { 12 | // include space for trailing null character 13 | size_t ch = ::_vscwprintf_p(wzFormat, args) + 1; 14 | size_t pos = m_output.size(); 15 | m_output.resize(m_output.size() + ch); 16 | 17 | size_t rem = m_output.size() - pos; 18 | auto end = const_cast(m_output.c_str()) + pos; 19 | ::_vswprintf_p(end, rem, wzFormat, args); 20 | 21 | // remove trailing null character 22 | m_output.resize(m_output.size() - 1); 23 | } 24 | -------------------------------------------------------------------------------- /test/vswhere.test/TestConsole.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class TestConsole : 9 | public Console 10 | { 11 | public: 12 | TestConsole(_In_ const CommandArgs& args) : 13 | Console(args), 14 | m_color(false) 15 | { 16 | } 17 | 18 | TestConsole(_In_ const TestConsole& obj) : 19 | Console(obj), 20 | m_output(obj.m_output), 21 | m_color(obj.m_color) 22 | { 23 | } 24 | 25 | void Initialize() noexcept override 26 | { 27 | m_fInitialized = true; 28 | } 29 | 30 | operator const wchar_t*() const 31 | { 32 | return m_output.c_str(); 33 | } 34 | 35 | void SetColorSupported(bool enable) noexcept 36 | { 37 | m_color = enable; 38 | } 39 | 40 | bool IsColorSupported() const override 41 | { 42 | return m_color; 43 | } 44 | 45 | protected: 46 | void Write(_In_ LPCWSTR wzFormat, va_list args) override; 47 | 48 | private: 49 | std::wstring m_output; 50 | bool m_color; 51 | }; 52 | -------------------------------------------------------------------------------- /test/vswhere.test/TestEnumInstances.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class TestEnumInstances : 9 | public IEnumSetupInstances 10 | { 11 | public: 12 | typedef ISetupInstance* ElementType; 13 | 14 | TestEnumInstances(_In_ std::initializer_list list) : 15 | m_instances(list.begin(), list.end()), 16 | m_i(0), 17 | m_ulRef(1) 18 | { 19 | } 20 | 21 | ~TestEnumInstances() 22 | { 23 | } 24 | 25 | // IUnknown 26 | STDMETHODIMP QueryInterface( 27 | _In_ REFIID riid, 28 | _Outptr_ LPVOID *ppvObject) 29 | { 30 | if (!ppvObject) 31 | { 32 | return E_POINTER; 33 | } 34 | 35 | HRESULT hr = S_OK; 36 | if (riid == __uuidof(IEnumSetupInstances)) 37 | { 38 | AddRef(); 39 | *ppvObject = static_cast(this); 40 | } 41 | else if (riid == __uuidof(IUnknown)) 42 | { 43 | AddRef(); 44 | *ppvObject = static_cast(this); 45 | } 46 | else 47 | { 48 | hr = E_NOINTERFACE; 49 | } 50 | 51 | return hr; 52 | } 53 | 54 | STDMETHODIMP_(ULONG) AddRef(void) 55 | { 56 | return ::InterlockedIncrement(&m_ulRef); 57 | } 58 | 59 | STDMETHODIMP_(ULONG) Release(void) 60 | { 61 | Microsoft::VisualStudio::CppUnitTestFramework::Assert::AreNotEqual(0UL, m_ulRef); 62 | return ::InterlockedDecrement(&m_ulRef); 63 | } 64 | 65 | // IEnumSetupInstances 66 | STDMETHODIMP Next( 67 | _In_ ULONG celt, 68 | _Out_writes_to_(celt, *pceltFetched) ISetupInstance** rgelt, 69 | _Out_ _Deref_out_range_(0, celt) ULONG* pceltFetched 70 | ) 71 | { 72 | *pceltFetched = 0; 73 | auto remaining = m_instances.size() - m_i; 74 | if (0 == remaining) 75 | { 76 | return S_FALSE; 77 | } 78 | 79 | for (unsigned long i = 0; i < celt && m_i < m_instances.size(); ++i, ++m_i, ++*pceltFetched) 80 | { 81 | rgelt[i] = m_instances[m_i]; 82 | } 83 | 84 | return S_OK; 85 | } 86 | 87 | STDMETHODIMP Skip( 88 | _In_ ULONG celt 89 | ) 90 | { 91 | return E_NOTIMPL; 92 | } 93 | 94 | STDMETHODIMP Reset(void) 95 | { 96 | return E_NOTIMPL; 97 | } 98 | 99 | STDMETHODIMP Clone( 100 | _Deref_out_opt_ IEnumSetupInstances** ppenum 101 | ) 102 | { 103 | return E_NOTIMPL; 104 | } 105 | 106 | 107 | private: 108 | const std::vector m_instances; 109 | ULONG m_i; 110 | ULONG m_ulRef; 111 | }; 112 | -------------------------------------------------------------------------------- /test/vswhere.test/TestHelper.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | const ci_equal TestHelper::s_comparer; 9 | -------------------------------------------------------------------------------- /test/vswhere.test/TestHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | #define MAKEVERSION(major, minor, build, revision) ULONGLONG(major) << 24 | ULONGLONG(minor) << 16 | ULONGLONG(build) << 8 | ULONGLONG(revision) 9 | 10 | class TestHelper : 11 | public ISetupHelper 12 | { 13 | public: 14 | TestHelper(_In_ ULONGLONG ullMinimumVersion, _In_ ULONGLONG ullMaximumVersion) : 15 | m_ullMinimumVersion(ullMinimumVersion), 16 | m_ullMaximumVersion(ullMaximumVersion), 17 | m_ulRef(1) 18 | { 19 | } 20 | 21 | ~TestHelper() 22 | { 23 | } 24 | 25 | // IUnknown 26 | STDMETHODIMP QueryInterface( 27 | _In_ REFIID riid, 28 | _Outptr_ LPVOID *ppvObject) 29 | { 30 | if (!ppvObject) 31 | { 32 | return E_POINTER; 33 | } 34 | 35 | HRESULT hr = S_OK; 36 | if (riid == __uuidof(ISetupHelper)) 37 | { 38 | AddRef(); 39 | *ppvObject = static_cast(this); 40 | } 41 | else if (riid == __uuidof(IUnknown)) 42 | { 43 | AddRef(); 44 | *ppvObject = static_cast(this); 45 | } 46 | else 47 | { 48 | hr = E_NOINTERFACE; 49 | } 50 | 51 | return hr; 52 | } 53 | 54 | STDMETHODIMP_(ULONG) AddRef(void) 55 | { 56 | return ::InterlockedIncrement(&m_ulRef); 57 | } 58 | 59 | STDMETHODIMP_(ULONG) Release(void) 60 | { 61 | Microsoft::VisualStudio::CppUnitTestFramework::Assert::AreNotEqual(0UL, m_ulRef); 62 | return ::InterlockedDecrement(&m_ulRef); 63 | } 64 | 65 | // ISetupHelper 66 | STDMETHODIMP ParseVersion( 67 | _In_ LPCOLESTR pwszVersion, 68 | _Out_ PULONGLONG pullVersion 69 | ) 70 | { 71 | static ci_equal equal; 72 | 73 | if (equal(pwszVersion, L"1.0")) 74 | { 75 | *pullVersion = MAKEVERSION(1, 0, 0, 0); 76 | return S_OK; 77 | } 78 | 79 | if (equal(pwszVersion, L"2.0")) 80 | { 81 | *pullVersion = MAKEVERSION(2, 0, 0, 0); 82 | return S_OK; 83 | } 84 | 85 | if (equal(pwszVersion, L"10.0")) 86 | { 87 | *pullVersion = MAKEVERSION(10, 0, 0, 0); 88 | return S_OK; 89 | } 90 | 91 | if (equal(pwszVersion, L"14.0")) 92 | { 93 | *pullVersion = MAKEVERSION(14, 0, 0, 0); 94 | return S_OK; 95 | } 96 | 97 | return E_NOTIMPL; 98 | } 99 | 100 | STDMETHODIMP ParseVersionRange( 101 | _In_ LPCOLESTR pwszVersionRange, 102 | _Out_ PULONGLONG pullMinVersion, 103 | _Out_ PULONGLONG pullMaxVersion 104 | ) 105 | { 106 | if (s_comparer(pwszVersionRange, L"invalid")) 107 | { 108 | return E_INVALIDARG; 109 | } 110 | 111 | *pullMinVersion = m_ullMinimumVersion; 112 | *pullMaxVersion = m_ullMaximumVersion; 113 | 114 | return S_OK; 115 | } 116 | 117 | private: 118 | static const ci_equal s_comparer; 119 | 120 | const ULONGLONG m_ullMinimumVersion; 121 | const ULONGLONG m_ullMaximumVersion; 122 | ULONG m_ulRef; 123 | }; 124 | -------------------------------------------------------------------------------- /test/vswhere.test/TestLegacyProvider.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | 10 | bool TestLegacyProvider::HasLegacyInstances() const 11 | { 12 | return !m_instances.empty(); 13 | } 14 | 15 | bool TestLegacyProvider::TryGetLegacyInstance(_In_ LPCWSTR wzVersion, _Out_ ISetupInstance** ppInstance) const 16 | { 17 | _ASSERT(ppInstance); 18 | 19 | *ppInstance = NULL; 20 | 21 | auto it = m_instances.find(wzVersion); 22 | if (it != m_instances.end()) 23 | { 24 | *ppInstance = new LegacyInstance(it->first.c_str(), it->second.c_str()); 25 | return true; 26 | } 27 | 28 | return false; 29 | } 30 | -------------------------------------------------------------------------------- /test/vswhere.test/TestLegacyProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class TestLegacyProvider : 9 | public ILegacyProvider 10 | { 11 | public: 12 | typedef std::unordered_map, ci_equal> MapType; 13 | 14 | TestLegacyProvider(_In_ std::initializer_list list) : 15 | m_instances(list.begin(), list.end()) 16 | { 17 | } 18 | 19 | bool HasLegacyInstances() const override; 20 | bool TryGetLegacyInstance(_In_ LPCWSTR wzVersion, _Out_ ISetupInstance** ppInstance) const override; 21 | 22 | private: 23 | MapType m_instances; 24 | }; 25 | -------------------------------------------------------------------------------- /test/vswhere.test/TestModule.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 10 | 11 | wstring __cdecl format(_In_ LPCWSTR fmt, ...) 12 | { 13 | va_list args; 14 | va_start(args, fmt); 15 | 16 | auto ch = _vscwprintf_p(fmt, args); 17 | if (0 > ch) 18 | { 19 | throw win32_error(ERROR_INVALID_PARAMETER); 20 | } 21 | 22 | wstring wsz; 23 | wsz.resize(++ch); 24 | 25 | if (0 > _vswprintf_p(&wsz[0], ch, fmt, args)) 26 | { 27 | throw win32_error(ERROR_INVALID_PARAMETER); 28 | } 29 | 30 | va_end(args); 31 | 32 | // trim the terminating null 33 | wsz.resize(ch - 1); 34 | 35 | return wsz; 36 | } 37 | 38 | TEST_MODULE_INITIALIZE(ModuleInitialize) 39 | { 40 | #pragma warning(suppress: 6387) // Ignore potential failure when getting module handle 41 | ResourceManager::SetInstance(::GetModuleHandleW(L"vswhere.test.dll")); 42 | } 43 | -------------------------------------------------------------------------------- /test/vswhere.test/TestModule.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | std::wstring __cdecl format(_In_ LPCWSTR fmt, ...); 9 | -------------------------------------------------------------------------------- /test/vswhere.test/TestPackageReference.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | 9 | class TestPackageReference : 10 | public ISetupPackageReference 11 | { 12 | public: 13 | typedef std::unordered_map, ci_equal> MapType; 14 | 15 | TestPackageReference(_In_ std::initializer_list list) : 16 | m_properties(list.begin(), list.end()), 17 | m_ulRef(1) 18 | { 19 | } 20 | 21 | ~TestPackageReference() 22 | { 23 | } 24 | 25 | // IUnknown 26 | STDMETHODIMP QueryInterface( 27 | _In_ REFIID riid, 28 | _Outptr_ LPVOID *ppvObject) 29 | { 30 | if (!ppvObject) 31 | { 32 | return E_POINTER; 33 | } 34 | 35 | HRESULT hr = S_OK; 36 | if (riid == __uuidof(ISetupPackageReference)) 37 | { 38 | AddRef(); 39 | *ppvObject = static_cast(this); 40 | } 41 | else if (riid == __uuidof(IUnknown)) 42 | { 43 | AddRef(); 44 | *ppvObject = static_cast(this); 45 | } 46 | else 47 | { 48 | hr = E_NOINTERFACE; 49 | } 50 | 51 | return hr; 52 | } 53 | 54 | STDMETHODIMP_(ULONG) AddRef(void) 55 | { 56 | return ::InterlockedIncrement(&m_ulRef); 57 | } 58 | 59 | STDMETHODIMP_(ULONG) Release(void) 60 | { 61 | Microsoft::VisualStudio::CppUnitTestFramework::Assert::AreNotEqual(0UL, m_ulRef); 62 | return ::InterlockedDecrement(&m_ulRef); 63 | } 64 | 65 | // ISetupPackageReference 66 | STDMETHODIMP GetId( 67 | _Out_ BSTR* pbstrId 68 | ) 69 | { 70 | return TryGetBSTR(L"Id", pbstrId); 71 | } 72 | 73 | STDMETHODIMP GetVersion( 74 | _Out_ BSTR* pbstrVersion 75 | ) 76 | { 77 | return TryGetBSTR(L"Version", pbstrVersion); 78 | } 79 | 80 | STDMETHODIMP GetChip( 81 | _Out_ BSTR* pbstrChip 82 | ) 83 | { 84 | return TryGetBSTR(L"Chip", pbstrChip); 85 | } 86 | 87 | STDMETHODIMP GetLanguage( 88 | _Out_ BSTR* pbstrLanguage 89 | ) 90 | { 91 | return TryGetBSTR(L"Language", pbstrLanguage); 92 | } 93 | 94 | STDMETHODIMP GetBranch( 95 | _Out_ BSTR* pbstrBranch 96 | ) 97 | { 98 | return TryGetBSTR(L"Branch", pbstrBranch); 99 | } 100 | 101 | STDMETHODIMP GetType( 102 | _Out_ BSTR* pbstrType 103 | ) 104 | { 105 | return TryGetBSTR(L"Type", pbstrType); 106 | } 107 | 108 | STDMETHODIMP GetUniqueId( 109 | _Out_ BSTR* pbstrUniqueId 110 | ) 111 | { 112 | return E_NOTIMPL; 113 | } 114 | 115 | STDMETHODIMP GetIsExtension( 116 | _Out_ VARIANT_BOOL* pfIsExtension 117 | ) 118 | { 119 | return E_NOTIMPL; 120 | } 121 | 122 | private: 123 | STDMETHODIMP TryGet(_In_ std::wstring name, _In_ std::wstring& value) 124 | { 125 | auto it = m_properties.find(name); 126 | if (it != m_properties.end()) 127 | { 128 | value = it->second; 129 | return S_OK; 130 | } 131 | 132 | return E_NOTFOUND; 133 | } 134 | 135 | STDMETHODIMP TryGetBSTR(_In_ LPCWSTR wszName, _Out_ BSTR* pbstrValue) 136 | { 137 | if (!pbstrValue) 138 | { 139 | return E_POINTER; 140 | } 141 | 142 | std::wstring value; 143 | 144 | auto hr = TryGet(wszName, value); 145 | if (SUCCEEDED(hr)) 146 | { 147 | *pbstrValue = ::SysAllocString(value.c_str()); 148 | } 149 | 150 | return hr; 151 | } 152 | 153 | MapType m_properties; 154 | ULONG m_ulRef; 155 | }; 156 | -------------------------------------------------------------------------------- /test/vswhere.test/TestPropertyStore.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | class TestPropertyStore : 9 | public ISetupPropertyStore 10 | { 11 | public: 12 | typedef std::unordered_map, std::hash, ci_equal> MapType; 13 | 14 | TestPropertyStore() : 15 | m_ulRef(1) 16 | { 17 | } 18 | 19 | TestPropertyStore(_In_ std::initializer_list list) : 20 | m_properties(list.begin(), list.end()), 21 | m_ulRef(1) 22 | { 23 | } 24 | 25 | TestPropertyStore(_In_ const MapType& properties) : 26 | m_properties(properties.begin(), properties.end()), 27 | m_ulRef(1) 28 | { 29 | } 30 | 31 | // IUnknown 32 | STDMETHODIMP QueryInterface( 33 | _In_ REFIID riid, 34 | _Outptr_ LPVOID *ppvObject) 35 | { 36 | if (!ppvObject) 37 | { 38 | return E_POINTER; 39 | } 40 | 41 | HRESULT hr = S_OK; 42 | if (riid == __uuidof(ISetupPropertyStore)) 43 | { 44 | AddRef(); 45 | *ppvObject = static_cast(this); 46 | } 47 | else if (riid == __uuidof(IUnknown)) 48 | { 49 | AddRef(); 50 | *ppvObject = static_cast(this); 51 | } 52 | else 53 | { 54 | hr = E_NOINTERFACE; 55 | } 56 | 57 | return hr; 58 | } 59 | 60 | STDMETHODIMP_(ULONG) AddRef(void) 61 | { 62 | return ::InterlockedIncrement(&m_ulRef); 63 | } 64 | 65 | STDMETHODIMP_(ULONG) Release(void) 66 | { 67 | Microsoft::VisualStudio::CppUnitTestFramework::Assert::AreNotEqual(0UL, m_ulRef); 68 | return ::InterlockedDecrement(&m_ulRef); 69 | } 70 | 71 | // ISetupPropertyStore 72 | STDMETHODIMP GetNames( 73 | _Out_ LPSAFEARRAY* ppsaNames 74 | ) 75 | { 76 | if (!ppsaNames) 77 | { 78 | return E_POINTER; 79 | } 80 | 81 | if (!m_properties.empty()) 82 | { 83 | auto psa = ::SafeArrayCreateVector(VT_BSTR, 0, m_properties.size()); 84 | auto hr = ::SafeArrayLock(psa); 85 | if (FAILED(hr)) 86 | { 87 | throw win32_error(hr, "failed to lock packages array"); 88 | } 89 | 90 | auto rgData = (BSTR*)psa->pvData; 91 | for (const auto& property : m_properties) 92 | { 93 | *rgData = ::SysAllocString(property.first.c_str()); 94 | if (!*rgData) 95 | { 96 | throw win32_error(E_OUTOFMEMORY, "failed to allocate memory"); 97 | } 98 | 99 | ++rgData; 100 | } 101 | 102 | hr = ::SafeArrayUnlock(psa); 103 | if (FAILED(hr)) 104 | { 105 | throw win32_error(hr, "failed to unlock packages array"); 106 | } 107 | 108 | *ppsaNames = psa; 109 | return S_OK; 110 | } 111 | 112 | return E_NOTFOUND; 113 | } 114 | 115 | STDMETHODIMP GetValue( 116 | _In_ LPCOLESTR pwszName, 117 | _Out_ LPVARIANT pvtValue 118 | ) 119 | { 120 | return TryGetVARIANT(pwszName, pvtValue); 121 | } 122 | 123 | // Other 124 | bool empty() const 125 | { 126 | return m_properties.empty(); 127 | } 128 | 129 | private: 130 | STDMETHODIMP TryGet(_In_ std::wstring name, _Inout_ VARENUM& vt, _Inout_ std::wstring& value) 131 | { 132 | auto it = m_properties.find(name); 133 | if (it != m_properties.end()) 134 | { 135 | tie(vt, value) = it->second; 136 | return S_OK; 137 | } 138 | 139 | return E_NOTFOUND; 140 | } 141 | 142 | STDMETHODIMP TryGetVARIANT(_In_ LPCWSTR wszName, _Out_ VARIANT* pvtValue) 143 | { 144 | if (!pvtValue) 145 | { 146 | return E_POINTER; 147 | } 148 | 149 | static ci_equal equals; 150 | VARENUM vt = VT_EMPTY; 151 | std::wstring value; 152 | 153 | auto hr = TryGet(wszName, vt, value); 154 | if (SUCCEEDED(hr)) 155 | { 156 | switch (vt) 157 | { 158 | case VT_BSTR: 159 | { 160 | pvtValue->bstrVal = ::SysAllocString(value.c_str()); 161 | if (!pvtValue->bstrVal) 162 | { 163 | return E_OUTOFMEMORY; 164 | } 165 | else 166 | { 167 | pvtValue->vt = VT_BSTR; 168 | return S_OK; 169 | } 170 | } 171 | 172 | case VT_BOOL: 173 | { 174 | auto f = L"1" == value || equals(L"true", value); 175 | 176 | pvtValue->boolVal = f ? VARIANT_TRUE : VARIANT_FALSE; 177 | pvtValue->vt = VT_BOOL; 178 | return S_OK; 179 | } 180 | 181 | case VT_I1: 182 | case VT_I2: 183 | case VT_I4: 184 | { 185 | auto loc = _wcreate_locale(LC_CTYPE, L"C"); 186 | pvtValue->intVal = ::_wtoi_l(value.c_str(), loc); 187 | pvtValue->vt = vt; 188 | _free_locale(loc); 189 | 190 | return S_OK; 191 | } 192 | 193 | default: 194 | return E_NOTSUPPORTED; 195 | } 196 | } 197 | 198 | return hr; 199 | } 200 | 201 | MapType m_properties; 202 | ULONG m_ulRef; 203 | }; 204 | -------------------------------------------------------------------------------- /test/vswhere.test/UtilitiesTests.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | 8 | using namespace std; 9 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 10 | 11 | TEST_CLASS(UtilitiesTests) 12 | { 13 | public: 14 | TEST_METHOD(ci_equal_theory) 15 | { 16 | vector> data = 17 | { 18 | { L"a", L"a", true }, 19 | { L"a", L"A", true }, 20 | { L"a", L"b", false }, 21 | { L"b", L"a", false }, 22 | { L"foo", L"foobar", false }, 23 | { L"foobar", L"foo", false }, 24 | }; 25 | 26 | ci_equal sut; 27 | for (const auto& item : data) 28 | { 29 | wstring lhs, rhs; 30 | bool expected; 31 | 32 | tie(lhs, rhs, expected) = item; 33 | auto actual = sut(lhs, rhs); 34 | 35 | Assert::AreEqual(expected, actual, format(L"ci_equal(%ls, %ls)", lhs.c_str(), rhs.c_str()).c_str()); 36 | } 37 | } 38 | 39 | TEST_METHOD(ci_less_theory) 40 | { 41 | vector> data = 42 | { 43 | { L"a", L"a", false }, 44 | { L"a", L"A", false }, 45 | { L"a", L"b", true }, 46 | { L"b", L"a", false }, 47 | { L"foo", L"foobar", true }, 48 | { L"foobar", L"foo", false }, 49 | }; 50 | 51 | ci_less sut; 52 | for (const auto& item : data) 53 | { 54 | wstring lhs, rhs; 55 | bool expected; 56 | 57 | tie(lhs, rhs, expected) = item; 58 | auto actual = sut(lhs, rhs); 59 | 60 | Assert::AreEqual(expected, actual, format(L"ci_less(%ls, %ls)", lhs.c_str(), rhs.c_str()).c_str()); 61 | } 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /test/vswhere.test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/vswhere.test/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by vswhere.test.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /test/vswhere.test/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #include "stdafx.h" 7 | -------------------------------------------------------------------------------- /test/vswhere.test/stdafx.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | #include "cppunittest.h" 10 | 11 | #define WIN32_LEAN_AND_MEAN 12 | 13 | // Windows headers 14 | #include 15 | #include 16 | 17 | // STL headers 18 | #include 19 | #include 20 | 21 | // Project headers 22 | #include 23 | #include "TestModule.h" 24 | #include "TestConsole.h" 25 | #include "TestEnumInstances.h" 26 | #include "TestHelper.h" 27 | #include "TestPropertyStore.h" 28 | #include "TestInstance.h" 29 | #include "TestLegacyProvider.h" 30 | #include "TestPackageReference.h" 31 | -------------------------------------------------------------------------------- /test/vswhere.test/targetver.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. See LICENSE.txt in the project root for license information. 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #ifdef _WIN32_WINNT 11 | #undef _WIN32_WINNT 12 | #endif 13 | 14 | #define _WIN32_WINNT 0x601 15 | 16 | #include 17 | -------------------------------------------------------------------------------- /test/vswhere.test/vswhere.test.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | Header Files 50 | 51 | 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files 76 | 77 | 78 | Source Files 79 | 80 | 81 | Source Files 82 | 83 | 84 | Source Files 85 | 86 | 87 | Source Files 88 | 89 | 90 | Source Files 91 | 92 | 93 | Source Files 94 | 95 | 96 | Source Files 97 | 98 | 99 | Source Files 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /tools/test.cmd: -------------------------------------------------------------------------------- 1 | @if not defined _echo echo off 2 | 3 | REM Copyright (C) Microsoft Corporation. All rights reserved. 4 | REM Licensed under the MIT license. See LICENSE.txt in the project root for license information. 5 | 6 | powershell.exe -NoLogo -ExecutionPolicy Bypass -Command "%~dp0\test.ps1" %* 7 | -------------------------------------------------------------------------------- /tools/test.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (C) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE.txt in the project root for license information. 3 | 4 | [CmdletBinding()] 5 | param ( 6 | [Parameter()] 7 | [ValidateNotNullOrEmpty()] 8 | [string] $Configuration = $env:CONFIGURATION, 9 | 10 | [Parameter()] 11 | [ValidateNotNullOrEmpty()] 12 | [string] $Platform = $env:PLATFORM, 13 | 14 | [Parameter()] 15 | [ValidateSet('Unit', 'Integration', 'Functional', 'Runtime')] 16 | [string[]] $Type = @('Functional', 'Runtime'), 17 | 18 | [Parameter()] 19 | [switch] $Download 20 | ) 21 | 22 | if (-not $Configuration) { 23 | $Configuration = 'Debug' 24 | } 25 | 26 | if (-not $Platform) { 27 | $Platform = 'x86' 28 | } 29 | 30 | [bool] $Failed = $false 31 | 32 | if ($Type -contains 'Unit' -or $Type -contains 'Functional') 33 | { 34 | # Find vstest.console.exe. 35 | $cmd = get-command vstest.console.exe -ea SilentlyContinue | select-object -expand Path 36 | if (-not $cmd) { 37 | $vswhere = get-childitem "$PSScriptRoot\..\bin\*" -filter vswhere.exe -recurse | select-object -first 1 -expand FullName 38 | if (-not $vswhere) { 39 | write-error 'Please build vswhere before running tests.' 40 | exit 1 41 | } 42 | 43 | $path = & $vswhere -latest -requires Microsoft.VisualStudio.Component.ManagedDesktop.Core -property installationPath 44 | if (-not $path) { 45 | write-error 'No instance of Visual Studio found with vstest.console.exe. Please start a developer command prompt.' 46 | exit 1 47 | } 48 | 49 | $cmd = join-path $path 'Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe' 50 | } 51 | 52 | if (-not (test-path $cmd)) { 53 | write-error 'Could not find vstest.console.exe. Please start a developer command prompt.' 54 | exit 1 55 | } 56 | 57 | # Discover test containers for the current configuration. 58 | $containers = get-childitem "bin\$Configuration" -include *.test.dll -recurse | foreach-object { 59 | [string] $path = $_.FullName 60 | 61 | write-verbose "Discovered test assembly '$path'." 62 | "$path" 63 | } 64 | 65 | # Run functional tests. 66 | & $cmd $logger $containers /parallel /platform:$Platform 67 | if (-not $?) { 68 | $Failed = $true 69 | } 70 | } 71 | 72 | if ($Type -contains 'Integration' -or $Type -contains 'Runtime') 73 | { 74 | # Run docker runtime tests. 75 | $cmd = (get-command docker-compose -ea SilentlyContinue).Path 76 | if (-not $cmd -and $Download) { 77 | invoke-webrequest 'https://github.com/docker/compose/releases/download/1.11.2/docker-compose-Windows-x86_64.exe' -outfile "${env:TEMP}\docker-compose.exe" 78 | $cmd = "${env:TEMP}\docker-compose.exe" 79 | } 80 | 81 | if ($cmd) { 82 | [string] $path = resolve-path "$PSScriptRoot\..\docker\docker-compose.yml" 83 | 84 | $verbose = if ($VerbosePreference -eq 'Continue') { 85 | '--verbose' 86 | } 87 | 88 | write-verbose "Running tests in '$path'" 89 | & $cmd -f "$path" $verbose run --rm test 90 | if (-not $?) { 91 | $Failed = $true 92 | } 93 | } else { 94 | write-warning 'Failed to find docker-compose; integration tests will not be performed.' 95 | } 96 | 97 | if ($Failed) { 98 | exit 1 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "3.1", 4 | "buildNumberOffset": -1, 5 | "publicReleaseRefSpec": [ 6 | "^refs/heads/main$", 7 | "^refs/tags/v\\d+\\.\\d+" 8 | ], 9 | "cloudBuild": { 10 | "setAllVariables": true, 11 | "buildNumber": { 12 | "enabled": false 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vswhere.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.12 5 | MinimumVisualStudioVersion = 15.0.26228.4 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{20C5861F-C1E5-4BFB-B082-209793FBDCA5}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2F0C5F28-FD43-4045-85E8-BBD98B6B66B5}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vswhere", "src\vswhere\vswhere.vcxproj", "{210864F0-9A29-4479-B830-B802EE3F4D92}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vswhere.test", "test\vswhere.test\vswhere.test.vcxproj", "{76268871-D5A5-46BD-9805-41DB1C3072D1}" 13 | EndProject 14 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vswhere.lib", "src\vswhere.lib\vswhere.lib.vcxproj", "{4CCF39CB-4794-44E2-AA57-D215F13CF606}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EC52ABCD-D322-41A0-AE25-D3D1F05C9EEC}" 17 | ProjectSection(SolutionItems) = preProject 18 | CONTRIBUTING.md = CONTRIBUTING.md 19 | LICENSE.txt = LICENSE.txt 20 | README.md = README.md 21 | EndProjectSection 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|x86 = Debug|x86 26 | Release|x86 = Release|x86 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {210864F0-9A29-4479-B830-B802EE3F4D92}.Debug|x86.ActiveCfg = Debug|Win32 30 | {210864F0-9A29-4479-B830-B802EE3F4D92}.Debug|x86.Build.0 = Debug|Win32 31 | {210864F0-9A29-4479-B830-B802EE3F4D92}.Release|x86.ActiveCfg = Release|Win32 32 | {210864F0-9A29-4479-B830-B802EE3F4D92}.Release|x86.Build.0 = Release|Win32 33 | {76268871-D5A5-46BD-9805-41DB1C3072D1}.Debug|x86.ActiveCfg = Debug|Win32 34 | {76268871-D5A5-46BD-9805-41DB1C3072D1}.Debug|x86.Build.0 = Debug|Win32 35 | {76268871-D5A5-46BD-9805-41DB1C3072D1}.Release|x86.ActiveCfg = Release|Win32 36 | {76268871-D5A5-46BD-9805-41DB1C3072D1}.Release|x86.Build.0 = Release|Win32 37 | {4CCF39CB-4794-44E2-AA57-D215F13CF606}.Debug|x86.ActiveCfg = Debug|Win32 38 | {4CCF39CB-4794-44E2-AA57-D215F13CF606}.Debug|x86.Build.0 = Debug|Win32 39 | {4CCF39CB-4794-44E2-AA57-D215F13CF606}.Release|x86.ActiveCfg = Release|Win32 40 | {4CCF39CB-4794-44E2-AA57-D215F13CF606}.Release|x86.Build.0 = Release|Win32 41 | EndGlobalSection 42 | GlobalSection(SolutionProperties) = preSolution 43 | HideSolutionNode = FALSE 44 | EndGlobalSection 45 | GlobalSection(NestedProjects) = preSolution 46 | {210864F0-9A29-4479-B830-B802EE3F4D92} = {20C5861F-C1E5-4BFB-B082-209793FBDCA5} 47 | {76268871-D5A5-46BD-9805-41DB1C3072D1} = {2F0C5F28-FD43-4045-85E8-BBD98B6B66B5} 48 | {4CCF39CB-4794-44E2-AA57-D215F13CF606} = {20C5861F-C1E5-4BFB-B082-209793FBDCA5} 49 | EndGlobalSection 50 | GlobalSection(ExtensibilityGlobals) = postSolution 51 | SolutionGuid = {B8B58651-8009-4980-AE0B-69997634A2E9} 52 | EndGlobalSection 53 | EndGlobal 54 | --------------------------------------------------------------------------------