Page MenuHomePhorge

Visual Studio
Updated 86 Days AgoPublic

Run builds without the Visual Studio GUI

Sometimes you can get away with just calling MSBuild against a solution or project file. For convenience, here's some example paths:

# Visual Studio 2008
# Visual Studio 2015 32-bit and 64-bit
"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe"
"C:\Program Files (x86)\MSBuild\14.0\Bin\amd64\MSBuild.exe"
# Visual Studio 2017
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"
# Visual Studio 2019
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe"
# Visual Studio 2022
"C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe"

# If you have a new enough version, this should locate it and describe it
"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"

You may need to run one of the vsdevcmd or vcvars* .bat files to initialize the environment. See for Microsoft's documentation. Note that you cannot run these batch files within Powershell and have a good time, you need to do this in cmd.exe as that keeps the cmd.exe instance it's opening open, though you can then open Powershell and have it inherit from the dev cmd it's in.

# Visual Studio 2017
"C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/Common7/Tools/vsdevcmd"

# Visual Studio 2022, 32-bit
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat"

Have multiple builds run without killing eachother

If you're running more than one build at once (especially with MSBuild, maybe also true of Visual Studio itself?) you may run into

LINK : fatal error LNK1318: Unexpected PDB error; RPC (23) '(0x000006BA)'

This is because the build process fires up an instance of PDBServe if one doesn't exist but uses existing ones if one is running, and then terminates that once the build is done. Which means if you start a second build while another build is running, well . . .

See and, which details that there's a secret undocumented environment variable to control what PDBServe instance is being used: _MSPDBSRV_ENDPOINT_. This led @keithzg to solve things in Jenkins builds like this:


Fuck the GUI I'm editing configs manually

To avoid the war crime that is Visual Studio's GUI, you can just edit the .sln and .*proj files manually, the former being fairly simple and the latter just being XML.

.csproj files

C# projects use .csproj. You can also avoid msbuild entirely with dotnet's native tools, though that is untried by this writer as of this writing. Documentation that might come very much in handy includes:

Redistribute the right DLLs

The official Microsoft tools for building software in C/C++ for Windows naturally results in binaries that, on their own, will not necessarily run on any given Windows installation, another win for Microsoft's org chart. documents three ways to distribute the necessary libraries.

  1. Redistribute the installer for that version of Visual Studio's redistributable DLLs and have it automatically run by your installer or something. This is Microsoft's recommended method.
  2. Something called merge modules that I don't know about and Microsoft says you shouldn't use.
  3. Just shipping the DLLs in the same folder as your software, which Microsoft admits you can do but really doesn't want you to do.

For years now, the only officially cited location for these DLLs has been this paragraph with the same typo:

The individual Redistributable DLLs are also included in your installation of Visual Studio. By default, they're installed in the Visual the %VCToolsRedistDir%\debug_nonredist\[architecture]\Microsoft.[toolset].[library] folders, where [architecture] represents the target architecture, [toolset] represents the toolset version, and [library] is the Debug library that has the debug DLLs.

In terms of figuring out which ones you need to include, Microsoft's docs also kinda leave this as an exercise for the reader:

To determine which DLLs you have to redistribute with your application, collect a list of the DLLs that your application depends on. These DLLs are normally listed as import library inputs to the linker. Certain libraries, such as vcruntime and the Universal C Runtime Library (UCRT), are included by default. If your app or one of its dependencies uses LoadLibrary to dynamically load a DLL, that DLL may not be listed in the inputs to the linker. One way to collect the list of dynamically loaded DLLs is to run Dependency Walker (depends.exe) on your app, as described in Understanding the Dependencies of a Visual C++ Application. Unfortunately, this tool is outdated and may report that it can't find certain DLLs.

Thanks, Microsoft!

For the mentioned UCRT DLLs:

If you can get enough DLLs in a folder that things run, you may then need, like Keith has, to resort to using scripts to just check each file and see if it's necessary or not. Ex.

import os
import subprocess

for fname in os.listdir('win-x86'):
	os.rename("win-x86/"+fname, "x/"+fname)
	try:".\\win-x86\\xaml2image.exe \\\\eliot\\case-data\\T1133\\xaml\\ D:/tmp/out", shell=True, check=True)
	except subprocess.CalledProcessError as err:
		os.rename("x/"+fname, "win-x86/"+fname)
Last Author
Last Edited
Mar 26 2024, 12:31 PM