CodeDom not linking main class - c#

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";

Related

Reading txt file and import as a c# code in a class

I wonder to know if it's possible. For example bootstrap has new version and i need to change cdn url and re-build project for RegisterBundles in asp.net web forms. It's simple url that we don't need to re-build project for that issue.
Is it possible that reading a *.txt file and using as c# code in a class. Class will be same as before and we will survive.
Example RegisterBundles code:
public class stylescriptbundle
{
public void RegisterBundles(BundleCollection bundles)
{
BundleTable.Bundles.Add(new ScriptBundle("/mybundle").Include(
"https://code.jquery.com/jquery-3.4.1.slim.min.js",
"https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js",
"https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
);
BundleTable.Bundles.UseCdn = true;
}
}
First, similar to what robbpriestley said, what you should do is read the strings from a file so you can include them in the bundle. Configuration file is best, but if you're hellbent on a .txt, try below:
var scripts = File.ReadAllLines("someConfigurationFile.txt");
var bundle = new ScriptBundle("/myBundle");
foreach(var script in scripts)
{
bundle.Include(script);
}
BundleTable.Bundles.UseCdn = true;
BundleTable.Bundles.Add(bundle);
(the above is just off the top of my head, you may have to tweak it to get it to work.)
That said, if you still want to compile code at run time, you can use the Microsoft.CSharp and Microsoft.CodeDom.Compiler namespaces, according to this tutorial. I'll try and summarize it here, for archival reasons.
Get your code into a string: var codeStr = File.ReadAllText("runtime compiled.cs");
Create a provider and compiler: var provider = new CSharpCodeProvider(); var parameters = new CodeParameters();
Define parameters of the compiler. It sounds like you'll definitely want to compile it into memory, and you'll need to reference any assemblies you use in that code: parameters.GenerateInMemory = true; parameters.ReferencedAssemblies.Add("necessaryAssembly.dll");
compile: var results = provider.CompileAssemblyFromSource(parameters, code);
Check errors
Get your assembly: var assembly = results.CompiledAssembly; so you can get your type: var program = assembly.GetType("first.program"); so you can get your method: var main = program.GetMethod("Main");
And then you can call your method with Invoke: main.Invoke(null, null); (check out reference on MethodInfo.)
Don't use a TXT file for this. Put the strings as settings into your web.config and then use the provided ASP.NET ConfigurationManager to reference them.
For example, see: https://stackoverflow.com/a/12892083/1348592

Is it possible to generate Satellite Assemblies from within code?

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
);

badimageformatexception dynamic code generation

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);

How to dynamically create a class derived from an interface and then preserve it for later use [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I'm writing a program that would have the possibility to learn for itself.
Example:
a word 'day' was typed
look for interface named 'day'
does it exists?
No => create class based on that interface (and implement the interface) and save it for next use then create instance of that class
Yes => create instance of that class
Google gave me this: How to dynamically create a class in C#?
This solution is the closest I can think of for my scenario, but it assumes you already know how many properties you will need and doesn't implement an interface at all.
I have no experience at all with system.reflection but I'm eager to learn!
Anyone know an example for my case?
Any help is appriciated.
Thank You!
EDIT: MY SOLUTION
(Because nobody gave me a straight answer I figured it out myself)
public void createObject(string name)
{
//Namespace where the interfaces are located
string strnamespace = "Intelligence.Omnia.Categories";
//Get interfacecollection
List<Type> interfaceCollection = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsInterface && t.Namespace == strnamespace).ToList();
foreach (Type myinterface in interfaceCollection)
{
//interface names
List<string> interfaceNames = new List<string>();
if (myinterface.Name == name)
{
//Add interface name
interfaceNames.Add(myinterface.Name);
//Add current interfaceproperties
List<PropertyInfo> myProps = myinterface.GetProperties().ToList();
//Does the current interface inhiretes from other interfaces?
foreach (Type inhiretences in myinterface.GetInterfaces())
{
//Add interface name
interfaceNames.Add(inhiretences.Name);
//Add those properties aswell!
foreach (PropertyInfo pi in inhiretences.GetProperties())
{
myProps.Add(pi);
}
}
createType(name, myProps, interfaceNames);
}
}
}
static void createType(string name, List<PropertyInfo> props, List<string> interfacesnames)
{
//create instance of CSharpCodeProvider
CSharpCodeProvider csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
//DLL
string pathDLL = AppDomain.CurrentDomain.BaseDirectory + "Objects.dll";
CompilerParameters parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.dll", "System.Linq.dll", "System.Threading.Tasks.dll", "Intelligence.dll" });
parameters.OutputAssembly = pathDLL;
parameters.GenerateExecutable = false;
ICodeCompiler icc = csc.CreateCompiler();
//>>>>Generated CODE
//Add namespaces
CodeCompileUnit compileUnit = new CodeCompileUnit();
CodeNamespace ns = new CodeNamespace("Objects");
compileUnit.Namespaces.Add(ns);
ns.Imports.Add(new CodeNamespaceImport("System"));
ns.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
ns.Imports.Add(new CodeNamespaceImport("System.Linq"));
ns.Imports.Add(new CodeNamespaceImport("System.Text"));
ns.Imports.Add(new CodeNamespaceImport("System.Threading.Tasks"));
ns.Imports.Add(new CodeNamespaceImport("Intelligence"));
ns.Imports.Add(new CodeNamespaceImport("Intelligence.Omnia"));
ns.Imports.Add(new CodeNamespaceImport("Intelligence.Omnia.Categories"));
//Define your class
CodeTypeDeclaration classType = new CodeTypeDeclaration("Object"+name);
classType.Attributes = MemberAttributes.Public; //make it public
foreach(string interfaceName in interfacesnames) //let it inherit from the interfaces
{
classType.BaseTypes.Add(interfaceName);
}
ns.Types.Add(classType);
//Add constructor
CodeConstructor constr = new CodeConstructor();
constr.Attributes = MemberAttributes.Public;
classType.Members.Add(constr);
//Add all the properties
foreach (var prop in props)
{
//If you want private fields
//CodeMemberField field = new CodeMemberField(prop.PropertyType, prop.Name);
//classType.Members.Add(field);
CodeMemberProperty property = new CodeMemberProperty();
property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
property.Type = new CodeTypeReference(prop.PropertyType);
property.Name = prop.Name;
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), prop.Name)));
property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), prop.Name), new CodePropertySetValueReferenceExpression()));
classType.Members.Add(property);
}
//Write the file for later use
TextWriter tw = new StreamWriter(new FileStream(AppDomain.CurrentDomain.BaseDirectory + "Objects\\" + name + ".cs", FileMode.Create));
csc.GenerateCodeFromCompileUnit(compileUnit, tw, null);
tw.Close();
//Compile the class
CompilerResults results = icc.CompileAssemblyFromFile(parameters, AppDomain.CurrentDomain.BaseDirectory + "Objects\\" + name + ".cs");
results.Errors.Cast<CompilerError>().ToList().ForEach(error => System.Windows.Forms.MessageBox.Show(error.ErrorText));
}
As #Stefan says you can dynamically create type using the DLR built into .net 4.0+ but you can also use the older reflection based mechanism in CodeDom. My suggestion is to look at IronPython as this can do what you want really easily. However if you want C#, then you need to understand that C# is compiled and you need to use the compilation in System.CodeDom.Compiler.
Nothing can infer knowledge from word Day to what the interface Day is - you have to supply this is some way. However if you know the rules by which the interface will exist, you can create it dynamically. You can also go further and create additional types based on the interface. Again no code can be written magically - you need to supply the code semantics and syntax.
However if you have this you could separate the code into multiple assemblies (CompileAssemblyFromSource). Then dynamically load the assemblies to load the types (step 2). You can create types and assemblies at runtime (see OS: Generating DLL assembly dynamically at run time).
This code is from the above linked SO answer, and shows you how to an assembly from some string of code.
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 = "My_Assembly_Day.dll";
CompilerResults results = icc.CompileAssemblyFromSource(parameters, ".... some C# code ....");
This SO answer shows you how to load assemblies to discover types:
https://stackoverflow.com/a/14184863/30225
edit: To answer comments.
You can name your assembly anything you like - my suggestion is to name it with interface/class combination. The above is just an example of how you can name it
You can either load the existing (pre created assemblies) at load time (first step in the program), or you can dynamically load the assemblies at runtime when needed.
Once you've loaded the assemblies the types are available to you (Activator.CreateInstance) for example.
If you get to your step and for instance type IDay is not available, you can dynamically create it using type text. Of course to get the CodeDom compiler to work, you'll need to ensure that all the things that this code references is supplied to the compilation unit. And compile that into an assembly on the disk. You can create the interface & the class that same time or in two steps.
Once step 4 is done you can load that assembly like in step 3 & 4.
at the end of the day the answer boils down to:
Find the existing type from existing types set
If existing type is not available create it in an assembly dynamically.
Load that type
Instantiate that type.
The concern comes when using that type. You can either create more code using CodeDom (in effect this gives you the ability to recreate classes that may already exist BTW) or you can use types dynamically in your code. The first has the compilation overhead, while the second has the complexity in writing code that doesn't have hard coded types - a job made very simple btw using the C# dynamic keyword. Either way this is a very run of the mill .net coding technique when using types dynamically, and many existing applications use techniques like this for managing plugins.
Caveat:
Please remember that the smallest unloadable unit in .net is the AppDomain. When you load assemblies you load them into an AppDomain. If you want unload assemblies (and thus types) to replace them fo instance, you need to unlaod the AppDomain. This means that you need to ensure that any dynamically loaded assemblies are loaded into new AppDomains which in turn can be unloaded when needed.

Can't find suitable Main method

I'm trying to create a Windows Form Application, that can create another Windows Form Application. But the error i'm getting when i'm trying to compile with CodeDom in the c# code, is a weird one.
'kjpUnityGameLauncherTemplate.RunLauncher' does not have a suitable static Main method
This kinda confuses me, since the class "RunLauncher" DOES have a main method, with the default setup described at the (http://msdn.microsoft.com/) site.
RunLauncher class: http://pastebin.com/NU3VYwpv (which have the main method)
The code i'm using to actually compile this via. CodeDom is this:
if (codeProvider.Supports(GeneratorSupport.EntryPointMethod))
{
parameters.MainClass = "kjpUnityGameLauncherTemplate.RunLauncher";
}
CodeCompileUnit compileUnits = new CodeCompileUnit();
CodeNamespace nsp = new CodeNamespace("kjpUnityGameLauncherTemplate");
parameters.CompilerOptions = "/main:kjpUnityGameLauncherTemplate.RunLauncher";
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);
nsp.Imports.Add(new CodeNamespaceImport("kjpUnityGameLauncherTemplate"));
compileUnits.Namespaces.Add(nsp);
CompilerResults results = icc.CompileAssemblyFromDom(parameters, compileUnits);
Theres some other stuff like declaration of the variables "codeProvider" etc. but those aren't the problem in this case, which is why I didn't include them.
To Create an Executable Your code must have an Entry Point Method declared and set properly to run in CodeDom. I do not see one declared in your example above. Below I have an example from MSDN located at
http://msdn.microsoft.com/en-us/library/y2k85ax6.aspx
CodeEntryPointMethod start = new CodeEntryPointMethod();
CodeMethodInvokeExpression cs1 = new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression("System.Console"),
"WriteLine", new CodePrimitiveExpression("Hello World!"));
start.Statements.Add(cs1);

Categories