ChatGPT解决这个技术问题 Extra ChatGPT

How can I use PowerShell with the Visual Studio Command Prompt?

I've been using Beta 2 for a while now and it's been driving me nuts that I have to punt to cmd.exe when running the Visual Studio 2010 Command Prompt. I used to have a nice vsvars2008.ps1 script for Visual Studio 2008. Is there a vsvars2010.ps1 script or something similar?

As of Visual Studio 2022, there is already a dedicated shortcut that sets PowerShell for development. The default path to the directory which contains both Cmd and PowerShell shortcuts for VS 2022 on Windows 11 is C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio 2022\Visual Studio Tools. NOTE: out of the box the shortcut opens Windows PowerShell (i.e. version 5.1), but it can be easily changed to pwsh.exe to use the latest and greatest PowerShell 7.

M
Matt Dillard

Stealing liberally from blog post Replace Visual Studio Command Prompt with PowerShell, I was able to get this to work. I added the following to my profile.ps1 file and all is well with the world.

pushd 'c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC'
cmd /c "vcvarsall.bat&set" |
foreach {
  if ($_ -match "=") {
    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
  }
}
popd
write-host "`nVisual Studio 2010 Command Prompt variables set." -ForegroundColor Yellow

This has worked well for years - until Visual Studio 2015. vcvarsall.bat no longer exists. Instead, you can use the vsvars32.bat file, which is located in the Common7\Tools folder.

pushd 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools'    
cmd /c "vsvars32.bat&set" |
foreach {
  if ($_ -match "=") {
    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
  }
}
popd
write-host "`nVisual Studio 2015 Command Prompt variables set." -ForegroundColor Yellow

Things have changed yet again for Visual Studio 2017. vsvars32.bat appears to have been dropped in favor of VsDevCmd.bat. The exact path may vary depending on which edition of Visual Studio 2017 you're using.

pushd "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools"
cmd /c "VsDevCmd.bat&set" |
foreach {
  if ($_ -match "=") {
    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
  }
}
popd
Write-Host "`nVisual Studio 2017 Command Prompt variables set." -ForegroundColor Yellow

You can also make the split create just two items to avoid breaking values including the equal sign, which is also the separator of the environment variable name and the value:

$v = $_.split("=", 2); set-item -force -path "ENV:\$($v[0])"  -value 

Minor Changes for Visual Studio 2022, now that it's 64-bit.

pushd "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools"
cmd /c "VsDevCmd.bat&set" |
foreach {
  if ($_ -match "=") {
    $v = $_.split("=", 2); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])" 
  }
}
popd
Write-Host "`nVisual Studio 2022 Command Prompt variables set." -ForegroundColor Yellow

Just a quick note that this same technique works for Visual Studio 2012. Simply change "Microsoft Visual Studio 10.0" to "Microsoft Visual Studio 11.0"
echo $Profile to see the intended path for your profile.ps1, if you've never created it
The script itself works wonderfully. A note of caution, though (possibly): As the Package Manager Console in Visual Studio is itself a PowerShell host, this script will get run there as well. This doesn't appear to be a problem until you notice that "Run without debugging" or any other functions or plugins run that launch a standard Windows console will not work after the PMC is initialized. I worked around this by, instead of saving the script in this answer to "profile.ps1", I saved it to "Microsoft.PowerShell_profile.ps1" so that it only gets run in a "proper" PowerShell session.
It's really bad practice to hardcode the paths when there are perfectly fine environment variables (VS140COMNTOOLS for VS2015) to use. That will even work for custom VS installs.
I appreciate the desire to use environment variables, but those variables appear to be initialized by the very batch file we're trying to extract variables from. I would be happy to see evidence to the contrary. I have a clean Windows 10 install with a clean Visual Studio 2017 install and no VS150COMNTOOLS environment variable until I execute VsDevCmd.bat.
P
Peter Mortensen

The simplest option is to run the VS 2010 command prompt and then start PowerShell.exe. If you really want to do this from your "home" PowerShell prompt, the approach you show is the way to go. I use a script that Lee Holmes wrote a while back:

<#
.SYNOPSIS
   Invokes the specified batch file and retains any environment variable changes
   it makes.
.DESCRIPTION
   Invoke the specified batch file (and parameters), but also propagate any
   environment variable changes back to the PowerShell environment that
   called it.
.PARAMETER Path
   Path to a .bat or .cmd file.
.PARAMETER Parameters
   Parameters to pass to the batch file.
.EXAMPLE
   C:\PS> Invoke-BatchFile "$env:VS90COMNTOOLS\..\..\vc\vcvarsall.bat"
   Invokes the vcvarsall.bat file to set up a 32-bit dev environment.  All
   environment variable changes it makes will be propagated to the current
   PowerShell session.
.EXAMPLE
   C:\PS> Invoke-BatchFile "$env:VS90COMNTOOLS\..\..\vc\vcvarsall.bat" amd64
   Invokes the vcvarsall.bat file to set up a 64-bit dev environment.  All
   environment variable changes it makes will be propagated to the current
   PowerShell session.
.NOTES
   Author: Lee Holmes
#>
function Invoke-BatchFile
{
   param([string]$Path, [string]$Parameters)

   $tempFile = [IO.Path]::GetTempFileName()

   ## Store the output of cmd.exe.  We also ask cmd.exe to output
   ## the environment table after the batch file completes
   cmd.exe /c " `"$Path`" $Parameters && set > `"$tempFile`" "

   ## Go through the environment variables in the temp file.
   ## For each of them, set the variable in our local environment.
   Get-Content $tempFile | Foreach-Object {
       if ($_ -match "^(.*?)=(.*)$")
       {
           Set-Content "env:\$($matches[1])" $matches[2]
       }
   }

   Remove-Item $tempFile
}

Note: this function will be available in the PowerShell Community Extensions 2.0 module-based release coming soon.


P
Peter Mortensen

I found a simple method here: modify the shortcut.

The original shortcut is something like this:

%comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd.bat""

Add & powershell before the last quote, like this:

%comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd.bat" & powershell"

If you want to make it look more like PowerShell, go to the Colors tab of the shortcut properties and set the Red, Green and Blue values to 1, 36 and 86 respectively.


Straightforward and works like a charm. This answer deserves more upvotes.
The only thing I don't like about this, is that it keeps a redundant cmd.exe process loaded. Other than that is a nice solution.
P
Peter Mortensen

To (a) provide Visual Studio 2013 support; (b) combine the best of two previous answers; and (c) provide a function wrapper:

This builds on Andy's technique (which builds on Allen Mack's technique as Andy indicated (which in turn builds on Robert Anderson's technique as Allen indicated (all of which had a slight glitch as indicated on this page by the user known only as "me--", so I took that into account as well))).

Here is my final code--note the use of the non-greedy quantifier in the regex to handle any possible embedded equals in the values. That also happens to simplify the code: a single match instead of a match then split as in Andy's example or a match then indexof and substrings as in "me--"'s example).

function Set-VsCmd
{
    param(
        [parameter(Mandatory, HelpMessage="Enter VS version as 2010, 2012, or 2013")]
        [ValidateSet(2010,2012,2013)]
        [int]$version
    )
    $VS_VERSION = @{ 2010 = "10.0"; 2012 = "11.0"; 2013 = "12.0" }
    $targetDir = "c:\Program Files (x86)\Microsoft Visual Studio $($VS_VERSION[$version])\VC"
    if (!(Test-Path (Join-Path $targetDir "vcvarsall.bat"))) {
        "Error: Visual Studio $version not installed"
        return
    }
    Push-Location $targetDir
    cmd /c "vcvarsall.bat&set" |
    foreach {
      if ($_ -match "(.*?)=(.*)") {
        Set-Item -force -path "ENV:\$($matches[1])" -value "$($matches[2])"
      }
    }
    Pop-Location
    Write-Host "`nVisual Studio $version Command Prompt variables set." -ForegroundColor Yellow
}

For PowerShell 2.0 compatibility the param section requires [parameter(Mandatory=$true, ...
Nice, but would imo be nicer without the pushd/popd. Just use something like cmd /c """$targetDir\vcvarsall.bat""&set"
P
Peter Mortensen

Keith has already mentioned PowerShell Community Extensions (PSCX), with its Invoke-BatchFile command:

Invoke-BatchFile "${env:ProgramFiles(x86)}\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"

I also noticed that PSCX also has an Import-VisualStudioVars function:

Import-VisualStudioVars -VisualStudioVersion 2013

As of PSCX 3.2.0, VS 2015 is not supported in this cmdlet. I opened an issue for it.
P
Peter Mortensen

Kudos to Andy S for his answer. I've been using his solution for a while, but I ran into a problem today. Any value that has an equals sign in it is truncated at the equals sign. For example, I had:

JAVA_TOOL_OPTIONS=-Duser.home=C:\Users\Me

But my PowerShell session reported:

PS C:\> $env:JAVA_TOOL_OPTIONS

-Duser.home

I fixed this by modifying my profile script to the following:

pushd 'c:\Program Files (x86)\Microsoft Visual Studio 11.0\VC'
cmd /c "vcvarsall.bat&set" |
foreach {
  if ($_ -match "=") {
    $i = $_.indexof("=")
    $k = $_.substring(0, $i)
    $v = $_.substring($i + 1)
    Set-Item -force -path "ENV:\$k"  -value "$v"
  }
}
popd

Hey nice addition. Update it with the VS2015/17 versions too? This is the first google result, I think your addition will help people.
A
AlQuemist

Using 'Modules' and VsDevShell is much simpler and cleaner. A simple Powershell script is the following which works for MSVC and Clang:

param(
    [String] $Compiler = "MSVC"
)

if ($Compiler -ne "MSVC" -and $Compiler -ne "Clang") {
    Write-Error "Unknown compiler '$Compiler'; must be MSVC or Clang"
    Exit -1
}

Write-Host "======================================="
Write-Host "Setting up environment variables..."

# Visual Studio path <https://github.com/microsoft/vswhere/wiki/Find-VC>
$vsPath = &"${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationpath

Write-Host "Microsoft Visual Studio path = '$vsPath'"

# Use module `Microsoft.VisualStudio.DevShell.dll`
Import-Module (Get-ChildItem $vsPath -Recurse -File -Filter Microsoft.VisualStudio.DevShell.dll).FullName

Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation -DevCmdArguments '-arch=x64'

# NOTE: `-DevCmdArguments` are arguments to `vsdevcmd.bat`

# Select compiler
if ($Compiler -eq "MSVC") {
    $_Compiler = "MSVC"
    Set-Item -Path "env:CC" -Value "cl.exe"
    Set-Item -Path "env:CXX" -Value "cl.exe"
}
elseif ($Compiler -eq "Clang") {
    $_Compiler = "Clang"
    Set-Item -Path "env:CC" -Value "clang-cl.exe"
    Set-Item -Path "env:CXX" -Value "clang-cl.exe"
}

Write-Host "Selecting $_Compiler as C/C++ compiler."
Write-Host "======================================="

G
Glavo

In fact, we have a simple and clean way to do it.

As we all know, the child shell launched in the shell automatically inherits the variables of the parent shell. Therefore, we only need to start CMD in PowerShell, execute vcvars64.bat, and start PowerShell again in this CMD.

For Visual Studio 2019:

cmd /K '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" && powershell.exe'

If you are using PowerShell Core:

cmd /K '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" && pwsh.exe'

You can wrap it as a function and put it in Powershell Profiles ($PSHOME\Microsoft.PowerShell_profile.ps1 for all users):

function vcvars64 {
  cmd /K '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" && powershell.exe'
}

Or if you are using PowerShell Core:

function vcvars64 {
  cmd /K '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" && pwsh.exe'
}

Then simply execute vcvars64 in PowerShell:

PS C:\Users\Glavo> cl
cl: The term 'cl' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
PS C:\Users\Glavo> vcvars64
**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.11.5
** Copyright (c) 2021 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'
PowerShell 7.1.5
Copyright (c) Microsoft Corporation.

https://aka.ms/powershell
Type 'help' to get help.

PS C:\Users\Glavo> cl
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30136 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

usage: cl [ option... ] filename... [ /link linkoption... ]

p
pratikpc

Instead of

Calling the Batch script Writing the environment variables down Performing modifications in so complicated a way, with Visual Studio 2022 (and probably 2019), you can use the following much more simpler approach

Import-Module "VS-Directory\Community\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell -VsInstallPath "VS-Directory\Community" -DevCmdArguments

If however, you wish to use 64 bit MSVC and environment by default for VS, you can use the DevCmdArguments with arch=64

Enter-VsDevShell -VsInstallPath "VS-Directory\Community" -DevCmdArguments '-arch=x64'

P
Peter Mortensen

For someone who is still struggling with it in 2020 and Visual Studio Code 1.41.1, so this is a bit off-topic here.

Using all different parts of code from previous answers and the Internet, e.g., from How can I use vcvars64.bat from PowerShell? and with a step by step approach I managed to have the below script working.

Saved into Visual Studio Code "settings.json" and with the Code Runner extension installed.

With Microsoft (R) C/C++ Optimizing Compiler Version "cl.exe" from Visual Studio 2015/14.0:

"code-runner.runInTerminal": true,
"code-runner.executorMap": {
  "cpp": "cd $dir; pushd \"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\"; cmd.exe /c \"call vcvarsall.bat x86_amd64 & set > %temp%\\vcvars.txt\"; Get-Content \"$env:temp\\vcvars.txt\" | Foreach-Object { if ($_ -match \"^(.*?)=(.*)$\") {   Set-Content \"env:\\$($matches[1])\" $matches[2]  }}; popd; cls; cl *.cpp; .\\\"$fileNameWithoutExt.exe\"; Write-Host -NoNewLine 'Press any key to continue...';  $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'); del \"$fileNameWithoutExt.exe\"; del \"$fileNameWithoutExt.obj\""}

With Microsoft (R) C/C++ Optimizing Compiler Version "cl.exe" from Visual Studio 2019/16.4.3:

"code-runner.runInTerminal": true,
"code-runner.executorMap": {
  "cpp": "cd $dir; pushd \"c:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\"; cmd.exe /c \"call vcvarsall.bat x86_amd64 & set > %temp%\\vcvars.txt\"; Get-Content \"$env:temp\\vcvars.txt\" | Foreach-Object { if ($_ -match \"^(.*?)=(.*)$\") {   Set-Content \"env:\\$($matches[1])\" $matches[2]  }}; popd; cls; cl *.cpp; .\\\"$fileNameWithoutExt.exe\"; Write-Host -NoNewLine 'Press any key to continue...';  $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown'); del \"$fileNameWithoutExt.exe\"; del \"$fileNameWithoutExt.obj\""}

Why does a C++ compiler enter the picture?
@PeterMortensen AFAIR it was more to demonstrate how PowerShell can be not only started from Visual Studio Code Command Prompt, but also how it can be used in a more complex case, like the one with running C++ compiler with attributes.
P
Peter Mortensen

As an extension to Andy S's answer, this is updated for Visual Studio 2019 Community edition. However, I also needed 64-bit, so this includes the -arch=amd64 and -host_arch=amd64 flags as well for anyone that needs them.

pushd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools"
cmd /c "VsDevCmd.bat -arch=amd64 -host_arch=amd64&set " |
foreach {
  if ($_ -match "=") {
    $v = $_.split("="); set-item -force -path "ENV:\$($v[0])"  -value "$($v[1])"
  }
}
popd
Write-Host "`nVisual Studio 2019 Command Prompt variables set." -ForegroundColor Yellow

8
8DH

Try powershell module posh-vsdev. It even seems to have support for multiple installed versions of Visual Studio.

Install-Module -Name posh-vsdev -Scope CurrentUser

# Import the posh-vsdev module module
Import-Module posh-vsdev

# Get all installed Visual Studio instances
Get-VisualStudioVersion

# Get a specific version
Get-VisualStudioVersion -Version 14

# Use the Developer Command Prompt Environment
Use-VisualStudioEnvironment

# Restore the non-Developer environment
Reset-VisualStudioEnvironment

# Reset cache of Visual Studio instances
Reset-VisualStudioVersionCache

# Add posh-vsdev to your PowerShell profile
Add-VisualStudioEnvironmentToProfile -UseEnvironment

M
MatrixManAtYrService

I like to pass the commands into a child shell like so:

cmd /c "`"${env:VS140COMNTOOLS}vsvars32.bat`" && <someCommand>"

Or alternatively

cmd /c "`"${env:VS140COMNTOOLS}..\..\VC\vcvarsall.bat`" amd64 && <someCommand> && <someOtherCommand>"  

Why? What is the reason? What advantages does it have? Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today).
Sorry @PeterMortensen, I can't remember why the above hijinks were necessary, or why I preferred them to the alternatives. The only thought I can summon when I look at this is that I should've switched to vim sooner.
s
sunny moon

First, check the contents of this folder:

C:/ProgramData/Microsoft/VisualStudio/Packages/_Instances/

There'll be another folder in it with a name consisting of hex digits (e.g. 2a7a9ed6, but that will vary for different MSVC versions). I'll refer to it as <instance_id>.

Then run from PowerShell:

Import-Module 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll'; Enter-VsDevShell <instance_id> -DevCmdArguments '-arch=x64'

Or you can create a shortcut with the following target:

<path to your powershell.exe> -noe -c "&{Import-Module """C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"""; Enter-VsDevShell <instance_id> -DevCmdArguments '-arch=x64'}"

Obviously, drop -arch=x64 if you need the x86 toolset.


E
Emiosproj

To start any latest PowerShell with dev tools env:

C:/Program Files/PowerShell/7/pwsh.exe -noe -c "&{Import-Module """C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"""; Enter-VsDevShell 5f8ef82b}" 

If it doesn't work look for Developer PowerShell for VS 2019 via quick start menu search -> right-click properties -> copy target line -> change exe path to desired PowerShell.exe

Can also start it like that for your Editor/IDE of choice Example for vscode:

"terminal.integrated.profiles.windows": {
"win_dev_shell_64": {
  "path": "C:/Program Files/PowerShell/7/pwsh.exe",
  "args": [
    "-noe",
    "-c",
    "&{Import-Module \"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\Common7\\Tools\\Microsoft.VisualStudio.DevShell.dll\"; Enter-VsDevShell 5f8ef82b}"
    ]
  }
},
"terminal.integrated.defaultProfile.windows": "win_dev_shell_64",