I can execute a C# source from PowerShell and a PowerShell source from C#.
The question is, How can I execute a C# source from a C# program without compiling with csc.exe?
Yes. This is explicitly catered for in the .net framework using the CodeDom class namespace. http://msdn.microsoft.com/en-us/library/650ax5cx(v=vs.110).aspx System.CodeDom and System.CodeDom.Compiler.
(from the documention)
CSharpCodeProvider provider = new CSharpCodeProvider();
// Build the parameters for source compilation.
CompilerParameters cp = new CompilerParameters();
// Add an assembly reference.
cp.ReferencedAssemblies.Add( "System.dll" );
// Generate an executable instead of
// a class library.
cp.GenerateExecutable = true;
// Set the assembly file name to generate.
cp.OutputAssembly = exeFile;
// Save the assembly as a physical file.
cp.GenerateInMemory = false;
// Invoke compilation.
CompilerResults cr = provider.CompileAssemblyFromFile(cp, sourceFile);
I realise this does use the compiler internally, which is something the OP wished to avoid, but I can't see any reason not to use this to .
Related
I'm compiling a C# assembly at runtime within my program using CSharpCodeProvider, which depends on a few libraries which are built as .dlls, and also the program that's building it.
I'd ideally like to only build one executable, and not have to copy around all the dependencies with it.
Here's the code I'm using to compile the assembly:
//Create the compiler (with arguments).
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = true;
cp.OutputAssembly = "example.exe";
cp.GenerateInMemory = false;
//Reference the main assembly (this one) when compiling.
Assembly entryasm = Assembly.GetEntryAssembly();
cp.ReferencedAssemblies.Add(entryasm.Location);
//Reference an external assembly it depends on.
cp.ReferencedAssemblies.Add("someExternal.dll");
//Attempt to compile.
CompilerResults results = provider.CompileAssemblyFromSource(cp, someScript);
The executable this produces still needs someExternal.dll and the program that built it in the same directory when run, and I'd prefer it to be a single executable with all dependencies included.
Is there any way to do this?
In the end, I ended up using the ILRepack.Lib NuGet package for the job, which allows you to merge binaries programmatically, without using a command line tool.
Here's the code I'm using to pack all .dll files in the directory into the executable:
//Setting required options.
RepackOptions opt = new RepackOptions();
opt.OutputFile = "example_packed.exe";
opt.SearchDirectories = new string[] { AppDomain.CurrentDomain.BaseDirectory, Environment.CurrentDirectory };
//Setting input assemblies.
string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
opt.InputAssemblies = new string[] { "example.exe", entryasm.Location }.Concat(files).ToArray();
//Merging.
ILRepack pack = new ILRepack(opt);
pack.Repack();
Currently I have some code that is being generated dynamically. In other words, a C# .cs file is created dynamically by the program, and the intention is to include this C# file in another project.
The challenge is that I would like to generate a .DLL file instead of generating a C# .cs file so that it could be referenced by any kind of .NET application (not only C#) and therefore be more useful.
using System.CodeDom.Compiler;
using System.Diagnostics;
using Microsoft.CSharp;
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler();
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = "AutoGen.dll";
CompilerResults results = icc.CompileAssemblyFromSource(parameters, yourCodeAsString);
Adapted from http://support.microsoft.com/kb/304655
The non-deprecated way of doing it (using .NET 4.0 as previous posters mentioned):
using System.CodeDom.Compiler;
using System.Reflection;
using System;
public class J
{
public static void Main()
{
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = "AutoGen.dll";
CompilerResults r = CodeDomProvider.CreateProvider("CSharp").CompileAssemblyFromSource(parameters, "public class B {public static int k=7;}");
//verify generation
Console.WriteLine(Assembly.LoadFrom("AutoGen.dll").GetType("B").GetField("k").GetValue(null));
}
}
Right now, your best bet is CSharpCodeProvider; the plans for 4.0 include "compiler as a service", which will make this fully managed.
An approach above doesn't seems work anymore (I've tried it from .Net core and .net framework 4.7.2 targeting project). I was getting "PlatformNotSupportedException" and to undestand, I've dived into source code and found this piece. And here is the explanation from them why it doesn't work and how to deal with this (link and documentation).
To be short (spoiler): I went to 1st link from there and it helped me. So, you can do it as described here: how-to-compile-a-c-sharp-file-with-roslyn-programmatically. It may be treated as an possibe answer.
I am aware of a class called AssemblyBuilder, and I would have thought I could use it to pass a folder containing C# source files, or pass a single C# source file to it in order to compile the source into an assembly (.dll) which can then be referenced in a config file.
I'm aware of csc.exe which can compile C#, and I'm effectively looking for a way to replicate this dynamically.
I couldn't figure out how to use AssemblyBuilder, or whether this is the wrong class to be using, or whether I should be doing something similar to the following:
http://support.microsoft.com/kb/304655
Can you point me in the right direction please.
You might want to look into CodeDomProvider
Example snippet:
CompilerParameters parms = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true,
IncludeDebugInformation = false
};
parms.ReferencedAssemblies.Add("System.dll");
parms.ReferencedAssemblies.Add("System.Data.dll");
CodeDomProvider compiler = CSharpCodeProvider.CreateProvider("CSharp");
return compiler.CompileAssemblyFromSource(parms, source);
Warning: assemblies built dynamically in this fashion won't be handled by the garbage collector.
I made a script tool using the .NET integrated compiler.
Shortly, at runtime it compiles a source file to a assembly, creates a object defined in the assembly and starts a method of the object.
Here's the code (heavily simplified):
CodeDomProvider provider = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = false;
cp.GenerateInMemory = true;
// ...
CompilerResults res = provider.CompileAssemblyFromSource(cp, source);
// ...
return res.CompiledAssembly;
This can happen multiple times during the tool is running.
So far so good, it's working great.
Problem:
The compiled assembly is loaded into my process, the debugger tells me this:
'ScriptTool.exe' (Managed): Loaded 'v7wyfy7w', No symbols loaded.
This happens every time i 'run' a script. So after a while, there are a lot of assemblies loaded into the process, killing memory.
Now the question is:
Is it possible to unload the assemblies?
I'm stuck on run-time compilation and CodeDom.
Here's a simplified example of what I have so far.
public static void Testing()
{
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
string Output = "Out.exe";
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.OutputAssembly = Output;
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Drawing.Dll");
parameters.ReferencedAssemblies.Add("System.Windows.Forms.Dll");
parameters.CompilerOptions = "/t:winexe";
string[] text = new string[] { #"C:\MyProject\Test.cs", #"C:\MyProject\Test.Designer.cs",
#"C:\MyProject\Program.cs"};
CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, text);
Process.Start(Output);
}
It works perfectly alright, and loads the Test form.
But! I need to pass a parameter to this Test form (a list of Panel controls) to populate the form.
How can I do this? Maybe, I am looking in the wrong direction, and it has to be done in a different way?
Thanks a lot in advance!
EDIT
In the end, I give up on CodeDom and used Mono.Cecil instead, injecting .exe file with information from my main program.
What you are doing is compiling an executable assembly then starting it in another process.
If you want to pass it information, command line arguments are one option. However, passing a .Net object on the command line will not work.
If you want to pass somthing managed you will have to use your new assembly with some late binding and pass your object to the constructor perhaps, rather depends what the code you are compiling accepts, if you have that at design time ...
Are you re-writing Visual Studio?