Using Ironpython, I created a .dll from a .py file. It has classes and respective functions that I want to call to be used in c#. I created the .dll so that I can hide the source from the user.
Here is what I have tried:
ScriptEngine engine = Python.CreateEngine();
scope = engine.CreateScope();
engine.Runtime.LoadAssembly(Assembly.LoadFile(fullPath2DLL));
scope = engine.ImportModule("Simulation");
However, it cannot find "Simulation".
Also, I want to import the whole script at once so I can call whatever, whenever [Rather than the class 'Simulation'].
Many things could go wrong, so I'll just show you complete example which works. Let's take this python code that I grabbed in some example:
MyGlobal = 5
class Customer(object):
"""A customer of ABC Bank with a checking account. Customers have the
following properties:
Attributes:
name: A string representing the customer's name.
balance: A float tracking the current balance of the customer's account.
"""
def __init__(self, name, balance=0.0):
"""Return a Customer object whose name is *name* and starting
balance is *balance*."""
self.name = name
self.balance = balance
def withdraw(self, amount):
"""Return the balance remaining after withdrawing *amount*
dollars."""
if amount > self.balance:
raise RuntimeError('Amount greater than available balance.')
self.balance -= amount
return self.balance
def deposit(self, amount):
"""Return the balance remaining after depositing *amount*
dollars."""
self.balance += amount
return self.balance
Now let's open ipy and compile that into dll with:
>>> import clr
>>> clr.CompileModules("path_to.dll", "path_to.py");
Now we have dll. As you see python code contains class definition, and our goal is to create instance of that class in C# and call some methods.
public class Program {
private static void Main(string[] args) {
ScriptEngine engine = Python.CreateEngine();
engine.Runtime.LoadAssembly(Assembly.LoadFile(#"path_to.dll"));
// note how scope is created.
// "test" is just the name of python file from which dll was compiled.
// "test.py" > module named "test"
var scope = engine.Runtime.ImportModule("test");
// fetching global is as easy as this
int g = scope.GetVariable("MyGlobal");
// writes 5
Console.WriteLine(g);
// how class type is grabbed
var customerType = scope.GetVariable("Customer");
// how class is created using constructor with name (note dynamic keyword also)
dynamic customer = engine.Operations.CreateInstance(customerType, "Customer Name");
// calling method on dynamic object
var balance = customer.deposit(10.0m);
// this outputs 10, as it should
Console.WriteLine(balance);
Console.ReadKey();
}
}
Related
I'm using IronPython v 2.7.8.1 in VS 2017. I've installed Python27, 36 and 37. I've tried switching between the various environments in VS. I've tried adding the search paths to the libraries of these installs. The python code will work if I run it in the interpreter. Trying to run the same code in VS throws: "Microsoft.Scripting.SyntaxErrorException: unexpected token ',' ". If I test a python script that doesn't include imports it will work? Is there a specific way python has to be installed to work IronPython? This is the C# Code:
class CallPython
{
public void PatchParameter(string parameter)
{
var FilePath = (#"C:\Users\Pac\Downloads\MvcAuth\MvcAuth\TwurlPy\TwurlPy\SendDirectMsg.py");
var engine = Python.CreateEngine(); // Extract Python language engine from their grasp
ICollection<string> searchPaths = engine.GetSearchPaths();
searchPaths.Add(#"C:\Users\Pac\AppData\Local\Programs\Python\Python37\Lib");
searchPaths.Add(#"C:\Users\Pac\AppData\Local\Programs\Python\Python37\Lib\site-packages");
engine.SetSearchPaths(searchPaths);
var scope = engine.CreateScope(); // Introduce Python namespace
(scope)
var d = new Dictionary<string, object>
{
{ "text", text},
{ "userID", userID},
};
// Add some sample parameters. Notice that there is no need in
// specifically setting the object type, interpreter will do that part for us
// in the script properly with high probability
scope.SetVariable("params", d); // This will be the name of the
// dictionary in python script, initialized with previously created .NET
// Dictionary
ScriptSource source = engine.CreateScriptSourceFromFile(FilePath);
// Load the script
object result = source.Execute(scope);
parameter = scope.GetVariable<string>("parameter"); // To get the
// finally set variable 'parameter' from the python script
return;
}
}
This is the Python script. If I comment out the import statements it works using IronPython, but of course I need them...
import twitter
import requests
import sys
parameter = "test"
def SendDM(text, userID, access_token, access_secret):
consumer_key = 'YkopsCQjEXccccccccccccccccccZvA9yy'
consumer_secret = 'TQVCoccccccccccccccccccct7y8VfmE'
access_token_key = access_token
access_token_secret = access_secret
api = twitter.Api(
consumer_key=consumer_key,
consumer_secret=consumer_secret,
access_token_key=access_token_key,
access_token_secret=access_token_secret)
send_msg = api.PostDirectMessage(text, user_id=userID)
print (send_msg)
return
SendDM(text, userID, access_token, access_secret)
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
This question already has answers here:
Creating an object from from an ID (or name)
(5 answers)
Closed 9 years ago.
in c# i am working on an text based adventure where each tile is represented by a class. in my main class i have inserted a way of reading from the console and in a class that i have called tile loader i have an update function. this function gets a string from the input read at the start. when i call it i want the string to be converted into an class reference so that i can call the class and an load function witch is inside of each tile class. i could just go ahead and insert a lot of if's but i don't really want to do that. i know there is an easier way.
You can use Activator.CreateInstance to do this.
You will need to provide an assembly name or reference and may need to add the namespace for the class unless you want to provide the fully qualified class in the command line.
For example, assuming that the class Test is in an assembly called WindowsFormsApplication1 and the user entry is in a variable called sInputClassName, the following code will create an instance of the class in the oClass variable:
// What the user entered
var sInputClassName = "Test";
// The name of the assembly; there are other ways to get this, such as through reflection
const string CLASS_ASSEMBLY_NAME = "WindowsFormsApplication1";
// Get the requested type from the entered class name and assembly name
var oType = Type.GetType(CLASS_ASSEMBLY_NAME + "." + sInputClassName, true);
if (oType != null) {
// Once we have the class type, create an instance of it
var oClass = Activator.CreateInstance(oType, false);
if (oClass != null) {
System.Diagnostics.Debug.WriteLine("Created " + sInputClassName);
} else {
System.Diagnostics.Debug.WriteLine("Could not create " + sInputClassName);
}
} else {
System.Diagnostics.Debug.WriteLine("Could not find " + sInputClassName);
}
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'm using the implementation of ironpython 2.6.2 in my application c# 3.5, but I'm getting the following error: "The method or operation is not implemented."
Added references to the DLR and IronPython assemblies (all found in the IronPython install directory, which is “C:\Program Files\IronPython 2.6” on my machine):
IronPython.dll
IronPython.Modules.dll
Microsoft.Scripting.dll
Microsoft.Scripting.Core.dll
follows the code of my application:
ScriptEngine engine = Python.CreateEngine();
//parameter file path
ScriptSource source = engine.CreateScriptSourceFromFile(pathFilePy);
ScriptScope scope = engine.CreateScope();
ObjectOperations op = engine.Operations;
source.Execute(scope); // class object
object classObject = scope.GetVariable("calc"); // get class object
object instance = op.Invoke(classObject); // create instance
object method = op.GetMember(instance, "calc01"); // get method
var result = op.Invoke(method, 10, 20,30); // call method and get result
the code file .py
class calc(object):
def calc01(self,var1,var2,var3):
bla = ((var1+var2+var3)/3)
return bla
The error occurs on this line:
var result = op.Invoke(method, 10, 20,30); // call method and get result
Maybe you should call it with 4 arguments instead of 3 like this:
>>> class calc(object):
... def calc01(self,var1,var2,var3):
... bla = ((var1+var2+var3)/3)
... return bla
...
>>> calc1 = calc()
>>> calc.calc01(calc1,10,20,30)
20
>>>
as was pointed out in the other answer it looks like your method needs 4 parameters and is only being invoked with 3 from the c# code. the question I would ask (not being a python expert) is why your python method has the parameter self at all? I would have thought you need to do 1 of 2 things, both of which will probably solve your problem
1 Redfine your python method not to contain self:
class calc(object):
def calc01(var1,var2,var3):
bla = ((var1+var2+var3)/3)
return bla
2 Invoke the python method using 4 arguments:
...
object classObject = scope.GetVariable("calc"); // get class object
object instance = op.Invoke(classObject); // create instance
object method = op.GetMember(instance, "calc01"); // get method
var result = op.Invoke(method, instance, 10, 20,30); // call method and get result
...