有没有一种方法可以获取最新的.NET Framework的csc.exe路径?
文件通常在:c:\Windows\Microsoft.NET\Framework\vX.X.XXX,但问题是可能安装了多个版本 + 既有32位也有64位版本。
是否有任何解决方案?
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位应用程序的路径。
cscui.exe
。 - undefined您可以使用 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
更新:
打开命令提示符或Powershell,运行下面的命令以列出已安装不同 .Net Framework 版本的编译器的完整路径。
dir %WINDIR%\Microsoft.NET\Framework64\csc.exe /s/b
CSC 路径如下:
C:\Program Files\MSBuild\\Bin
例如:如果您正在使用 Visual Studio 2013,则为 12.0。
csc.exe
在一个普通的非开发人员PC上编译文件,这就是为什么您要询问路径的原因。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
::::
CompileCS.cmd
并将其放置在与*.cs文件相同的路径中。然后,您可以按以下方式简单编译它:
这将编译控制台应用程序GetDotNetVersion,一个用于确定已安装的.NET版本的程序,我已在此处。发布。
CompileCS GetDotNetVersion.cs
提示: 如果您想在编译后立即运行 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 的情况下获取它。来看看吧 :-)
"%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Roslyn\csc.exe"
- beppe9000csc.exe
就是例如 C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Roslyn\csc.exe
。 - Carl Walshwhere csc
C:\Program Files\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Roslyn\csc.exe
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe
csc PathToYourCsSource
csc.exe
不在您的$PATH
环境变量中,则此方法将无法工作。 - Kellen Stuart死灵法术。
这是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"
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
在命令提示符中更简单的使用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
CodeDomProvider
从字符串源文件构建代码吗?(或者更精确地说是CodeDomProvider.CompileAssemblyFromSource
)换句话说,当框架已经支持这一点时,就没有必要使用独立的工具csc.exe
。 - vgru