I'm trying to dynamically generate an executable with the CSharpCodeProvider whose only purpose is to invoke one single method from a specific dll. When I execute the generated file I get a BadImageFormatException.
I already set the platform to x86. When I manually write the code which invokes the method and debug it in Visual Studio, it works perfectly fine.
This is the code for the executable:
using DLLName;
namespace ExampleNamespace
{
class Program
{
public static void Main(string[] args)
{
MyClass obj = new MyClass();
obj.MyMethod();
}
}
}
Before dynamically compiling the code I add the assembly via
compilerParameters.ReferencedAssemblies.Add("PathToDLL");
I write the executable to the same directory as the dll.
EDIT
This is the code that I use to invoke the compiler:
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.GenerateInMemory = false;
parameters.OutputAssembly = #"DirectoryOfDLL\MyMethod.exe";
parameters.ReferencedAssemblies.Add("PathToDLL");
provider.CompileAssemblyFromSource(parameters, code);
Related
I'm trying to compile multiple C# files into one executable at runtime and I keep getting compiler errors that aren't present if I run this code from Visual Studio.
The specific errors I'm getting from my runtime compiler is "; expected" and "Method must have a return type" at line 7 in 'Program.cs'. Obviously, I would fix these if there was something to fix.
So the code. Here's the actual compiler code: (with some parts removed, like the actual error reporting)
private CSharpCodeProvider provider = new CSharpCodeProvider();
private CompilerParameters parameters = new CompilerParameters();
private void RunOption_Click(object sender, RoutedEventArgs e)
{
parameters.GenerateInMemory = false;
parameters.GenerateExecutable = true;
parameters.OutputAssembly = Path + "/builds/debug/bot.exe";
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Threading.Tasks.dll");
parameters.CompilerOptions = "/optimize";
parameters.WarningLevel = 3;
parameters.TreatWarningsAsErrors = false;
string[] filesToCompile = Directory.GetFiles(Path + "\\src\\", "*.cs");
CompilerResults results = provider.CompileAssemblyFromFile(parameters, filesToCompile);
}
And here are the two files it's trying to compile.
Program.cs
using System.Threading.Tasks;
namespace MyBot
{
class Program
{
static void Main(string[] args) => new MyBot().RunAsync().GetAwaiter().GetResult();
}
}
MyBot.cs
using System;
using System.Threading.Tasks;
namespace MyBot
{
public class MyBot
{
public async Task RunAsync()
{
Console.Title = "MyBot";
Console.Read();
}
}
}
What am I missing here? As mentioned, if I run the code from 'Program.cs' and 'MyBot.cs' in Visual Studio, everything works fine, so there are no errors in the actual code.
As Eric Lippert pointed out, I was using a newer .NET version than the compiler. So I simply changed the compiler to the new Roslyn compiler that supports C# 6, which I just what I need. So lesson learned: Always make sure you target the right framework version.
OLD ANSWER
Welp, I'm going to declare myself idiot once again. Better to do it myself before anyone else does.
I got it working by changing the Program.cs file.
Instead of
static void Main(string[] args) => new MyBot().RunAsync().GetAwaiter().GetResult();
I did
static void Main(string[] args)
{
new MyBot().RunAsync().GetAwaiter().GetResult();
}
But I still have no idea why it didn't work in the first place since it worked in Visual Studio.
I'm about to elaborate a solution for simplifying a translator tool. Therefore I currently try to automatically compile a Satellite Assembly from within my code.
So what I want to achive is to replace a manual run of the following command:
AL.exe /culture:de /out:de\TestResource.resources.dll /embed:TestResource.de.resources
So far I've tested generating a .dll file, which worked. But embedding/linking an ressource like shown below doesn't has any effect, but expanding the dll's size. So obviously it's there but not usable as if the resulting dll was a Satellite Assembly.
static void Main(string[] args)
{
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = "./output/satellite_test.dll";
parameters.EmbeddedResources.Add(#"./TestResource.en.resources");
parameters.LinkedResources.Add(#"./TestResource.de.resources");
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, "");
}
Is there any way to generate a dll programmatically which contains just the localized resources for one language, so that it's usable as a Satellite Assembly?
Finally I've managed to generate Satellite Assemblies from Code.
Following code generates an appropriate resourcefile:
// Already the resourcefilename has to match the
// exact namespacepath of the original resourcename.
var resourcefileName = #"TranslationTest.Resources.TestResource.de.resources";
// File has to be a .resource file. (ResourceWriter instead of ResXResourceWriter)
// .resx not working and has to be converted into .resource file.
using (var resourceWriter = new ResourceWriter(resourcefileName))
{
resourceWriter.AddResource("testtext", "Language is german!!");
}
Using this resourcefile there are some compileroptions which are necessary:
CompilerParameters parameters = new CompilerParameters();
// Newly created assembly has to be a dll.
parameters.GenerateExecutable = false;
// Filename has to be like the original resourcename. Renaming afterwards does not work.
parameters.OutputAssembly = "./de/TranslationTest.resources.dll";
// Resourcefile has to be embedded in the new assembly.
parameters.EmbeddedResources.Add(resourcefileName);
Finally compiling the assembly there is some required code which has to be compiled into:
// Culture information has to be part of the newly created assembly.
var assemblyAttributesAsCode = #"
using System.Reflection;
[assembly: AssemblyCulture(""de"")]";
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
CompilerResults results = codeProvider.CompileAssemblyFromSource(
parameters,
assemblyAttributesAsCode
);
I have created my project and now want to compile using a CodeDOM compiler.
I have a folder full of the .CS files that should be compiled to an EXE. The application is supposed to be a console application although it fails to launch any console. There are no building errors. The following is my compile method:
public static void Build(string AssemblyName, string OutputDirectory, string[] SourceFiles)
{
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.GenerateInMemory = false;
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Data.dll");
parameters.ReferencedAssemblies.Add("System.Xml.dll");
parameters.OutputAssembly = OutputDirectory + #"\" + AssemblyName + ".exe";
parameters.CompilerOptions = "/unsafe /target:winexe /platform:x86";
if (codeProvider.Supports(GeneratorSupport.EntryPointMethod))
{
parameters.MainClass = "MyApp.Program";
}
CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, SourceFiles);
if (results.Errors.Count > 0)
{
foreach (CompilerError error in results.Errors)
Console.WriteLine(error.ErrorText);
}
}
string[] SourceFiles correctly provides all .CS files (classes, structs and enums) located in the folder like follows:
"D:\\Development\\MyAppCodeDom\\Program.cs"
"D:\\Development\\MyAppCodeDom\\IniParser.cs"
And 26 more of those. I do not use any external DLL files as reference whatsoever. It fails, however, to launch the console window.
Any idea? Perhaps a console application requires certain options?
EDIT:
Using ILSpy, the assembly seems to contain ALL the classes etc it should have.
Thank you in advance.
I removed /target:winexe from the CompilerOptions and now it works.
I have a project where I am dynamically compiling code from a string as such:
public static Assembly BuildItem(Document doc)
{
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults results = provider.CompileAssemblyFromSource(parameters, doc.GetCompileString());
return results.CompiledAssembly;
}
What I'd like to be able to do is take these resulting assembly files and combine them into a single assembly without writing them to disk. I know about ILMerge, and that is currently a fallback plan if I need to do that. Any help is appreciated. Thanks in advance.
When compiling you can pass an array of source files, then they're all in one assembly. e.g
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults results = provider.CompileAssemblyFromSource(parameters, new string [] { source1, source2... })
Alternatively if you really have to call CompileAssemblyFromSource seperately for each source. You could add all the generated assemblies as embedded resources to another assembly using
CSharpCodeProvider provider = new CSharpCodeProvider();
foreach(string assemblyPath in generatedAssemblies)
provider.EmbeddedResources.Add(assemblyPath);
Then...
CompilerResults results = provider.CompileAssemblyFromSource(parameters, source);
...where source is from the following blog which describes how to load assemblies from embedded resources...
http://blogs.msdn.com/b/microsoft_press/archive/2010/02/03/jeffrey-richter-excerpt-2-from-clr-via-c-third-edition.aspx
So, I'm creating this application, that allows you to build a Windows Form Application without having to use the Visual Studio Build, but just doing it through code.
It works fine if I forget about other classes and the fact that the main class has to run "Application.Run();" to another class.
My problem is, that it says following:
Could not find 'RunLauncher' specified for Main method
I have a couple of scripts. The first one being the standard C# Script, that contains the Main method, to run the Windows Form C# Script, via. the Application.Run() method. This Windows Form C# Script, then have another class linked to it as an object (Created in the Load method), so there is basicly 3 scripts in total, that needs to be compiled into the new executable.
The main class "RunLauncher", that runs the method Application.Run() to run the Windows Form Application, runs the Launcher class, which is listed below.
I'm almost positive theres something wrong with the actual code to compile it and find the class, and not these classes, but for the sake of doubts, I've linked them anyway:
Main Class for the new Executable | http://pastebin.com/NU3VYwpv
Launcher Winform C# Class | http://pastebin.com/gQwV923g
The compiling CodeDom code:
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler();
string Output = game_filename.Text + ".exe";
Button ButtonObject = (Button)sender;
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.OutputAssembly = Output;
parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.IO.Compression.dll");
parameters.ReferencedAssemblies.Add("System.IO.Compression.FileSystem.dll");
if (codeProvider.Supports(GeneratorSupport.EntryPointMethod))
{
parameters.MainClass = "RunLauncher";
}
CodeCompileUnit compileUnits = new CodeCompileUnit();
CodeNamespace nsp = new CodeNamespace("kjpUnityGameLauncherTemplate");
compileUnits.Namespaces.Add(nsp);
nsp.Imports.Add(new CodeNamespaceImport("kjpUnityGameLauncherTemplate"));
CodeTypeDeclaration class1 = new CodeTypeDeclaration("RunLauncher");
nsp.Types.Add(class1);
CodeTypeDeclaration class2 = new CodeTypeDeclaration("kjpUnityGameLauncher");
nsp.Types.Add(class2);
CodeTypeDeclaration class3 = new CodeTypeDeclaration("Launcher");
nsp.Types.Add(class3);
CompilerResults results = icc.CompileAssemblyFromDom(parameters, compileUnits);
The problem is that there is no class called RunLauncher in the generated assembly. The class you want is called kjpUnityGameLauncherTemplate.RunLauncher, so that's what you need to set at the MainClass:
parameters.MainClass = "kjpUnityGameLauncherTemplate.RunLauncher";