2016-12-31 8 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

私のプロジェクトの1つでは、同様の機能を実装していました。我々はランナーのさまざまなサービスを公開するRunScriptメソッドへのインタフェースを提供することによってそれを解決しました。 スクリプト作成者は、エグゼキュータに提出する前に、サービスを模擬してコードをテストすることができました。 このアプローチのもう1つの利点は、コンパイル時に互換性の問題をキャッチすることです。 別の解決策として、リフレクションを使用することができますが、強いタイプのものではなく、実行者のサービスが変更された場合は 実行時に問題が発生する可能性があります。例えば

あなたのマクロ

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

関連する問題