Integrating IronPython in a Windows Forms Application - c#

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

Related

Application .NET which compile code in Windows form

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

Complex types used in a .NET DLL (but defined in an external Web Service) not available in VB6

I developed a Web Service that checks user authorizations by querying a central database. It should be consumed by any application whenever a user starts it, in order to check if the user is allowed to run the application.
Since in the company there are many old applications written in Visual Basic 6 by people that do not have good programming skills, I'm trying to create a DLL that they can easily use in their VB6 applications to check user authorizations through a simple function call instead of dealing with details such as Web Service calls and XML messages.
I created a Class Library in Visual Studio 2010 using C#, which simply wraps the Web Service functions. I successfully created the DLL file and registered it (through RegAsm) into another machine used to develop Visual Basic 6 applications.
In VB6, the intellisense shows the functions exposed by the DLL (that can be called) but it is not working for the complex types returned by the functions.
For example, the DLL defines the following function, where the returned type (User) is defined into the Web Service code:
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("AuthorLibrary.Author")]
[Guid("D2E23F5E-C209-464A-AF32-CC0251078076")]
//[ComDefaultInterface(typeof(IAuthor))]
public class Author //: AuthorLibrary.IAuthor
{
//[...]
public User getUserData(string username)
{
ResponseMessageOfUser user;
using (AccessControlSoapClient ws = new AccessControlSoapClient())
{
user = ws.getUserData(username);
}
if (user.response != ResponseType.Ok)
throw new Exception(user.response.ToString());
return user.result;
}
//[...]
}
In VB6, the following code runs correctly:
Dim ws As AuthorLibrary.Author
Set ws = New AuthorLibrary.Author
Dim u As AuthorLibrary.User
Set u = ws.getUserData("john.smith")
Text1.Text = u.email
The problem is that when the developer writes u. nothing appears and so he does not know the available properties and/or methods for the object (even if, in the example above, u.email works).
Another method returns an instance of Department (let's call it dept). I can successfully print the values of simple string properties like dept.Description or dept.Code, but more complex properties such as dept.childrenDepartments - which should return a list of Department - give an error: "Missing object".
I think the problem is that the DLL file does not show the classes/types/structures defined into the Web Service. How can I do that?
I have also tried to define the following interface (IAuthor, as you can see from the first code), but it doesn't seem to work:
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("DB3BA840-B997-4D5E-86F0-0FC0A786C0D3")]
interface IAuthor
{
//[...]
}
The [InterfaceType] attribute is indeed the key. The default is ComInterfaceType.InterfaceIsIDispatch if the attribute is not applied explicitly. Which permits only late-binding, the equivalent of calling a method on an object reference of type dynamic in C#. Note how you don't get IntelliSense for that either, the VB6 programmer has the same problem.
The IAuthor you tried probably doesn't work because you forgot to make it public.
So you need to get that attribute on the User class. You'll have a problem because you didn't create that class. So for one, you probably can't easily declare an IUser interface either. The fallback for that is applying [ClassInterface(ClassInterfaceType.AutoDual)] on the class. It isn't exactly the prettiest solution, that also exposes the members of System.Object and gives the VB6 programmer a dependency on the .NET Framework type library but that's not the end of the world. You however still have to edit the class declaration to apply that attribute.
If it was generated from a web service then you'll have a maintenance headache. Every time it is re-generated you'll have to make the edit again. Then again, that is liable to break the VB6 code as well, it has to be recompiled. So you might want to consider isolating that declaration so this isn't strictly necessary. Declare your own User class and write the code to copy the fields/properties of the web service's User object to yours. And of course you won't have any trouble writing an interface and applying the attribute.

How can I create a plugin mechanism that calls functions only when the plugin is available?

Sorry if I am not clear enough, I've had a hard time writing this question.
I downloaded an open source software. I would like to expand the functionalities so I would like to create modules that encapsulates the functionality these modules would be .dll files.
I would like to have one completely independent from another: if I set a key to true in the config file and if the DLL is present on the folder, the plugin should be loaded.
The problem is: how can I make the call for the plugin dynamically (only call of the plugin is applied)?
If I reference the plugin classes directly, I would have to reference the plugin dll, but I want to be able to run the core software without the plugin. Is there any design pattern or other mechanism that would allow me to load and use the DLL only if the plugin is applied and still be possible to run the core software without the plugin?
There are various ways to achieve this and I will describe one simple solution here.
Make a common interface that each plugin must implement in order to be integrated with core application. Here is an example:
// Interface which plugins must implement
public interface IPlugin
{
void DoSomething(int Data);
}
// Custom plugin which implements interface
public class Plugin : IPlugin
{
public void DoSomething(int Data)
{
// Do something
}
}
To actually load your plugin from dll, you will need to use reflection, for example:
// Load plugin dll and create plugin instance
var a = Assembly.LoadFrom("MyCustomPlugin.dll");
var t = a.GetType("MyCustomPlugin.Plugin");
var p = (IPlugin)Activator.CreateInstance(t);
// Use plugin instance later
p.DoSomething(123);
You can use some kind of naming convention for your plugin assemblies and classes
so that you can load them easily.
You can use MEF.
The Managed Extensibility Framework (MEF) is a composition layer for
.NET that improves the flexibility, maintainability and testability of
large applications. MEF can be used for third-party plugin
extensibility, or it can bring the benefits of a loosely-coupled
plugin-like architecture to regular applications.
Here is programming guide.
Plugins or DLLs in .NET jargon are called assemblies. Check out the Assemply.Load method, and also this guide in msdn.
The System.Reflection namespace provides many tools that will help you with this scenario.
You can
inspect assemblies (DLL files) to examine the objects inside them,
find the types that you are looking for (specific classes, classes which implement specific interfaces, etc)
create new instances of those classes, and
invoke methods and access properties of those classes.
Typically you would write a class in the extension which does some work, create a method (e.g. DoWork()), and then invoke that method dynamically.
The MEF mentioned in this question does exactly this, just with a lot more framework.

IronPython - Editor for end-user

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.

Issues with embedding IronPython 2 in a C# web application

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.

Categories