71

Is there a way to get path for the latest .NET Framework's csc.exe?

The file usually in: c:\Windows\Microsoft.NET\Framework\vX.X.XXX but the problem is there can be multiple versions installed + there are both 32 and 64 bit versions.

Any solution to this?

svick
  • 236,525
  • 50
  • 385
  • 514
Rok Povsic
  • 4,626
  • 5
  • 37
  • 53
  • 2
    What exactly is your problem? – leppie Jul 12 '11 at 06:59
  • 7
    You do know that you can use a `CodeDomProvider` to build from a string source file? (or `CodeDomProvider.CompileAssemblyFromSource`, to be precise) In other words, there is no need to use `csc.exe` as a separate tool when framework already supports it. – vgru Jul 12 '11 at 07:04
  • Did you check the `Path` under system variables, maybe that holds the path – V4Vendetta Jul 12 '11 at 07:09
  • 1
    Hopefully, MS will never repeat the joyfulness of the "versioning" that gave us 3.0 and 3.5, but when 3.0 installs, it doesn't bring its own version of csc (you have to know to go and find the 2.0.xxxxx version). If MS ever do repeat that kind of thing, latest framework version may not lead you to latest version of csc. – Damien_The_Unbeliever Jul 12 '11 at 07:23
  • @Damien_The_Unbeliever .Net 4.5 installs on top of .Net 4.0, so it doesn't have its own version of csc.exe either. – svick Apr 22 '12 at 14:19
  • I don't think there is a reliable way to do this any more with the diversification of frameworks and different versions of Visual Studio. I would like to consider deleting my answer since it's no longer valid. – Richard Cook Jan 24 '17 at 23:02
  • 1
    I can unaccept your answer if you wish, however, the answer solved my problem at the time and the currently highest voted answer provided no value to me. – Rok Povsic Jan 25 '17 at 16:08

10 Answers10

36

c:\Windows\Microsoft.NET\Framework\vX.X.XXX Should contain the latest 32 bit version of csc.exe

c:\Windows\Microsoft.NET\Framework64\vX.X.XXX Should contain the lastest 64 bit version of csc.exe

That's what it is for mine anyway.

BTW: You can access both by using the Visual Studio Command Line from your visual studio tools folder in your program files. It auto sets up all the paths you need to build 32 and 64 bit apps with your csc compiler.

Liquid Wotter
  • 493
  • 3
  • 5
21

The best way to find CSC.exe path is running in CLI (Command Line Interpreter) that simple line:

dir /s %WINDIR%\CSC.EXE

dir - shows directory

/s - includes subfolders

%WINDIR%\CSC.EXE - looks in root folder for phrase like "CSC.exe".

And it is our result: enter image description here

Then we can simply compile example code by line like:

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

Regards.

Bear
  • 1,017
  • 1
  • 10
  • 23
16

You can use 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
Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
11

Updated:

Open Command Prompt or Powershell and run the below command to list complete path of compilers for different .Net Frameworks version installed.

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

CSC Path is as follows:

C:\Program Files\MSBuild\\Bin

Ex: It will be 12.0, if you are using Visual Studio 2013.

Naved Deshmukh
  • 528
  • 7
  • 20
  • Yep this is what I use. For example, I added C:\Program Files (x86)\MSBuild\14.0\Bin to my path so that I can use csc in my terminal app. – Kelly Kiernan Mar 22 '16 at 21:04
8

I assume you want to use csc.exe to compile files on a plain, non-developer PC, that's why you were asking for the path.

Rather than looking into the registry for this purpose you can use the following little batch script I have created to get the path to the latest csc.exe available; the path is determined in the first section of the script and then stored in the environment variable 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 
::::

It will then use that path to invoke the latest available C# compiler embedded in Windows. Note that Visual Studio is using newer C# compilers than Windows, with Windows you are limited to C# version 5. This is important to keep in mind when you intend to compile source code with it.

I have updated the code so that it is able to use Roslyn instead of Windows CSC.EXE if it is installed.

Save it as CompileCS.cmd and put it in the same path as your *.cs files. Then you can simply compile it as follows:

CompileCS GetDotNetVersion.cs

Which will compile the console application GetDotNetVersion, a program to determine the installed .NET versions, which I've published here.

Hint: If you want to run the C# application immediately after compilation, use the /run parameter as follows (you can pass command line arguments at the end of the .cs file if required, they will be passed to the compiled .exe file):

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

The script checks for existence of the system directories for .NET 2.0.x, 3.5 and 4.0.30319 - in other folders I have never seen csc.exe. Because it does the check from the oldest to the newest version, the variable dotNet contains the latest existing path.

Note that

  • Language is set to preview to allow trying out latest syntax. Change langversion to latest if you encounter any issues (applies to Roslyn compiler)

  • For .NET core projects, you can use the dotnet run command instead of CSC. It is included in the .NET core SDK.

  • Microsoft stores all the 4.x versions of .NET - including the latest version 4.7.1 - in the folder 4.0.30319. Because of that, if any version of .NET 4.x is installed, you will find it there.

  • If you need the 64 bit version rather than the 32 bit version then simply replace Framework by Framework64 in the environment variable dotNetBase (2nd line of the script). Note that even on 64 bit Windows there is usually a 32 bit version of the framework installed and if you don't really need to have 64 bit, it will work fine on most PCs with 32 bit. However, you could add a check for existance of the path and favor Framework64 if you prefer that (like I did it for the .NET versions).

  • As said before, while csc.exe still exists, it is restricted to C# version 5 (you will get this warning each time you invoke csc.exe). But for many little useful console applications, C#5 is still fine. If you need a higher version (C#6, ..., C#9 or higher), then you'll need either Visual Studio or you can visit the Roslyn GitHub area to get the Roslyn compiler source code.

  • If you're referencing external assemblies (*.dll's), you will need to update the following line in the code: set assemblies=-reference:.\PresentationCore.Dll,.\WindowsBase.dll - just append any dll's you need - the list is comma-separated. You need to copy them from the dotNet path to the output directory (where the *.exe is being created). If no assemblies are needed, just set it blank (i.e. set assemblies=).

  • You could enhance the script by checking if compilation is required (by comparing the file date/time of the *.cs file and *.exe file and compile only if the *.cs file is newer than the *.exe file). How that can be done with a batch is described here.

  • An alternative way to use the C# compiler is from within your C# program. Microsoft describes this approach here.

  • I found a nice description how to use response files with CSC.exe. And for more complex scenarios you will need MSBUILD This description from the Valve Developer Community shows you how you can get it without installing Visual Studio. Check it out :-)

Matt
  • 25,467
  • 18
  • 120
  • 187
  • I think the roslyn compiler paths whould be added, like this one... `"%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Roslyn\csc.exe"` – beppe9000 Feb 15 '19 at 16:52
  • @beppe9000 - Microsoft isn't very clear about this. Check [this link](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/command-line-building-with-csc-exe), there is only mentioned *"The [csc.exe](https://github.com/dotnet/roslyn/issues/15556) executable file usually is located in the Microsoft.NET\Framework\ folder under the Windows directory. Its location might vary depending on the exact configuration of a particular computer. If more than one version of the .NET Framework is installed on your computer, you'll find multiple versions of this file."* – Matt Feb 18 '19 at 13:24
  • @Matt if you use the Developer Command Prompt like that link recommends, the first `csc.exe` on your path is e.g. `C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Roslyn\csc.exe` – Carl Walsh Sep 18 '19 at 06:11
  • @CarlWalsh - That is correct. I was referring to a plain, non-developer PC that you often have to deal with. On such PCs you can use the link I provided to compile + run [a little C# program which checks and prints the .NET version](https://stackoverflow.com/questions/951856/is-there-an-easy-way-to-check-the-net-framework-version/46826686#46826686) installed. – Matt Sep 20 '19 at 08:14
  • 1
    @CarlWalsh - Late answer, but I added now Visual Studio 2022 support. – Matt Oct 12 '22 at 06:56
7

In the Developer command prompt, run the command:

where csc

You get the path to csc which is installed with Visual Studio and net4 like:

C:\Program Files\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Roslyn\csc.exe
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe
M.Hassan
  • 10,282
  • 5
  • 65
  • 84
3

If you already installed Visual Studio, just: Click Start, point to All Programs, point to Microsoft Visual Studio, point to Visual Studio Tools, and then click Visual Studio Command Prompt and there you have your command line box where you compile as follows:

csc PathToYourCsSource
svick
  • 236,525
  • 50
  • 385
  • 514
2

Microsoft has documented this well recently - check here

The csc.exe executable file usually is located in the Microsoft.NET\Framework\ folder under the Windows directory. Its location might vary depending on the exact configuration of a particular computer. If more than one version of the .NET Framework is installed on your computer, you'll find multiple versions of this file.

Naren
  • 797
  • 13
  • 12
1

Necromancing.
This is how they do it in 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" 

But in 2018, you'd be better off using the roslyn built-in compiler:

Here an example:

         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
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
0

More simple way to use csc.exe in Command prompt is that running the following get_csc.bat. You can run csc.exe by %CSC_PATH%.

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