如何获取csc.exe路径?

72

有没有一种方法可以获取最新的.NET Framework的csc.exe路径?

文件通常在:c:\Windows\Microsoft.NET\Framework\vX.X.XXX,但问题是可能安装了多个版本 + 既有32位也有64位版本。

是否有任何解决方案?


2
你的问题是什么? - leppie
7
你知道可以使用CodeDomProvider从字符串源文件构建代码吗?(或者更精确地说是CodeDomProvider.CompileAssemblyFromSource)换句话说,当框架已经支持这一点时,就没有必要使用独立的工具csc.exe - vgru
你检查了系统变量下的“Path”吗?也许那里保存了路径。 - V4Vendetta
1
希望微软永远不会重复给我们带来3.0和3.5版本的“版本控制”所带来的愉悦,但是当3.0安装时,它并没有带上自己的csc版本(你必须知道去找2.0.xxxxx版本)。如果微软再次重复这种事情,最新的框架版本可能不会引导您到最新的csc版本。 - Damien_The_Unbeliever
1
如果您希望的话,我可以取消接受您的答案,但是那个答案在当时解决了我的问题,而当前得票最高的答案对我没有任何价值。 - Rok Povsic
显示剩余2条评论
10个回答

37

c:\Windows\Microsoft.NET\Framework\vX.X.XXX应该包含最新的32位版本csc.exe。

c:\Windows\Microsoft.NET\Framework64\vX.X.XXX应该包含最新的64位版本csc.exe。

至少对于我的电脑是这样的。

另外,你可以使用Visual Studio命令行从程序文件夹中的Visual Studio工具文件夹访问两个版本。它会自动设置所有你需要用csc编译器构建32位和64位应用程序的路径。


4
但这基本上就是OP所说的。 :) - vgru
2
是的,也许我只是没有理解他的问题。噢,不管怎样,我尝试过了。 :) - Liquid Wotter
1
当3.0是框架的最新“版本”时,3.0文件夹中没有包含csc的副本(现在仍然没有)。 - Damien_The_Unbeliever
对我来说可以,但还需要 cscui.exe - undefined

21

找到 CSC.exe 路径的最佳方式是在命令行界面(CLI)中运行以下简单命令:

where CSC.exe

dir /s %WINDIR%\CSC.EXE

dir - 显示目录

/s - 包括子文件夹

%WINDIR%\CSC.EXE - 在根目录中查找类似"CSC.exe"的短语。

这是我们的结果:输入图像描述

然后,我们可以按行编译示例代码,例如:

C:\WINDOWS\...\v.4.0.30319\CSC.exe HelloWorld.cs

致敬。


16

您可以使用 System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory() 方法。

using System.Runtime.InteropServices;
var frameworkPath = RuntimeEnvironment.GetRuntimeDirectory();
var cscPath = Path.Combine(frameworkPath, "csc.exe");

Console.WriteLine(frameworkPath);  // C:\Windows\Microsoft.NET\Framework\v4.0.30319
Console.WriteLine(cscPath); }      // C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe

11

更新:

打开命令提示符或Powershell,运行下面的命令以列出已安装不同 .Net Framework 版本的编译器的完整路径。

dir %WINDIR%\Microsoft.NET\Framework64\csc.exe /s/b

CSC 路径如下:

C:\Program Files\MSBuild\\Bin

例如:如果您正在使用 Visual Studio 2013,则为 12.0。


是的,这就是我使用的方法。举个例子,我将C:\Program Files (x86)\MSBuild\14.0\Bin添加到了我的路径中,这样我就可以在终端应用程序中使用csc命令。 - Kelly Kiernan

8
我假设您希望使用csc.exe在一个普通的非开发人员PC上编译文件,这就是为什么您要询问路径的原因。
与其查找注册表以达到此目的,不如使用我创建的以下小批处理脚本来获取可用的最新csc.exe 路径; 路径是在脚本的第一部分确定,然后存储在环境变量dotNet中。
@echo off
pushd .& setlocal
set dotNetVS=%ProgramFiles(x86)%\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\Roslyn\
set dotNetBase=%SystemRoot%\Microsoft.NET\Framework\
rem get latest .net path containing csc.exe:
set dotNet20=%dotNetBase%v2.0.50727\
set dotNet35=%dotNetBase%v3.5\
set dotNet40=%dotNetBase%v4.0.30319\
if exist %dotNet20%nul set dotNet=%dotNet20%
if exist %dotNet35%nul set dotNet=%dotNet35%
if exist %dotNet40%nul set dotNet=%dotNet40%
set msbuildDir=%ProgramFiles(x86)%\MSBuild\14.0\Bin\
if exist "%dotNetVS%csc.exe" set msbuildDir=%dotNetVS%
set cscExe="%msbuildDir%csc.exe"
if not exist %cscExe% set cscExe="%dotNet%csc.exe"
::echo %cscExe%

set assemblies=
REM reference required assemblies here, copy them to the output directory after the colon (:), separated by comma
REM set assemblies=-reference:.\PresentationCore.Dll,.\WindowsBase.dll

REM If preview does not work, set this to latest:
set langversion=preview

set runArgs=%3 %4 %5 %6 %7 %8 %9 
if not "%1"=="/run" (set outPath=%~DP1& set outFileName=%~n1& CALL :Compile) else (set outPath=%~DP2& set outFileName=%~n2& CALL :CompileAndRun)
GOTO :End

:Compile
    echo Compiling "%outPath%%outFileName%.cs"
    if exist "%outPath%%outFileName%.exe" del /F "%outPath%%outFileName%.exe" 
    %cscExe% -langversion:%langversion% -optimize+ -lib:"%dotNet%\" %assemblies% /t:exe /out:"%outPath%%outFileName%.exe" %outFileName%.cs
exit /B

:CompileAndRun
    echo Compiling "%outPath%%outFileName%.cs"
    if exist "%outPath%%outFileName%.exe" del /F "%outPath%%outFileName%.exe" 
    %cscExe% -langversion:%langversion% -optimize+ -lib:"%dotNet%\" %assemblies% /t:exe /out:"%outPath%%outFileName%.exe" %outFileName%.cs >nul
    echo Running "%outPath%%outFileName%.exe"
    "%outPath%%outFileName%.exe" %runArgs%
exit /B
    
:End 
::::

它将使用该路径来调用嵌入在Windows中的最新可用C#编译器。请注意,Visual Studio正在使用比Windows更新的C#编译器,使用Windows时您仅限于C#版本5。这在您打算使用它编译源代码时非常重要。
我已更新代码,使其能够在安装Roslyn而不是Windows CSC.EXE的情况下使用。
将其保存为CompileCS.cmd并将其放置在与*.cs文件相同的路径中。然后,您可以按以下方式简单编译它:

CompileCS GetDotNetVersion.cs

这将编译控制台应用程序GetDotNetVersion,一个用于确定已安装的.NET版本的程序,我已在此处。发布。

提示: 如果您想在编译后立即运行 C# 应用程序,请使用以下方式的 /run 参数(如果需要,可以在 .cs 文件末尾传递命令行参数,它们将被传递到已编译的 .exe 文件中):

CompileCS /run GetDotNetVersion.cs [arg1] ... [arg7]

该脚本检查 .NET 2.0.x、3.5 和 4.0.30319 的系统目录是否存在 - 在其他文件夹中,我从未见过 csc.exe。因为它从旧版本到新版本进行检查,所以变量 dotNet 包含最新的现有路径。

请注意

  • 语言设置为 preview,以允许尝试最新的语法。如果遇到任何问题,请将 langversion 更改为 latest(适用于 Roslyn 编译器)。

  • 对于 .NET core 项目,您可以使用 dotnet run 命令而不是 CSC。它包含在 .NET core SDK 中。

  • Microsoft 将所有 4.x 版本的 .NET - 包括最新版本 4.7.1 - 存储在文件夹 4.0.30319 中。因此,如果安装了任何版本的 .NET 4.x,则会在那里找到它。

  • 如果需要 64 位版本 而不是 32 位版本,则只需在环境变量 dotNetBase 中将 Framework 替换为 Framework64(脚本的第二行)。请注意,即使在 64 位 Windows 上,通常也会安装 32 位版本的框架,如果您不真正需要 64 位,它将在大多数具有 32 位的 PC 上正常工作。但是,如果您更喜欢 Framework64,则可以添加路径存在性检查并优先使用Framework64(就像我为 .NET 版本所做的那样)。

  • 如前所述,虽然 csc.exe 仍然存在,但它被限制在 C# 版本 5 上(每次调用 csc.exe 时都会收到此警告)。但是对于许多有用的小型控制台应用程序,C#5 仍然可以使用。如果您需要更高版本 (C#6、...、C#9 或更高版本), 那么您将需要 Visual Studio,或者您可以访问 Roslyn GitHub 区域 获取 Roslyn 编译器源代码。

  • 如果您引用了外部程序集(*.dll),则需要更新代码中的以下行:set assemblies=-reference:.\PresentationCore.Dll,.\WindowsBase.dll - 只需附加您需要的任何 dll - 列表以逗号分隔。您需要将它们从 dotNet 路径复制到输出目录(创建 *.exe 的位置)。如果不需要任何程序集,则只需将其设置为空白(即 set assemblies=)。

  • 您可以通过比较 *.cs 文件和 *.exe 文件的文件日期/时间来检查是否需要编译(并仅在 *.cs 文件新于 *.exe 文件时进行编译),从而增强脚本的功能。如何使用批处理程序完成这项工作在 此处 进行了描述。

  • 使用 C# 编译器的另一种方法是从您的 C# 程序中。Microsoft 在 此处 描述了这种方法。

  • 我发现一个很好的描述如何使用CSC.exe 响应文件。对于更复杂的情况,您将需要 MSBUILD。来自 Valve Developer Community 的这个描述向您展示了如何在不安装 Visual Studio 的情况下获取它。来看看吧 :-)


我认为应该添加Roslyn编译器路径,就像这个... "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Roslyn\csc.exe" - beppe9000
@beppe9000 - 微软对此并不是很清楚。请查看此链接,只提到了 "通常csc.exe可执行文件位于Windows目录下的Microsoft.NET\Framework<Version>文件夹中。它的位置可能因特定计算机的确切配置而有所不同。如果您的计算机上安装了多个版本的.NET Framework,则会找到多个此文件的版本。" - Matt
@Matt 如果你像那个链接建议的那样使用开发人员命令提示符,那么你路径上的第一个 csc.exe 就是例如 C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Roslyn\csc.exe - Carl Walsh
@CarlWalsh - 是的,你说得对。我指的是通常需要处理的普通非开发人员PC。在这样的PC上,您可以使用我提供的链接来编译+运行一个小的C#程序,该程序检查并打印已安装的.NET版本。 (https://dev59.com/MXNA5IYBdhLWcg3wdtpd#46826686) - Matt
1
@CarlWalsh - 回答晚了,但我现在已经添加了对Visual Studio 2022的支持。 - Matt

7
在开发者命令提示符中运行以下命令:
where csc

你可以获取安装有Visual Studio和net4的路径,例如:
C:\Program Files\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Roslyn\csc.exe
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe

谢谢,这非常有用。 - Ashraf Sada

3
如果您已经安装了Visual Studio,则只需执行以下操作: 单击“开始”,将鼠标指向“所有程序”,将鼠标指向“Microsoft Visual Studio”,将鼠标指向“Visual Studio工具”,然后单击“Visual Studio命令提示符”。 这样您就可以进入命令行界面进行编译,如下所示:
csc PathToYourCsSource

2
我认为他试图以编程方式完成它。 - David
1
如果csc.exe不在您的$PATH环境变量中,则此方法将无法工作。 - Kellen Stuart

2
最近,微软对此进行了很好的记录-请查看此处。csc.exe可执行文件通常位于Windows目录下的Microsoft.NET\Framework\文件夹中。它的位置可能因特定计算机的确切配置而异。如果您的计算机安装了多个版本的.NET Framework,则会找到多个此文件的版本。

1

死灵法术。
这是ReportViewer中的实现方式:

string compilerDirectory = System.IO.Path.Combine(
System.Environment.GetEnvironmentVariable("windir")
, "Microsoft.NET\\Framework" + (System.Environment.Is64BitProcess ? "64" : "")
, System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion());

C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319
"C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319\vbc.exe" 
"C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319\csc.exe" 

但是在2018年,最好使用内置的roslyn编译器:
以下是一个示例:
         protected override System.CodeDom.Compiler.CompilerResults FromFileBatch(System.CodeDom.Compiler.CompilerParameters options, string[] fileNames)
        {

#if NETSTANDARD2_0
            return NetStandardFromFileBatch(options, fileNames);
#else
            return OldFromFileBatch(options, fileNames);
#endif
        }




#if NETSTANDARD2_0         



        protected System.CodeDom.Compiler.CompilerResults NetStandardFromFileBatch(System.CodeDom.Compiler.CompilerParameters options, string[] fileNames)
        {
            //// C:\Program Files\dotnet\sdk\2.0.0\Roslyn

            //string sysver = System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion();
            //System.Console.WriteLine(sysver);


            //string pf64 = System.Environment.ExpandEnvironmentVariables("%ProgramW6432%");
            //string pf32 = System.Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%");
            //string pf = pf32;

            //if (System.IntPtr.Size * 8 == 64)
            //    pf = pf64;

            //// compilerDirectory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFiles);
            ////compilerDirectory = System.IO.Path.Combine(compilerDirectory, "dotnet", "sdk", "2.0.0", "Roslyn");
            //compilerDirectory = System.IO.Path.Combine(pf32, "MSBuild", "14.0", "Bin");
            //if (System.IntPtr.Size * 8 == 64)
            //    compilerDirectory = System.IO.Path.Combine(compilerDirectory, "amd64");

            string assemblyName = System.IO.Path.GetFileNameWithoutExtension(options.OutputAssembly);

            Microsoft.CodeAnalysis.SyntaxTree[] syntaxTrees = new Microsoft.CodeAnalysis.SyntaxTree[fileNames.Length];

            for (int i = 0; i < fileNames.Length; ++i)
            {
                string fileContent = System.IO.File.ReadAllText(fileNames[i], System.Text.Encoding.UTF8);

                Microsoft.CodeAnalysis.VisualBasic.VisualBasicParseOptions op = null;

                // ERR_EncodinglessSyntaxTree = 37236 - Encoding must be specified... 
                syntaxTrees[i] = Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.ParseText(
                    fileContent, op, fileNames[i], System.Text.Encoding.UTF8
                );

            }

            Microsoft.CodeAnalysis.MetadataReference[] references =
                new Microsoft.CodeAnalysis.MetadataReference[options.ReferencedAssemblies.Count];

            for (int i = 0; i < references.Length; ++i)
            {
                references[i] = Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(
                    options.ReferencedAssemblies[i]
                );
            }



            Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions co =
                new Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions
            (
                Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary
            );

            co.WithOptionStrict(Microsoft.CodeAnalysis.VisualBasic.OptionStrict.Off);
            co.WithOptionExplicit(false);
            co.WithOptionInfer(true);

            Microsoft.CodeAnalysis.Compilation compilation = Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation.Create(
                assemblyName,
                syntaxTrees,
                references,
                co
            );


            System.CodeDom.Compiler.CompilerResults compilerResults = new System.CodeDom.Compiler.CompilerResults(options.TempFiles);

            compilerResults.NativeCompilerReturnValue = -1;

            // using (var dllStream = new System.IO.MemoryStream())
            using (System.IO.FileStream dllStream = System.IO.File.Create(options.OutputAssembly))
            {
                using (System.IO.MemoryStream pdbStream = new System.IO.MemoryStream())
                {
                    Microsoft.CodeAnalysis.Emit.EmitResult emitResult = compilation.Emit(dllStream, pdbStream);
                    if (!emitResult.Success)
                    {

                        foreach (Microsoft.CodeAnalysis.Diagnostic diagnostic in emitResult.Diagnostics)
                        {
                            // options.TreatWarningsAsErrors
                            if (diagnostic.IsWarningAsError || diagnostic.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error)
                            {
                                string errorNumber = diagnostic.Id;
                                string errorMessage = diagnostic.GetMessage();

                                string message = $"{errorNumber}: {errorMessage};";
                                string fileName = diagnostic.Location.SourceTree.FilePath;

                                Microsoft.CodeAnalysis.FileLinePositionSpan lineSpan = diagnostic.Location.GetLineSpan();
                                string codeInQuestion = lineSpan.Path;
                                int line = lineSpan.StartLinePosition.Line;
                                int col = lineSpan.StartLinePosition.Character;

                                compilerResults.Errors.Add(
                                    new System.CodeDom.Compiler.CompilerError(fileName, line, col, errorNumber, errorMessage)
                                );
                            } // End if 

                        } // Next diagnostic 

                        // emitResult.Diagnostics
                        // CheckCompilationResult(emitResult);
                    }
                    else
                    {
                        compilerResults.PathToAssembly = options.OutputAssembly;
                        compilerResults.NativeCompilerReturnValue = 0;
                    }
                }
            }

            // compilerResults.CompiledAssembly = System.Reflection.Assembly.Load(array3, null);

            return compilerResults;
        }
#endif

0

在命令提示符中更简单的使用csc.exe的方法是运行以下get_csc.bat。您可以通过%CSC_PATH%运行csc.exe。

@FOR /f "usebackq" %%A IN (`powershell join-path ^([Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory^(^)^)  csc.exe`) DO @SET CSC_PATH=%%A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接