Feedback

C# - Dynamic Language Runtime in Desktop-Applikation einbinden

Veröffentlicht von am 11/4/2008
(1 Bewertungen)
Die neue Basis von Microsoft für Script-Sprachen, die Dynamic Language Runtime (DLR - aktuelle Version 0.91), bietet ein gute Möglichkeit ein Automationsinterface für seine eigene Anwendung zu bauen.

Alles was notwendig ist, ist ein Download der aktuellen Version von IronPython.

Zur aktuellen Version 0.91 kompatible Sprachen, für die DLR:
* IronRuby - http://ironruby.codeplex.com

zur DLR 0.9 kompatibel:
* Groovy/DLR - http://gavingrover.blogspot.com/ (erst vor kurzem erschienen, wird nicht aktiv weitergepflegt)

inkompatibel zur aktuellen DLR-Version (alter Branch verwendet)
* IronScheme - http://ironscheme.codeplex.com ( IronLisp durch diese Implementierung ersetzt)
* Phalanger - http://phalanger.codeplex.com/ (PHP-Engine)

* IronGlue - http://ironglue.codeplex.com/ (Smalltalk for .NET) - geringe Aktivität
* IronLua - http://code.google.com/p/ironlua/ - geringe Aktivität
* IronSmalltalk - http://ironsmalltalk.codeplex.com/ - geringe Aktivität


nicht mehr unterstüzt:
* Managed JScript (siehe http://dlr.codeplex.com/Thread/View.aspx?ThreadId=58121)

Was ist notwendig?

1. Download von IronPython http://ironpython.codeplex.com/ oder IronRuby http://ironpython.codeplex.com/ (oder einer anderen DLR-Sprache
2. Kopieren der Assemblies aus dem Downloadverzeichnis in das Zielverzeichnis der Applikation.
3. App.Config für die verwendeten Scriptsprachen konfigurieren. (siehe Kommentar zu App.Config im Snippet)
4. Referenzen im Projekt zu Microsoft.Scripting und Microsoft.Scripting.Core einfügen.
5. Codesnippet zum initialisieren und ausführen verwenden.

Zum jetzigen Zeitpunkt kann mit dem RC1 nur IronPython ausgeführt werden. Eine aktive Entwicklung findet meines Wissens nach bei den anderen Sprachen ebenfalls statt.

App.Config siehe Codesnippet.

Beispielapplikation zu finden unter:
http://cid-1eafce86943950d3.skydrive.live.com/self.aspx/Dev/DLR|_Hosting.zip

REPL für die DLR (von Jimmy Schementi für IronPython und IronRuby):
http://github.com/jschementi/repl-lib/tree/master/

Siehe auch:
http://en.wikipedia.org/wiki/Dynamic_Language_Runtime
using System;
using System.CodeDom;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Runtime;

/* App.Config für die DLR
 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
	  <section name="microsoft.scripting" 
			   type="Microsoft.Scripting.Hosting.Configuration.Section, Microsoft.Scripting, Version=0.9.6.20, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
			   requirePermission="false" />
  </configSections>
  <microsoft.scripting>
    <languages>
      <language names="IronPython;Python;py" 
    	        extensions=".py" 
                displayName="IronPython 2.6"
                type="IronPython.Runtime.PythonContext, IronPython, Version=2.6.0.20, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      <language names="IronRuby;Ruby;rb" 
    	        extensions=".rb"
                displayName="IronRuby 0.9"
                type="IronRuby.Runtime.RubyContext, IronRuby, Version=0.9.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </languages>
    <options>
      <set language='Ruby' option='LibraryPaths' value='.\lib\IronRuby;.\lib\ruby\site_ruby\1.8;.\lib\ruby\site_ruby;.\lib\ruby\1.8'/>
    </options>
  </microsoft.scripting>
</configuration>
*/

namespace DLR_hosting
{
	static class Program
	{
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main ()
		{
			// Die DLR muss in der App.Config eingerichtet werden. Hier sind auch
			// die zur Verfügung stehenden Sprach-Engines a la IronPython, IronRuby, IronScheme ...
			ScriptRuntime runtime = ScriptRuntime.CreateFromConfiguration ();
			
			// Alternativ kann auch direkt über die Engine initialisiert werden, dann muss aber
			// die Assembly in der die Engine steckt referenziert werden.
			//ScriptRuntime runtime = IronPython.Hosting.Python.CreateRuntime ();
			
			ScriptEngine pythonEngine	= runtime.GetEngine ( "IronPython" );
			// Die anderen Engines funktionieren im Moment nicht, da der aktuelle Stand nocht nicht 
			// auf der neuesten DLR basiert.
			//ScriptEngine rubyEngine		= runtime.GetEngine ( "IronRuby" );			
			//ScriptEngine luaEngine		= runtime.GetEngine ( "IronLua" );
			//ScriptEngine schemeEngine		= runtime.GetEngine ( "IronScheme" );
			
			// Einen Gültigkeitsbereich für Code in der Runtime anlegen.
			// Das ist jetzt unser globaler Namespace für unseren Host.
			ScriptScope baseScope = runtime.CreateScope ();			 

			// Jetzt registrieren wir unsere Instanzen, die wir im Script bearbeiten
			// wollen für die ScriptEngine in unserem "globalen Namespace" baseScope.
			// Dann können wir ...
			MyClass myClass = new MyClass ();
			baseScope.SetVariable ( "MyClassInstance", myClass );
			
			// ... uns einen eigenen dynamische UserScope definieren
			var userSymbols = new DynamicSymbolDictionary ( baseScope );
			ScriptScope useScope = pythonEngine.CreateScope ( userSymbols );

			// ... und Code ausführen der unsere C# Instanzen verändert.
			// 1. aus einer Zeichenkette
			var scriptSourceFromString = pythonEngine.CreateScriptSourceFromString(
				"MyClassInstance.MyPropertyInt = 3\nprint MyClassInstance.MyPropertyInt",
				SourceCodeKind.Statements);

			// 2. aus einer Datei
			var scriptSourceFromFile = pythonEngine.CreateScriptSourceFromFile ( @"test.py" );

			// 3. aus einem Provider (wird wohl eher von den einzelnen Scriptimplementierungen verwendet)
			//TODO: Provider erstellen.
			//StreamContentProvider scp;
			//TextContentProvider tcp;
			//var scriptSourceFromStreamProvider = pythonEngine.CreateScriptSource ( scp );
			//var scriptSourceFromTextProvider   = pythonEngine.CreateScriptSource ( tcp );

			// 4. aus dem CodeDom (eingeschränkt)
			//CodeObject codeObject;
			//TODO: codeObject erzeugen.
			//var scriptSourceFromCodeDom = pythonEngine.CreateScriptSource( codeObject);
		}
	}

	public class DynamicSymbolDictionary : CustomSymbolDictionary
	{
		private ScriptScope superScope;

		public DynamicSymbolDictionary ( ScriptScope superScope )
			: base ()			
		{
			this.superScope = superScope;
		}

		/// <summary>
		/// Try to set the extra value and return true if the specified key was found in the
		/// list of extra values.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		protected override bool TrySetExtraValue ( SymbolId key, object value )
		{
			return false;
		}

		/// <summary>
		/// Gets a list of the extra keys that are cached by the the optimized implementation
		/// of the module.
		/// </summary>
		/// <returns></returns>
		public override SymbolId[] GetExtraKeys ()
		{
			return null;
		}

		/// <summary>
		/// Try to get the extra value and returns true if the specified key was found in the
		/// list of extra values.  Returns true even if the value is Uninitialized.
		/// This function is called every time a script uses a variable.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		protected override bool TryGetExtraValue ( SymbolId key, out object value )
		{
			// if the variable is located in the super-scope we return this one. 
			// we achive scope-chaining with this technique.
			if ( superScope.TryGetVariable ( SymbolTable.IdToString ( key ), out value ) )
			{
				return true;
			}
			
			value = null;
			return false;
		}
	}
}

Kommentare zum Snippet

 

Logge dich ein, um hier zu kommentieren!