I have WinForm called Form1 and there are Button1, MemoEdit1 and 2 TextBoxes named TextBox1 and TextBox2. At runtime user should be able write C# code in MemoEdit1 in order to manipulate the TextBox controls. F.e: at runtime user typed into MemoEdit1 simple code like: TextBox2.Text = "Hello" + TextBox1.Text;
So, when I click on Button1, I need to compile and execute the code.
Question may sound so simple as I am a newbie in compiling/executing code during runtime in C#.
Could you pls, help?
Thanks.
Take a look on this snippet
public class Evaluator
{
public void Eval(string Code)
{
Microsoft.CSharp.CSharpCodeProvider Provider = new Microsoft.CSharp.CSharpCodeProvider(); // Create an provider
System.CodeDom.Compiler.ICodeCompiler Compiler = Provider.CreateCompiler(); // Create An Compiler
System.CodeDom.Compiler.CompilerParameters Parameters = new System.CodeDom.Compiler.CompilerParameters(); // Create a parameters of the compiler
Parameters.GenerateInMemory = true; // It should generate the compiled assembly in the memory
System.CodeDom.Compiler.CompilerResults Results = Compiler.CompileAssemblyFromSource(Parameters, Code); //Compile it
///Now you just need to use reflection to call its methods
object SomeClass = Results.CompiledAssembly.CreateInstance("ClassName"); //Name of the class you want to create an instance
var Method = SomeClass.GetType().GetMethod("MethodName"); //Name of the Method you want to call
Method.Invoke(SomeClass, null); // change null for the argument it needs
}
}
if you want to just write code you will have to add the an class and a Method to wrap the user code and then you call it through the Invoke, you will probably have to reference your own assembly into this assembly
Related
I want simply use io.py from C# to write a file and I use the following code:
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
...
System.IO.Directory.SetCurrentDirectory(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) +
"\IronPython\Lib");
ScriptRuntime py = Python.CreateRuntime();
dynamic io = py.UseFile("io.py");
dynamic f = io.open("tmp.txt", "w");
f.writelines("some text...");
f.close();
but when I run the program the runtime give me a:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException telling that no overload of writelines accept argument '1'
it seems like that method doesn't exists... but into io.py documentation it exists.
P.S. The same is for close method!!!
Any idea?
I can only tell you how to make your code working, however I don't have much experience with IronPython and have no idea why it is done this way (though I try to learn that). First, it seems io module is treated in a special way and there is special (non-dynamic) class for that. When you do io.open what is returned is instance of PythonIOModule._IOBase class. You can do
var f = (PythonIOModule._IOBase) io.open("tmp.txt", "w");
And see for yourself that "writeline" method (which is regular method, not a dynamic one) accepts CodeContext instance as first argument, and second argument is lines. Interesting that this class itself already contains field with that CodeContext, but it is made internal for some reason, and what is even worse - writelines (and other methods) could have been using that CodeContext and not require us to provide external one. Why is it done like this - I have no idea.
So to make your code working, we have to get CodeContext somewhere. One way is do that via reflection:
var context = (CodeContext) f.GetType().GetField("context", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(f);
Another way is to craft it yourself:
var languageContext = HostingHelpers.GetLanguageContext(engine);
var context = new ModuleContext(io._io, new PythonContext(languageContext.DomainManager, new Dictionary<string, object>())).GlobalContext;
Both methods will work and program will successfully write to a file. Full working sample:
static void Main(string[] args) {
System.IO.Directory.SetCurrentDirectory(#"G:\Python27\Lib");
var engine = Python.CreateEngine();
dynamic io = engine.ImportModule("io");
var f = (PythonIOModule._IOBase) io.open("tmp.txt", "w");
var languageContext = HostingHelpers.GetLanguageContext(engine);
var context = new ModuleContext(io._io, new PythonContext(languageContext.DomainManager, new Dictionary<string, object>())).GlobalContext;
f.writelines(context, "some text....");
f.close(context);
}
I am developing an Desktop Application in WPF using C# .
For the sake of simplicity, Assume my Application has functions which draw lines in said direction goleft() , goright() , goforward() , goback() .
when any of these function is called a line of one inch will be drawn on screen.
I want to make application where user will write code in a file in any editor (say notepad) and save that file in some fixed format (say .abc or .xyz)
Imaginary Example :
(section_start)
For(int i = 0 ; i<= 20 ; i++ )
{
if(i<5)
goforward();
else if(i==5)
goleft();
else if(i < 10)
forward();
.......
........
}
(section_End)
Can i make application which should be capable of reading this file and execute code which is written in between of (section_start) and (section_End). and only if possible can check for syntax errors too... (Not compulsory).
Please guide me on this issue :
Disclosure : My actual Application is somewhat else and could not discuss here due to my company's rules.
Thanks to all who replied to my question. Stackoverflow is fantastic site , i have found the roadmap where to go , till today morning i did not have any clue but now i can go ahead , thanks all of you once again
Will ask question again if i get stucked somewhere
You can read the file content using FileInfo and get the code you need to execute.
Then you can execute the code using CSharpCodeProvider like in this post:
using (Microsoft.CSharp.CSharpCodeProvider foo =
new Microsoft.CSharp.CSharpCodeProvider())
{
var res = foo.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true
},
"public class FooClass { public string Execute() { return \"output!\";}}"
);
var type = res.CompiledAssembly.GetType("FooClass");
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
}
You can choose CodeDOM or IL Emit
More help on CodeDOM
More information on IL Generator / Emit
This is called scripting your application if I understand correctly. C# does not support this out of the box. One thing to look into could be the new Roselyn compiler from Microsoft (it is a new take on the C# compiler, which lets you do just this).
For more info on Roselyn check out:
http://blogs.msdn.com/b/csharpfaq/archive/2011/12/02/introduction-to-the-roslyn-scripting-api.aspx
I've only seen a demo of it, but it looks very promissing, and should solve your problem.
It's not clear what kind of code you want compiled but here is a guide on how to compile code code with C#.
You could use IronPython to handle the script.
Here's an example of how to do this:
First you need a navigation object to perform the operations on:
public class NavigationObject
{
public int Offset { get; private set; }
public void GoForwards()
{
Offset++;
}
public void GoBackwards()
{
Offset--;
}
}
Then the code to execute the file:
public void RunNavigationScript(string filePath, NavigationObject navObject)
{
var engine = Python.CreateEngine();
var scope = engine.CreateScope();
scope.SetVariable("navigation", navObject);
var source = engine.CreateScriptSourceFromFile(filePath);
try
{
source.Execute(scope);
}
catch(Exception
{
}
}
The script file can then take the form of something like:
for x in range(0,20):
if x == 5:
navigation.GoBackwards()
else:
navigation.GoForwards()
I have a main form that is launched and then it can go to any of the other forms I have created. But the kicker is that I have written a class that I call that returns a string with the name of the form to go to.
Currently I don't have this working so I am going from form to form like this (statically written linking code):
this.Hide();
CloudAccess nextForm1 = new CloudAccess();
//Where CloudAccess is the class of the next form.
nextForm1.ShowDialog();
What I want is something like this:
FormController pick = new FormController();
//Where FormController is the Class I create an object of and ask what's next
string next = pick.whereToGo(); //lets say it returns "CloudAccess"
this.Hide();
next nextForm1 = new next(); //next is desired to be the contents of the string
nextForm1.ShowDialog();
The problem is that I don't know how to use the returned string to make the new object and use it. I've been looking at Invoke and Reflection topics like this one: Use string value to create new instance
But I'm new to C# and I'm not sure how to apply that to this scenario.
Thoughts? Thanks!
Here's the working code from what fejejosco said:
string asdf = "CloudAccess";
Type CAType = Type.GetType("namespace." + asdf);
Form nextForm2 = (Form)Activator.CreateInstance(CAType);
nextForm2.ShowDialog();
Thanks!
Get the type of the form: Type.GetType("name.space." + formname), so if your class is name.space.CloudAccessForm, then pass in CloudAccessForm as formname, and it will give you the type. Then you can instantiate it with Activator.CreateInstance(type). Then cast it to a Form, and show it.
I have an event like this:
private void btnStartAnalysis_Click(object sender, EventArgs e)
{
SqlConnectionStringBuilder objConnectionString = new SqlConnectionStringBuilder();
objConnectionString.DataSource = txtHost.Text;
objConnectionString.UserID = txtUsername.Text;
objConnectionString.Password = txtPassword.Text;
objConnectionString.InitialCatalog = Convert.ToString(cmbDatabases.SelectedValue);
string[] arrArgs = { objConnectionString.ConnectionString };
//Checks for the selectedItem in the cmbOpearions dropdown and make call to appropriate functions.
string assemblyName = cmbOperations.SelectedValue.ToString();
Assembly assembly = Assembly.LoadFrom(assemblyName);
Type localType = assembly.GetType("PrimaryKeyChecker.PrimaryKeyChecker");
IMFDBAnalyserPlugin analyser = (IMFDBAnalyserPlugin) Activator.CreateInstance(localType);
string response = analyser.RunAnalysis(objConnectionString.ConnectionString);
//show the response of the the function call
txtPluginResponse.Text = response;
}
I want this line to be dynamic:
Type localType = assembly.GetType("PrimaryKeyChecker.PrimaryKeyChecker");
where PrimaryKeyChecker is a namespace and another PrimaryKeyChecker is the class.
But I want to create other namespaces and classes, so is there any way to call them dynamically and load them in the combobox like this.
public void SetOperationDropDown()
{
cmbOperations.DataSource = PluginManager.GetAllPlugins();
if(cmbOperations.Items.Count > 0)
{
cmbOperations.SelectedItem = cmbOperations.Items[0];
}
}
You've almost answered your own question! Assuming you have a list of plugins, configured in a config file or whatnot, then your PluginManager can load up the Types from the assembly using code similar to:
Type analyserType = typeof(IMFDBAnalyserPlugin);
foreach(Type t in assembly.GetTypes()) {
if(t.IsSubtypeOf(analyserType) {
plugins.Add((IMFDBAnalyserPlugin) Activator.CreateInstance(t));
}
}
If you do not have a list of plugins, then you can either scan a directory and do the same thing as above. You could also consider using a plugin framework architecture like MEF and it does a lot of that work for you and discovers the assemblies and plugins at runtime.
I think the answer of Tom can help you populate a list of plugins. Bind them to the combobox where you put the text / description to the Type name and bind the value of combo-items to the actual Type declaration. And you asked for the event to be "Dynamic"...Do you probably mean generic??? Then i would advice to refactor the code in the click_event to a private method, to be able to call it from other "places" as well. Then in the click_event you retrieve the selected Plugin Type from the currently selected item a provide this in the generic function call to RunAnalysis like this:
private void btnStartAnalysis_Click(object sender, EventArgs e)
{
if(cmbOperations.SelectedItem != null)
RunAnalysis<cmbOperations.SelectedItem.Value>();
}
private void RunAnalysis<T>()
{
//Checks for the selectedItem in the cmbOpearions dropdown and make call to appropriate functions.
//string assemblyName = cmbOperations.SelectedValue.ToString();
//Assembly assembly = Assembly.LoadFrom(assemblyName);
//Type localType = assembly.GetType("PrimaryKeyChecker.PrimaryKeyChecker");
IMFDBAnalyserPlugin analyser =
(IMFDBAnalyserPlugin) Activator.CreateInstance(T);
string response = analyser.RunAnalysis(objConnectionString.ConnectionString);
//show the response of the the function call
txtPluginResponse.Text = response;
}
Another way could be to just use a parameter for the Type currently selected. Hope this helps you out or to bring you to new ideas towards a solution.
ControlType = "System.Windows.Forms.WindowsFormsApplication1." + "PictureBox1";
System.Reflection.Assembly asm;
asm = typeof(Form).Assembly;
ControlObject = (System.Windows.Forms.Control)asm.CreateInstance(ControlType);
ControlObject.Name = ControlName;
The next code generated following exception for me:
ControlObject.Name = ControlName;
NullReferenceException was unhandle
Object reference not set to an instance of an object.
Assembly.CreateInstance is expecting a type name and you appear to be passing it the name of an instance of a type (namely, a PictureBox named PictureBox1.). Therefore, ControlObject is null and thus ControlObject.Name will throw a NullReferenceException.
It's not clear what you're trying to do, but that is why you are encountering the problem that you are. If you're trying to create a new instance of PictureBox I don't see why you don't just say new PictureBox(); this class has a public parameterless constructor. Alternatively, if you insist on reflection, you could say
controlType = PictureBox1.GetType();
controlObject = Activator.CreateInstance<Control>(controlType);
We could help more if we knew what you were trying to do instead of just throwing code that doesn't work at us and expecting us to solve world hunger.
Additionally,
ControlType = "System.Windows.Forms.WindowsFormsApplication1." + "PictureBox1";
Please rename this variable to controlType. You should use camel case for variable names.
Why do you have your application class WindowsFormsApplication1 living in the system namespace System.Windows.Forms? Don't do this.
You're probably trying to write
ControlObject = new PictureBox();
Looks like CreateInstance returns null, which means the type wasn't found in the assembly. Is PictureBox1 a type or an object?
Surly your application is not in System.Windows.Forms namespace
"System.Windows.Forms.WindowsFormsApplication1." + "PictureBox1"
Try:
ControlObject = (System.Windows.Forms.Control)asm.CreateInstance(typeof(PictureBox));
or just
ControlObject = new PictureBox();
to create a new instance of control.
Or maybe you want to find an existing PictureBox control on your form?
It means that asm.CreateInstance(ControlType); is returning null.
So, ControlType has a wrong value. It is supposed to recieve as parameter a type http://msdn.microsoft.com/en-us/library/dex1ss7c.aspx and it seems that you are sending an instance PictureBox1.
It should be ControlType = "System.Windows.Forms.PictureBox";