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
Related
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 want to compile several .cs-files to an executable program.
My CodeDomProvider doesn't find the using declaratives that I wrote into the .cs-files. Especially the followings error Messages are created:
-The type- or namespace 'CodeDom' in the namespace 'System' is not available.
-The type- or namespace 'Windows' in the namespace 'System' is not available.
-The type- or namespace 'Stack' could not be found.
From this function, I'm calling the CodeDomProvider:
private CompileParserSolution()
{
List<string> cSharpFiles = new List<string>();
DirectoryInfo dir = new DirectoryInfo(Path.Combine(_inData.ProjectDir, #"NCParser\NCParser"));
foreach (FileInfo f in dir.GetFiles("*.cs"))
{
cSharpFiles.Add(Path.Combine(dir.FullName, f.Name));
}
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = true;
cp.OutputAssembly = "Parser_TEST.exe";
cp.GenerateInMemory = true;
cp.TreatWarningsAsErrors = false;
cp.ReferencedAssemblies.Add("System.Linq.dll");
cp.ReferencedAssemblies.Add("System.Text.RegularExpressions.dll");
cp.ReferencedAssemblies.Add(Path.Combine(_inData.ProjectDir, #"NCParser\NCParser", #"QUT.ShiftReduceParser.dll"));
CompilerResults cr = provider.CompileAssemblyFromFile(cp, cSharpFiles.ToArray());
}
My question is, how can I include the System.CodeDom, System.Windows and the System.Collections.Stack librarys into the project to compile.
With the command:
cs.ReferencedAssemblies.Add("System.CodeDom.dll");
...
it doesn't work!
Assemblies and namespaces don't correspond to each other 1:1. To find in which assembly a certain type is, look at its MSDN documentation.
There is no System.CodeDom.dll, a lot of the CodeDOM types are in the System.dll, which you didn't reference.
Stack<T> is likewise in System.dll.
WPF is spread over several assemblies, the basic types are in PresentationFramework.dll.
Referencing these assemblies will likely fix your issue.
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'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);
I have this code:
static void Main(string[] args)
{
CompilerParameters cp = new CompilerParameters
{
GenerateInMemory = true,
IncludeDebugInformation = false,
};
cp.ReferencedAssemblies.AddRange(new string[]{
"System.dll",
"System.Data.dll",
"System.Xml.dll",
"Microsoft.mshtml.dll",
"System.Windows.Forms.dll"
});
Assembly _assembly = Assembly.GetExecutingAssembly();
StreamReader _textStreamReader = new StreamReader(_assembly.GetManifestResourceStream("myprog.restext.txt"));
string src = _textStreamReader.ReadToEnd();
byte[] code = Convert.FromBase64String(src);
src = Encoding.UTF8.GetString(code);
CompilerResults cr = CSharpCodeProvider.CreateProvider("CSharp").
CompileAssemblyFromSource(cp, src);
Assembly asm = cr.CompiledAssembly;
Type typ = asm.GetType("clicker.Program");
MethodInfo method = typ.GetMethod("DoStart");
method.Invoke(null, new[] { (object)args });
}
I thows FileNotFoundException becouse CompileAssemblyFromSource returns the same error. Source using mshtml.
Then I'm trying to compile it using csc.exe, it says:
error CS0006. (no Metadata for "Microsoft.mshtml.dll")
I think it because mshtml is ActiveX library. So The question is how to assemble source usings activeX mshtml.
p.s.
Source has no errors and successfully has compiled from VS but can't be compiled by "on the fly" compilation.
I thows FileNotFoundException
That's normal, Microsoft.mshtml.dll is a primary interop assembly. It is not part of the .NET Framework so cannot be located automatically. It also won't be available on the user's machine, PIAs have to be installed.
The best way to go about it is to ensure that the assembly is present in your build directory so it will be deployed along with your program and can always be found. Project + Add Reference, select Microsoft.mshtml. Select it from the References node and set the Isolated property to False, Copy Local to True. Rebuild and verify that you now have Microsoft.mshtml.dll present in your bin\Debug directory.
And modify your code to pass the full path name to the file. Like this:
var referenceAssemblies = new List<string> {
"System.dll",
"System.Data.dll",
"System.Xml.dll",
"System.Windows.Forms.dll"
};
var homedir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var mshtml = Path.Combine(homedir, "Microsoft.mshtml.dll");
referenceAssemblies.Add(mshtml);
cp.ReferencedAssemblies.AddRange(referenceAssemblies.ToArray());