We're currently investigating how we can embed IronPython (scripting) into our C# application.
We see the benefits it will provide to our end users, giving them the ability to hook into our application but one question that keeps arising is how do we provide the end-user with code editing abilities that are aware of the different entry contexts in our application.
I know that we can provide a simple text editor with syntax highlighting but how do we go one step further and allow a user to test their scripts against objects that we expose from our application. Keeping in mind that we will expose different objects depending upon the context of the entry-point.
How do you allow end users to test, write and edit scripts in your application?
PS - I am new here so let me know if I am not doing this right!!!
Maybe what you want is to use the Visual Studio 2010 Shell Isolated. It can be used to provide a visual studio environment within an application, kind of how VBA used to be. As far was Python support you can look at IPyIsolatedShell
You could host IronPython in your C# application. Then you can pass in variables from your C# application and execute IronPython code which uses them. Dino Viehland did a talk at PDC about this called Using Dynamic Languages to Build Scriptable Applications. Dino made the source code for the application he created at the PDC available but it is using an older version of IronPython.
Here is some code for IronPython 2.7.1 that shows you how you can host IronPython in a few lines of code.
using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
public class MyIronPythonHost
{
ScriptEngine scriptEngine;
ScriptScope scriptScope;
public void Initialize(MyApplication myApplication)
{
scriptEngine = Python.CreateEngine();
scriptScope = scriptEngine.CreateScope();
scriptScope.SetVariable("app", myApplication);
}
public void RunPythonCode(string code)
{
ScriptSource scriptSource = scriptEngine.CreateScriptSourceFromString(code);
scriptSource.Execute(scriptScope);
}
}
The code above passes an application object called MyApplication to IronPython via a script scope and sets its variable name to be app. This app variable is then available to the IronPython code where it can call methods on it, access properties, etc.
The final method in the code above is the RunPythonCode method which takes in the IronPython code written by the user and executes it.
Going further than this and allowing the user to debug their IronPython code in a similar way to how you can debug VBA macros is a major piece of development work however.
Related
I'm developing an UWP app via C#, that uses IBasicVideoEffect with IDirect3DSurface. As mentioned in documentation, I have created Windows Runtime Component (Universal Windows) project.
But settings of effects are stored in some implementation of IPropertySet (smth like Dictionary<object, object>).
To use high - level operations on this storage, I've introduced IPropertySetExtensions into runtime component, that has for example Get<T>, GetOrDefault<T> methods, which return instance of T (not object) from storage.
Than I realized, that same operations will be needed in main project, but when I changed
internal static class IPropertySetExtensions into public one, I got an error like "winmd components cant contain generic methods".
That's why I duplicated extension class into main project.
How can I avoid this duplicating? Maybe move shared code into NuGet .dll or smth?
Unfortunately, when writing a WinRT Component there are quite a few limitations due to the fact that WinRT Components are usable in C++, C#, VB and JS (yes also JS). The following document covers part of what to watch out for: https://learn.microsoft.com/en-us/windows/uwp/winrt-components/creating-windows-runtime-components-in-csharp-and-visual-basic
In your case, I think creating a "Class Library (Universal Windows)" will solve that problem since you can write C# code as you like without having to watch out for WinRT Component limitations.
I have problem. I can't find mechanism in .Net which allow me compile code in Windows form.
I want have 1 textbox with code and later compiled code show in another box.
How I can do this?
It sounds like you want to use a C# compiler from within your Windows Forms app, but I'm unsure what your other box would show.
If you're looking for just compiling and visually displaying IL, there are certainly ways to call Roslyn from Windows Forms.
If you're looking to dynamically generate a GUI, your options are a little less clear. I would consider exposing your own wrapper functions to another language. I would consider using Moonsharp to compile Lua code on the fly. IronPython would also work. I'm unfamiliar with whether F# language services could be invoked in a similar way, but these are options I'd consider.
If you're specifically looking to compile C# and use the results to display a WinForms GUI, you'll need to use CodeDOM. CodeDOM is a pretty deep rabbit hole even if it's powerful, and it won't be easy to sandbox any GUI it renders to the output container you have in mind.
As has been mentioned, compiling code using Roslyn is certainly the way forward.
If you wish to see some output by executing the compiled code you may wish to create an abstract class for script writing which provides an entry point and a way of reporting output. You could then compile the script into a dll and use reflection to load the output assembly, instantiate an instance of your class (a class that implements ScriptTemplate below) and execute via the entry point.
abstract class ScriptTemplate
{
public abstract void Main();
public string Output
{
get;
protected set;
}
}
Your form could then write the Output property to a text box for example.
class Script : ScriptTemplate
{
public override void Main()
{
Output = "Hello world!";
}
}
I want to extend my Windows Forms Application programmed in C# with IronPython to enable the users of the software to extend the business logic with scripts. My Idea is to integrate a powerful editor with syntax highlighting and IntelliSense. At the moment I have no idea which editor I should use and how to access my Assemblies programmed in C# from the Python Script. Does anyone know, if there is
any Tutorial which covers this Issue or if there are any components available on the market, which I can integrate in my software to get the functionality I need.
You dont have to do anything when calling your own assemblies from IronPython, it uses reflection to find types and members
For instance, I have a class OrderPrice
public class OrderPrice
{
public decimal GetTotalPrice(Tariff.enumTariffType tariffType)
{
//....
}
}
Then in C# I add the variable price to the ScriptScope
OrderPrice price = .....
ScriptScope scope = scriptEngine.CreateScope();
scope.SetVariable("price", price);
Then in the python script you just call the members needed
if price.ErrorText == None:
totalType = price.GetTariffType()
totalPrice = price.GetTotalPrice(totalType)
And if you want to instantiate C# objects from the script you must use the clr module and add dll's as reference
import clr
clr.AddReferenceToFileAndPath(r"Entities.dll")
from Entities import OrderPrice
o = OrderPrice()
I currently have a .NET class library written in C# that exposes its functionaility via COM to a C++ program (pre-.NET).
We now want to move the library out-of-process to free up address space in the main application (it is an image-processing application, and large images eat up address space). I remember from my VB6 days that one could create an "OLE automation server". The OS would automatically start and stop the server .exe as objects were created/destroyed. This looks like the perfect fit for us: as far as I can see nothing would change in the client except it would call CoCreateInstance with CLSCTX_LOCAL_SERVER instead of CLSCTX_INPROC_SERVER.
How would I create such an out-of-process server in C#? Either there is no information online about it, or my terminology is off/out of date!
You can actually do this in .NET (I've done it before as a proof-of-concept), but it's a bit of work to get everything working right (process lifetime, registration, etc).
Create a new Windows application. In the Main method, call RegistrationServices.RegisterTypeForComClients- this is a managed wrapper around CoRegisterClassObject that takes care of the class factory for you. Pass it the Type of the managed ComVisible class (the one you actually want to create- .NET supplies the class factory automatically) along with RegistrationClassContext.LocalServer and RegistrationConnectionType.SingleUse. Now you have a very basic exe that can be registered as a LocalServer32 for COM activation. You'll still have to work out the lifetime of the process (implement refcounts on the managed objects with constructors/finalizers- when you hit zero, call UnregisterTypeForComClients and exit)- you can't let Main exit until all your objects are dead.
The registration isn't too bad: create a ComRegisterFunction attributed method that adds a LocalServer32 key under HKLM\CLSID(yourclsidhere), whose default value is the path to your exe. Run regasm yourexe.exe /codebase /tlb, and you're good to go.
You could always expose your .NET class as COM classes using InteropServices and then configure the library as a COM+ application. The .NET library would run out-of-process and be hosted by a DLLHOST.EXE instance.
Here is an article in MSDN that covers all aspects of how to create COM localserver in c# (.net): link
Your post started a while ago and I had the same problem. The following link is absolute gold and tells you everything
http://www.andymcm.com/blog/2009/10/managed-dcom-server.html
First Some Background (incase it helps):
My application is a Web-based framework recently upgraded to v3.5 of the .Net Framework but does not use a Master Pages / User Controls system. It's more akin to the MVC pattern (although much older) and outputs pure HTML down the response stream from Templates. The Python expressions allow some rules and template variations to be achieved.
The old way
When embedding the IronPython 1.x engine in C#, we were able to do code such as:
PythonEngine pe = new PythonEngine();
Assembly a = Assembly.LoadFile("path to assembly");
pe.LoadAssembly(a);
pe.Import("Script");
there is no Import() method in ipy 2.0 and the ImportModule() method doesn't seem to work the same way. The Import() alleviated the need to put a line in every python script we write, such as:
from MyAssembly import MyClass
the fact that MyClass is full of static methods, means that calls to MyClass.MyMethod() work really well. I can't just instansiate an object and assign it to a variable in scope as the assembly that MyClass is contained in is dynamically loaded at runtime.
Now to the issue
I have sorted out all the other parts of the integration of IronPython 2.0 but would prefer not to require my implementers to type "from MyAssembly import MyClass" at the top of every script they write (just seems silly when it was not necessary in ipy 1.x) and likely to be a support issue for a while too.
And finally the question
Has anyone had this issue and resolved it? Am I doing things the wrong way for the DLR? or am I missing something obvious?
I'm not sure of the detail required for someone to help, but I hope this is enough.
Once the assembly's loaded, you can execute the import in the Scope you're using to run the script:
ScriptEngine engine = Python.CreateEngine();
engine.Runtime.LoadAssembly(a);
string code = "from MyAssembly import MyClass";
ScriptSource source = engine.CreateScriptSourceFromString(code, "<import>", SourceCodeKind.Statements);
CompiledCode c = source.Compile();
Scope scope = engine.CreateScope();
c.Execute(scope);
// then run your script in the same scope
We do something similar in our product.
(Hopefully this is valid C# - I actually tried it in IronPython itself, because it was more convenient.)
Thanks Wilberforce,
In the end I did the following:
Assembly a = Assembly.LoadFile("path to assembly");
object var = a.CreateInstance("MyNamespace.MyClass");
Scope.SetVariable("MyClass", var);
This made an object of my class in C# and then passed it to the IronPython scope as a variable.
Note, that this is creating the object in the C# scope (AppDomain) and just passing it to the IronPython. This seems (so far) to work for my problem because the object I am passing is full of only static methods but may not work for a class with state.