/// <summary> /// Loads/evaluates C# code into a remote AppDomain and returns a transparent proxy of the /// instance of the first class defined in the code. /// <para>The returned proxy can be used to unload the AppDomain owning the actual object /// the proxy points to.</para> /// </summary> /// <remarks> /// Note, the concrete type of the return value depends on the script class definition. /// It the class implement interface then an ordinary type castes proxy object is returned. /// However if the class doesn't implement the interface the a dynamically emitted duck-typed /// proxy returned instead. Such proxy cannot be built for the types implemented in file-less /// (in-memory) assemblies. Thus neither Mono nor Roslyn engines cannot be used with this /// technique. Meaning that /// <see cref="CSScriptLibrary.CSScript.CodeDomEvaluator"/> needs to be used. /// <para>While the script class to be evaluated doesn't have to implement from 'T' interface but /// it must inherit <see cref="System.MarshalByRefObject"/> though.</para> /// </remarks> /// <example> ///<code> /// // duck-typed proxy; must use CodeDomEvaluator /// var script = CSScript.CodeDomEvaluator /// .LoadCodeRemotely<ICalc>( /// @"using System; /// public class Calc : MarshalByRefObject /// { /// public int Sum(int a, int b) /// { /// return a + b; /// } /// }"); /// /// // ordinary type casted proxy /// var script2 = CSScript.Evaluator /// .LoadCodeRemotely<ICalc>( /// @"using System; /// public class Calc : MarshalByRefObject : ICalc /// { /// public int Sum(int a, int b) /// { /// return a + b; /// } /// }"); /// /// int result = script.Sum(15, 3); /// /// // after the next line call the remote domain with loaded script will be unloaded /// script.UnloadOwnerDomain(); /// </code> /// </example> /// <typeparam name="T">The interface type the remote object should be casted or aligned (duck-typed) to.</typeparam> /// <param name="evaluator">The evaluator.</param> /// <param name="scriptCode">The script code that defines the script class to be loaded. /// <para>The script class doesn't have to implement from 'T' interface but /// it must inherit <see cref="System.MarshalByRefObject"/> though.</para> /// </param> /// <param name="probingDirs">The probing directories for the assemblies the script /// assembly depends on.</param> /// <returns></returns> public static T LoadCodeRemotely <T>(this IEvaluator evaluator, string scriptCode, params string[] probingDirs) where T : class { var cx = RemoteLoadingContext.NewFor(evaluator, scriptCode); var searchDirs = probingDirs.ConcatWith(Path.GetDirectoryName(typeof(T).Assembly.Location())); var remoteDomain = evaluator.GetRemoteDomain(); if (remoteDomain == null) { remoteDomain = AppDomain.CurrentDomain.Clone(); } remoteDomain.Execute(context => { try { CSScript.GlobalSettings.InMemoryAssembly = context.inMemoryAsm; IEvaluator eval = RemoteLoadingContext.CreateEvaluator(context); context.scriptObj = eval.CompileCode(context.code) .CreateObject("*"); bool implementsInterface = typeof(T).IsAssignableFrom(context.scriptObj.GetType()); if (!implementsInterface) //try to align to T { context.scriptObj = context.scriptObj.AlignToInterface <T>(context.scriptObj.GetType().Assembly.Location); } } catch (Exception e) { context.error = e.ToString(); } }, cx, searchDirs); if (cx.error != null) { throw new CompilerException("Exception in the remote AppDomain: " + cx.error); } var result = (T)cx.scriptObj; evaluator.SetRemoteDomain(remoteDomain); result.SetOwnerDomain(remoteDomain); return(result); }
/// <summary> /// Wraps C# code fragment into auto-generated class (type name <c>DynamicClass</c>), /// evaluates it and loads the class to the remote AppDomain. /// <para>Returns non-typed <see cref="CSScriptLibrary.MethodDelegate"/> of the remote object for /// class-less style of invoking.</para> /// </summary> /// <example> /// <code> /// /// var log = CSScript.Evaluator /// .CreateDelegateRemotely( /// @"void Log(string message) /// { /// Console.WriteLine(message); /// }"); /// /// log("Test message"); /// /// log.UnloadOwnerDomain(); /// </code> /// </example> /// <param name="evaluator">The evaluator.</param> /// <param name="code">The C# code.</param> /// <param name="probingDirs">The probing directories for the assemblies the script /// assembly depends on.</param> /// <returns> The instance of a 'duck typed' <see cref="CSScriptLibrary.MethodDelegate"/></returns> public static MethodDelegate CreateDelegateRemotely(this IEvaluator evaluator, string code, params string[] probingDirs) { string scriptCode = CSScript.WrapMethodToAutoClass(code, true, false, "MarshalByRefObject"); var agentDef = @" public class RemoteAgent : MarshalByRefObject, CSScriptLibrary.IRemoteAgent { public object Method(params object[] parameters) { return Implementation(parameters); } public CSScriptLibrary.MethodDelegate Implementation {get; set;} }"; var cx = RemoteLoadingContext.NewFor(evaluator, scriptCode + agentDef); var remoteDomain = evaluator.GetRemoteDomain(); if (remoteDomain == null) { remoteDomain = AppDomain.CurrentDomain.Clone(); } remoteDomain.Execute(context => { try { IEvaluator eval = RemoteLoadingContext.CreateEvaluator(context); var script = eval.ReferenceAssemblyOf <CSScript>() .CompileCode(context.code); //var asm = script.GetType().Assembly.Location; #if net45 string agentTypeName = script.DefinedTypes.Where(t => t.Name == "RemoteAgent").First().FullName; #else string agentTypeName = script.GetModules() .SelectMany(m => m.GetTypes()) .Where(t => t.Name == "RemoteAgent") .First() .FullName; #endif var agent = (IRemoteAgent)script.CreateObject(agentTypeName); agent.Implementation = script.GetStaticMethod(); context.scriptObj = agent; } catch (Exception e) { context.error = e.ToString(); } }, cx, probingDirs); if (cx.error != null) { throw new CompilerException("Exception in the remote AppDomain: " + cx.error); } var agentProxy = (IRemoteAgent)cx.scriptObj; MethodDelegate result = (param) => agentProxy.Method(param); evaluator.SetRemoteDomain(remoteDomain); result.SetOwnerDomain(remoteDomain) .SetOwnerObject(agentProxy); return(result); }