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);
Related
I logged onto my Visual Studio and started to make some unit tests for a project and I noticed that in order to make an instance of a class from another project (my main project) I have to add the the namespace before the class. It wasn't like this yesterday. It's kind of bugging me. So was there an update overnight or did I change something in my IDE?
var foo = new Something.Something(); // <-- This is what I have to do now in projects
var foo = new Something(); // <-- This is what I was doing for the past year
If it is just me could you tell me how to fix this.
var g1 = new GradeBook.GradeBook();
var g2 = g1;
g1 = new GradeBook.GradeBook();
g1.Name = "Jakub's GradeBook";
Assert.AreEqual(g1.Name, g2.Name);
If I get rid of the GradeBook namespace the compiler throws out an error.
var g1 = new GradeBook();
var g2 = g1;
g1 = new GradeBook();
g1.Name = "Jakub's GradeBook";
Assert.AreEqual(g1.Name, g2.Name);
When I add a using directive it still throws out errors - missing reference in file
The problem is, that both your class and its namespace have the same name.
Making the compiler unable to tell which one you are referring to. Hence you'll get an error similiar like GradeBook is a namespace but is used like a type
It should work if you write
var x = new GradeBook.GradeBook();
You can work arround this by using an alias for your using like so:
using G=GradeBook;
And then write
var x = new G.GradeBook();
But I`d recommend you to reevaluate your naming.
No.
I think you are missing the namespace to your project containing Something() class.
Import your project as follow:
using Something;
And then you will be able to use your class without having to add the project name before it, like:
var foo = new Something();
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.
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";
I found that C# didn't directly support ini files, so I went on the prowl and found the following library called ini-parser. The usage shows an extremely simple example, but for some reason I can't get Visual studio 2010 to like it. If I copy the following exactly from the wiki on their web page:
IniParser.FileIniDataParser parser = new FileIniDataParser();
IniData parsedData = parser.LoadFile("TestIniFile.ini");
I get the following error, with the parser part of parser.LoadFile() underlined and the following error:
Error 1 A field initializer cannot reference the non-static field,
method, or property
'WindowsFormsApplication1.Form1.parser' C:\Users\Support\Documents\Visual
Studio
2010\Projects\WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs 28 30 WindowsFormsApplication1
I'm not sure what to make of what it's saying, or how to fix it. Can someone else offer up a suggestion/solution?
You're trying to do this in a field initializer. You're not allowed to refer to this within an instance field initalizer. Do it in the constructor instead:
private readonly IniData configuration;
public Form1()
{
InitializeComponent();
IniParser.FileIniDataParser parser = new FileIniDataParser();
configuration = parser.LoadFile("TestIniFile.ini");
}
Or just do it inline without a separate variable for the parser at all:
private readonly IniData configuration =
new FileIniDataParser().LoadFile("TestIniFile.ini");
(I'm assuming you don't actually need the parser for anything else, so it's pointless using a field for it.)
The error message is basically saying that you can't use parser (another field) within a field initializer of the class (the initializer for parsedData).
You have to put this logic in the constructor:
FileIniDataParser parser = new FileIniDataParser();
IniData parsedData;
public Form1()
{
InitializeComponent();
parsedData = parser.LoadFile("TestIniFile.ini");
}
I have a string variable contain:
string classCode = "public class Person { public string Name{get;set;} }";
How can I create an instance of an object from the classCode ?
like
object obj = CreateAnInstanceAnObject(classCode);
You'll need to use CodeDom to compile an in-memory assembly, and then use reflection to create the type.
Here's a sample article on MSDN that walks through the process of code generation.
Once you've compiled the code, you can use Activator.CreateInstance to create an instance of it.
Building on the answers from above, here is a working demo to generate, compile and instantiate a class from an in-memory assembly:
namespace DynamicCompilation
{
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
internal static class Program
{
private static void Main()
{
var ccu = new CodeCompileUnit();
var cns = new CodeNamespace("Aesop.Demo");
cns.Imports.Add(new CodeNamespaceImport("System"));
var ctd = new CodeTypeDeclaration("Test")
{
TypeAttributes = TypeAttributes.Public
};
var ctre = new CodeTypeReferenceExpression("Console");
var cmie = new CodeMethodInvokeExpression(ctre, "WriteLine", new CodePrimitiveExpression("Hello World!"));
var cmm = new CodeMemberMethod
{
Name = "Hello",
Attributes = MemberAttributes.Public
};
cmm.Statements.Add(cmie);
ctd.Members.Add(cmm);
cns.Types.Add(ctd);
ccu.Namespaces.Add(cns);
var provider = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
CompilerOptions = "/target:library /optimize",
GenerateExecutable = false,
GenerateInMemory = true
};
////parameters.ReferencedAssemblies.Add("System.dll");
var results = provider.CompileAssemblyFromDom(parameters, ccu);
if (results.Errors.Count == 0)
{
var t = results.CompiledAssembly.GetType("Aesop.Demo.Test");
var inst = results.CompiledAssembly.CreateInstance("Aesop.Demo.Test");
t.InvokeMember("Hello", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, inst, null);
}
Console.ReadLine();
}
}
}
Simple put you cannot do this in one line as you are attempting. It is possible to create an instance of an existing class via it's name and one of the overloads of Activator.CreateInstance.
What you are trying to achieve here though is quite different. You are attempting to both 1) define a new class type and 2) create an instance of it. Defining new metadata in the running process dynamically is very difficult to achieve with static languages like C#. It requires a significant amount of work that can't easily be put into a StackOverflow answer.
The following project should guide you in what your trying to accomplish:
RunTime Code Compilation
However, if you are attempting to write code at runtime, you may want to rethink your architecture. You may be creating more of a headache for yourself than you need to be.
What are you trying to accomplish by creating this object?