从C#脚本中调用外部应用程序方法
问题描述:
在将脚本功能集成到我的团队构建的应用程序中,我已经取得了很多进展,但我现在有点卡住了。从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!");
}
}
}
让我知道,如果你有任何问题,并希望我已经说清楚了。
答
在我的一个项目中,我实现了类似的功能。我们通过提供暴露跑步者不同服务的方法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!");
}
}
}
你能解释一下吗?我没有看到如果我提供RunScript接口,我可以调用内部方法。 – spaderdabomb