Issues with embedding IronPython 2 in a C# web application - c#

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.

Related

How do I call a COM object in C# that I registered using RegistrationServices

I'm experimenting with COM objects and created a simple COM service that acts as a calculator with add, subtract, multiply, divide (details not important).
I then wrote some code to register it dynamically with a C# application
Assembly asm = Assembly.LoadFile("C:\\...COMCalc.dll");
RegistrationServices regAsm = new RegistrationServices();
bool bResult = regAsm.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase);
After registering it I've been able to use the service from an IE browser in javascript.
var num1 = 2
var num2 = 2
var objTest = new ActiveXObject("COMCalc.COMCalc")
alert(num1 + " - " + num2 + " = " + objTest.Subtract(num1,num2))
I'd like to now be able to test it from my C# Application so I can have a register, unregister, and test method for my COM object. I've struggled to find the documentation for how to do this. Any Ideas?
Bonus: I also would like to access it with the GUID defined in the COM object as opposed to the COMCalc.
regAsm.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase)
By writing your own custom registration method, you are missing out on the normal way that COM client programs or unit testers will exercise your code. They'll use the type library of your COM component, a machine-readable file that describes the types that you expose from your component. It is the COM equivalent of .NET metadata.
You get a type library by using the normal way to register, either by using your project's "Register for COM Interop" setting or by running Regasm.exe with the /tlb option. Or by running Tlbexp.exe to generate it manually.
This however does not let you test your component with a C# unit test, you'd normally use Project > Add Reference > Browse and pick the .tlb file. But the IDE refuses to accept it, it can see that the type library was created from a .NET assembly. It insists that you use a normal assembly reference instead, picking the DLL instead.
There's a very good reason for that. You can fool the IDE by using late binding but that does not fool the CLR. In other words, you are not actually testing the COM interop at all. You might as well use the normal way to add a .NET assembly reference. Truly testing the component requires using a COM client written in a non-.NET language. Not that many practical ones around anymore, you could use a scripting language like Javascript or VBScript. The closer it is to the actual language and runtime environment that is going to use your component, the better. If you are going to use it in a browser then something like Selenium or HtmlAgilityPack would be wise choice.
Nobody ever likes to hear advice like that. You fool the IDE by late binding, very similar to what you did in the browser:
Type type = Type.GetTypeFromProgID("COMCalc.COMCalc");
dynamic obj = Activator.CreateInstance(type);
dynamic result = obj.Subtract(2, 1);

Can you perform code indirection in .NET?

I remember with InterSystems Cache code, you can use indirection to take a string and turn that into real executable code by preceding the string variable with "#". Can this be done in C#.NET or VB.NET code? So I'd like to have a method that would take an arguments array of strings (with one or multiple lines of code), and run that code, assuming it doesn't throw an exception of course. Where am I going with this? I'm trying to write a compiler within .NET code.
SET x="set a=3" XECUTE x ; sets the public variable a to 3
OR
SET x="tag1" d #x ; do/call the public subroutine tag1
OR
Set Y = "B",#Y = 6 ; sets public variable B = 6
I assume that you want to compile during runtime.
System.CodeDom and System.CodeDom.Complier namespaces contain interfaces that are relevant to runtime compilation.
For your own language you need to implement your derived class from a derived class of CodeDomProvider.
For .NET you can either programmatically build up code using System.CodeDom which is basically a wrapper over the Intermediate Language, or you can use System.CodeDom.Compiler to get an object that compiles a string (or file) into an executable or DLL using a C# or VB.NET compiler.
Compiling the string is more like the Intersystems Cache way of doing it, but it's still more work, because you must provide all the information the compiler needs. If you look at the CompilerParameters class you will see the added complexity. The compiled code will be in it's own assembly. An assembly can't be unloaded unless it's in it's own App Domain, and when dynamically compiling it is difficult enough that most people don't bother if they can avoid it.
Various approaches to your problem are proposed on this very site.
Some source code for one solution to what you've described can be found here if the link stays alive.

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.

Recompile C# while running, without AppDomains

Let’s say that I have two C# applications - game.exe (XNA, needs to support Xbox 360) and editor.exe (XNA hosted in WinForms) - they both share an engine.dll assembly that does the vast majority of the work.
Now let’s say that I want to add some kind of C#-based scripting (it’s not quite "scripting" but I’ll call it that). Each level gets its own class inherited from a base class (we’ll call it LevelController).
These are the important constraints for these scripts:
They need to be real, compiled C# code
They should require minimal manual "glue" work, if any
They must run in the same AppDomain as everything else
For the game - this is pretty straight forward: All the script classes can be compiled into an assembly (say, levels.dll) and the individual classes can be instanced using reflection as needed.
The editor is much harder. The editor has the ability to "play the game" within the editor window, and then reset everything back to where it started (which is why the editor needs to know about these scripts in the first place).
What I am trying to achieve is basically a "reload script" button in the editor that will recompile and load the script class associated with the level being edited and, when the user presses the "play" button, create an instance of the most recently compiled script.
The upshot of which will be a rapid edit-test workflow within the editor (instead of the alternative - which is to save the level, close the editor, recompile the solution, launch the editor, load the level, test).
Now I think I have worked out a potential way to achieve this - which itself leads to a number of questions (given below):
Compile the collection of .cs files required for a given level (or, if need be, the whole levels.dll project) into a temporary, unique-named assembly. That assembly will need to reference engine.dll. How to invoke the compiler this way at runtime? How to get it to output such an assembly (and can I do it in memory)?
Load the new assembly. Will it matter that I am loading classes with the same name into the same process? (I am under the impression that the names are qualified by assembly name?)
Now, as I mentioned, I can’t use AppDomains. But, on the other hand, I don’t mind leaking old versions of script classes, so the ability to unload isn’t important. Unless it is? I’m assuming that loading maybe a few hundred assemblies is feasible.
When playing the level, instance the class that is inherited from LevelController from the specific assembly that was just loaded. How to do this?
And finally:
Is this a sensible approach? Could it be done a better way?
UPDATE: These days I use a far simpler approach to solve the underlying problem.
There is now a rather elegant solution, made possible by (a) a new feature in .NET 4.0, and (b) Roslyn.
Collectible Assemblies
In .NET 4.0, you can specify AssemblyBuilderAccess.RunAndCollect when defining a dynamic assembly, which makes the dynamic assembly garbage collectible:
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("Foo"), AssemblyBuilderAccess.RunAndCollect);
With vanilla .NET 4.0, I think that you need to populate the dynamic assembly by writing methods in raw IL.
Roslyn
Enter Roslyn: Roslyn lets you compile raw C# code into a dynamic assembly. Here's an example, inspired by these two blog posts, updated to work with the latest Roslyn binaries:
using System;
using System.Reflection;
using System.Reflection.Emit;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
namespace ConsoleApplication1
{
public static class Program
{
private static Type CreateType()
{
SyntaxTree tree = SyntaxTree.ParseText(
#"using System;
namespace Foo
{
public class Bar
{
public static void Test()
{
Console.WriteLine(""Hello World!"");
}
}
}");
var compilation = Compilation.Create("Hello")
.WithOptions(new CompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(MetadataReference.CreateAssemblyReference("mscorlib"))
.AddSyntaxTrees(tree);
ModuleBuilder helloModuleBuilder = AppDomain.CurrentDomain
.DefineDynamicAssembly(new AssemblyName("FooAssembly"), AssemblyBuilderAccess.RunAndCollect)
.DefineDynamicModule("FooModule");
var result = compilation.Emit(helloModuleBuilder);
return helloModuleBuilder.GetType("Foo.Bar");
}
static void Main(string[] args)
{
Type fooType = CreateType();
MethodInfo testMethod = fooType.GetMethod("Test");
testMethod.Invoke(null, null);
WeakReference weak = new WeakReference(fooType);
fooType = null;
testMethod = null;
Console.WriteLine("type = " + weak.Target);
GC.Collect();
Console.WriteLine("type = " + weak.Target);
Console.ReadKey();
}
}
}
In summary: with collectible assemblies and Roslyn, you can compile C# code into an assembly that can be loaded into an AppDomain, and then garbage collected (subject to a number of rules).
Check out the namespaces around Microsoft.CSharp.CSharpCodeProvider and System.CodeDom.Compiler.
Compile the collection of .cs files
Should be pretty straightforward like http://support.microsoft.com/kb/304655
Will it matter that I am loading classes with the same name into the same process?
Not at all. It's just names.
instance the class that is inherited from LevelController.
Load the assembly that you created something like Assembly.Load etc. Query the type you want to instanciate using reflection. Get the constructor and call it.
Well, you want to be able to edit things on the fly, right? that's your goal here isn't it?
When you compile assemblies and load them there's now way to unload them unless you unload your AppDomain.
You can load pre-compiled assemblies with the Assembly.Load method and then invoke the entry point through reflection.
I would consider the dynamic assembly approach. Where you through your current AppDomain say that you want to create a dynamic assembly. This is how the DLR (dynamic language runtime) works. With dynamic assemblies you can create types that implement some visible interface and call them through that. The back side of working with dynamic assemblies is that you have to provide correct IL yourself, you can't simply generate that with the built in .NET compiler, however, I bet the Mono project has a C# compiler implementation you might wanna check out. They already have a C# interpreter which reads in a C# source file and compiles that and executes it, and that's definitely handled through the System.Reflection.Emit API.
I'm not sure about the garbage collection here though, because when it comes to dynamic types I think the runtime doesn't release them because they can be referenced at any time. Only if the dynamic assembly itself is destroyed and no references exist to that assembly would it be reasonable to free that memory. If you're and re-generating a lot of code make sure that the memory is, at some point, collected by the GC.
If the language was Java the answer would be to use JRebel. Since it isn't, the answer is to raise enough noise to show there's demand for this. It might require some sort of alternate CLR or 'c# engine project template' and VS IDE working in coordination etc.
I doubt there's a lot of scenarios where this is "must have" but there's many in which it would save lot of time as you could get away with less infrastructure and quicker turnaround for things that aren't going to be used for long. (Yeah there's some who argue to over-engineer things because they'll be used 20+ years but the problem with that is when you need to do some massive change, it'll likely be as expensive as rebuilding the whole thing from scratch. So it comes down to whether to spend money now or later. Since it's not know for sure the project will become business critical later and might require large changes later anyhow, the argument to me is to use "KISS"-principle and have the complexities of live-editing in the IDE,CLR/runtime and so forth instead of building it into each app where it might be useful later. Certainly some defensive programming and practises will be needed to modify some live service using this sort of feature. As Erlang devs are said to be doing)

Whats the best way to wrap a c# class for use by powershell script

I have an engine that executes powershell scripts, and when they execute, need
to feed back in some xml to the caller. The engine only takes i/o as an idmef xml message. So the script needs to return a similarly formatted xml message. I have a class which does my formatting for me and it would like the script writers to use it.
So question is I want to wrap a c# class to enable it to be used by powershell.
I saw something some where you can refer to c# class using the square bracket mob, eg.
How to you make the c# available to be used like this. I reckon it needs to be made into class lib and somehow loaded into the powershell runtime but how. Also as I'm running the powershell from c#, how do I add it into the environment at that point.
Any help would be appreciated.
Bob.
If have an assembly (exe or dll) with the class in it, PowerShell can load it via [System.Reflection.Assembly]::LoadFile("PathToYourAssembly")
or in V2
Add-Type -Path "PathToYourAsembly"
If you are creating a runspace in your application and would like to make an assembly available, you can do that with a RunspaceConfiguration.
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
AssemblyConfigurationEntry myAssembly = new AssemblyConfigurationEntry("strong name for my assembly", "optional path to my assembly");
rsConfig.Assemblies.Append(myAssembly);
Runspace myRunSpace = RunspaceFactory.CreateRunspace(rsConfig);
myRunSpace.Open();
I don't think anything is necessary. You can access pretty much the entire .NET Framework from PowerShell. I'm sure they didn't create any wrappers to do it.
Dealing with interfaces in PowerShell is very difficult if not impossible at least in V1, so avoid these in your class. In PowerShell a simple [reflection.assembly]::Load or LoadFile is all it takes.
I would write a Cmdlet in C# that takes a bunch of parameters and spits out the XML that you want. Then you can use that Cmdlet from Powershell by piping things into it.
I hope this helps!

Categories