2016-12-31 154 views
1

在将脚本功能集成到我的团队构建的应用程序中,我已经取得了很多进展,但我现在有点卡住了。从C#脚本中调用外部应用程序方法

我们已经编写了数据采集软件(应用程序),我们也希望拥有脚本功能。我在应用程序中嵌入了一个能够读取和编译外部写入脚本的类。例如,我可以编写一个不以任何方式链接到应用程序的.cs文件,并在运行时调用该文件,并成功执行该文件。

我现在需要做的事情是扩展脚本的用处,就是从脚本调用应用程序中的预先存在的方法。我会尽力来形容这一切用下面的例子:

在这里,我有我在任何时候改变,同时应用程序运行

ScriptFile.cs我的脚本文件

namespace SimpleScripts 
{ 
    public class MyScriptMul5 : ScriptingInterface.IScriptType1 
    { 
     public string RunScript(int value) 
     { 
      System.Console.WriteLine("Hello World! This works!"); 

      //NEED THIS: Code to call pre-existing method in application 
     } 
    } 
} 

对不起,这个大的代码块,但我想我只是包括一切案件问题出现。我想要做的就是用上述脚本调用方法TestExternalCall,同时通过此脚本处理程序运行它。

ScriptHandler.cs

namespace ScriptingInterface 
{ 
    public interface IScriptType1 
    { 
     string RunScript(int value); 
    } 
} 

namespace ScriptingExample 
{ 
    public static class ScriptingEx 
    { 
     public static void StartScript() 
     { 

      string path = @"TestScript1.cs"; 

      // Open the file to read from. 
      string readText = File.ReadAllText(path); 

      Assembly compiledScript = CompileCode(readText); 

      if (compiledScript != null) 
      { 
       RunScript(compiledScript); 
      } 
     } 

     static Assembly CompileCode(string code) 
     { 
      Microsoft.CSharp.CSharpCodeProvider csProvider = new Microsoft.CSharp.CSharpCodeProvider(); 

      CompilerParameters options = new CompilerParameters(); 
      options.GenerateExecutable = false; 
      options.GenerateInMemory = true; 
      options.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location); 

      // Compile our code 
      CompilerResults result; 
      result = csProvider.CompileAssemblyFromSource(options, code); 

      if (result.Errors.HasErrors) 
      { 
       // Report back to the user that the script has errored 
       Console.WriteLine("Script has errored"); 

       for (int i = 0; i < result.Errors.Count; i++) 
       { 
        Console.WriteLine("Error {0}: {1}", i+1, result.Errors[i]); 
       } 
       return null; 
      } 

      if (result.Errors.HasWarnings) 
      { 
       Console.WriteLine("Script has warnings"); 
      } 

      return result.CompiledAssembly; 
     } 

     static void RunScript(Assembly script) 
     { 
      foreach (Type type in script.GetExportedTypes()) 
      { 
       foreach (Type iface in type.GetInterfaces()) 
       { 
        if (iface == typeof(ScriptingInterface.IScriptType1)) 
        { 
         ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes); 
         if (constructor != null && constructor.IsPublic) 
         { 
          ScriptingInterface.IScriptType1 scriptObject = constructor.Invoke(null) as ScriptingInterface.IScriptType1; 
          if (scriptObject != null) 
          { 
           //Lets run our script and display its results 
           MessageBox.Show(scriptObject.RunScript(50)); 
          } 
         } 
        } 
       } 
      } 
     } 

     public static void TestExternalCall1() 
     { 
      Console.WriteLine("Called successfully!"); 
     } 
    } 
} 

让我知道,如果你有任何问题,并希望我已经说清楚了。

回答

0

在我的一个项目中,我实现了类似的功能。我们通过提供暴露跑步者不同服务的方法RunScript的方法来解决这个问题。 它允许脚本编写者在提交给执行者之前模拟服务并测试他们的代码。 该方法的另一个好处是,在编译期间您将捕获不兼容问题。 作为替代解决方案,您可以使用反射,但它不是很强类型,如果执行者的服务发生变化,您可能会在运行时遇到问题。

例如:

您的宏

namespace SimpleScripts 
{ 
    public class MyScriptMul5 : ScriptingInterface.IScriptType1 
    { 
     public string RunScript(ScriptingInterface.IServiceProvider serviceProvider, int value) 
     { 
      System.Console.WriteLine("Hello World! This works!"); 

      serviceProvider.Messenger.SendMessage("Test"); 
     } 
    } 
} 

大会,包含宏引擎的API(ScriptingInterface.dll)

namespace ScriptingInterface 
{ 
    public interface IScriptType1 
    { 
     string RunScript(int value); 
    } 

    public interface IMessenger{ 
     void SendMessage(String message); 
    } 

    public interface IServiceProvider 
    { 
     IMessenger Messenger {get;} 

     String TempDirectory {get;} 
    } 
} 

宏编译\执行

namespace ScriptingExample 
{ 
    public static class ScriptingEx 
    { 
     public static void StartScript(ScriptingInterface.IServiceProvider serviceProvider) 
     { 
      string path = @"TestScript1.cs"; 

      // Open the file to read from. 
      string readText = File.ReadAllText(path); 

      Assembly compiledScript = CompileCode(readText); 

      if (compiledScript != null) 
      { 
       RunScript(serviceProvider, compiledScript); 
      } 
     } 

     static Assembly CompileCode(string code) 
     { 
      Microsoft.CSharp.CSharpCodeProvider csProvider = new Microsoft.CSharp.CSharpCodeProvider(); 

      CompilerParameters options = new CompilerParameters(); 
      options.GenerateExecutable = false; 
      options.GenerateInMemory = true; 


       options.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location); 

      //Add references to ScriptingInterface.dll 
      String pathToScriptingInterfaceDll = Path.Combine(Environment.CurrentDirectory, "ScriptingInterface.dll"); 
      options.ReferencedAssemblies.Add(pathToScriptingInterfaceDll); 

      // Compile our code 
      CompilerResults result; 
      result = csProvider.CompileAssemblyFromSource(options, code); 

      if (result.Errors.HasErrors) 
      { 
       // Report back to the user that the script has errored 
       Console.WriteLine("Script has errored"); 

       for (int i = 0; i < result.Errors.Count; i++) 
       { 
        Console.WriteLine("Error {0}: {1}", i+1, result.Errors[i]); 
       } 
       return null; 
      } 

      if (result.Errors.HasWarnings) 
      { 
       Console.WriteLine("Script has warnings"); 
      } 

      return result.CompiledAssembly; 
     } 

     static void RunScript(ScriptingInterface.IServiceProvider serviceProvider, Assembly script) 
     { 
      foreach (Type type in script.GetExportedTypes()) 
      { 
       foreach (Type iface in type.GetInterfaces()) 
       { 
        if (iface == typeof(ScriptingInterface.IScriptType1)) 
        { 
         ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes); 
         if (constructor != null && constructor.IsPublic) 
         { 
          ScriptingInterface.IScriptType1 scriptObject = constructor.Invoke(null) as ScriptingInterface.IScriptType1; 
          if (scriptObject != null) 
          { 

           //Lets run our script and display its results 
           MessageBox.Show(scriptObject.RunScript(50)); 
          } 
         } 
        } 
       } 
      } 
     } 

     public static void TestExternalCall1() 
     { 
      Console.WriteLine("Called successfully!"); 
     } 
    } 
} 
+0

你能解释一下吗?我没有看到如果我提供RunScript接口,我可以调用内部方法。 – spaderdabomb