编译和执行单行代码
Compiling and Executing single lines of code
我正在为项目创建/种类/自定义编译器。我正在做的是让用户在文本框中输入代码行,或者他们可以从文本文件中导入一些代码行。
过去几天我一直在尝试对此进行测试,但没有结果。我有一个名为 Pressure 的 class,其中我有一个名为 'emit' 的 public 方法,它只显示一个文本框,就像这样...
public void emit()
{
MessageBox.Show("HOORA!");
}
我有一个名为 "testCompile.txt" 的文本文件,如下所示:
PressureTransducer pt = new PressureTransducer(0,0);
pt.emit();
当插入到 VS 中时,它应该编译得很好。之后,我尝试像这样编译文件...
String sourceName = @"C:\Users\Devic\Desktop\CompileTester\testCompile.txt";
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = true;
//cp.OutputAssembly = null;
cp.GenerateInMemory = true;
cp.TreatWarningsAsErrors = false;
CompilerResults cr = provider.CompileAssemblyFromFile(cp,
sourceName);
if (cr.Errors.Count > 0)
{
// Display compilation errors.
Console.WriteLine("Errors building {0} into {1}",
sourceName, cr.PathToAssembly);
foreach (CompilerError ce in cr.Errors)
{
Console.WriteLine(" {0}", ce.ToString());
Console.WriteLine();
}
}
else
{
// Display a successful compilation message.
Console.WriteLine("Source {0} built into {1} successfully.",
sourceName, cr.PathToAssembly);
}
但是 VS 给我错误:
c:\Users\Devic\Desktop\CompileTester\testCompile.txt(1,29) : error CS1518: Expected class, delegate, enum, interface, or struct
The thread 0x1290 has exited with code 0 (0x0).
对发生的事情有什么想法吗?
您需要将文本文件中的代码封装到可用的 class 和方法中。
下面是我使用了几年的代码,它允许 C# 脚本在我的应用程序中 运行,它甚至可以传递用户定义的变量。我在我的代码中传递了其他参数,让脚本编写者可以完全访问其他现有的 class 实例,但我将它们删除,因为它们是我的软件所独有的。如果您想提供对应用中任何现有 class 元素或表单的访问权限,您也可以这样做。
要使用您的 class PressureTransducer
,您需要确保正确引用声明该类型的 DLL,并且命名空间包含在伪造代码封装的使用部分中。但是,我有一个内置部分可以自动引用您的 运行ning 程序当前引用的所有程序集,因此通常会自动处理所有事情。
此外,这将代码作为源代码的字符串并将程序集生成到内存中,因此没有磁盘访问 - 它 运行 非常快。
注意:其中使用了一个过时的函数,codeProvider.CreateCompiler();
,但它对我仍然有效。不过我可能最终应该更新它。
private static object RunCSharpCode(string CSharpCode, bool ShowErrors, string StringParameter)
{
try
{
#region Encapsulate Code into a single Method
string Code =
"using System;" + Environment.NewLine +
"using System.Windows.Forms;" + Environment.NewLine +
"using System.IO;" + Environment.NewLine +
"using System.Text;" + Environment.NewLine +
"using System.Collections;" + Environment.NewLine +
"using System.Data.SqlClient;" + Environment.NewLine +
"using System.Data;" + Environment.NewLine +
"using System.Linq;" + Environment.NewLine +
"using System.ComponentModel;" + Environment.NewLine +
"using System.Diagnostics;" + Environment.NewLine +
"using System.Drawing;" + Environment.NewLine +
"using System.Runtime.Serialization;" + Environment.NewLine +
"using System.Runtime.Serialization.Formatters.Binary;" + Environment.NewLine +
"using System.Xml;" + Environment.NewLine +
"using System.Reflection;" + Environment.NewLine +
"public class UserClass" + Environment.NewLine +
"{" + Environment.NewLine +
"public object UserMethod( string StringParameter )" + Environment.NewLine +
"{" + Environment.NewLine +
"object Result = null;" + Environment.NewLine +
Environment.NewLine +
Environment.NewLine +
CSharpCode +
Environment.NewLine +
Environment.NewLine +
"return Result;" + Environment.NewLine +
"}" + Environment.NewLine +
"}";
#endregion
#region Compile the Dll to Memory
#region Make Reference List
Assembly[] FullAssemblyList = AppDomain.CurrentDomain.GetAssemblies();
System.Collections.Specialized.StringCollection ReferencedAssemblies_sc = new System.Collections.Specialized.StringCollection();
foreach (Assembly ThisAssebly in FullAssemblyList)
{
try
{
if (ThisAssebly is System.Reflection.Emit.AssemblyBuilder)
{
// Skip dynamic assemblies
continue;
}
ReferencedAssemblies_sc.Add(ThisAssebly.Location);
}
catch (NotSupportedException)
{
// Skip other dynamic assemblies
continue;
}
}
string[] ReferencedAssemblies = new string[ReferencedAssemblies_sc.Count];
ReferencedAssemblies_sc.CopyTo(ReferencedAssemblies, 0);
#endregion
Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
System.CodeDom.Compiler.ICodeCompiler CSharpCompiler = codeProvider.CreateCompiler();
System.CodeDom.Compiler.CompilerParameters parameters = new System.CodeDom.Compiler.CompilerParameters(ReferencedAssemblies);
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
parameters.IncludeDebugInformation = false;
parameters.OutputAssembly = "ScreenFunction";
System.CodeDom.Compiler.CompilerResults CompileResult = CSharpCompiler.CompileAssemblyFromSource(parameters, Code);
#endregion
if (CompileResult.Errors.HasErrors == false)
{ // Successful Compile
#region Run "UserMethod" from "UserClass"
System.Type UserClass = CompileResult.CompiledAssembly.GetType("UserClass");
object Instance = Activator.CreateInstance(UserClass, false);
return UserClass.GetMethod("UserMethod").Invoke(Instance, new object[] { StringParameter });
#endregion
}
else // Failed Compile
{
if (ShowErrors)
{
#region Show Errors
StringBuilder ErrorText = new StringBuilder();
foreach (System.CodeDom.Compiler.CompilerError Error in CompileResult.Errors)
{
ErrorText.Append("Line " + (Error.Line - 1) +
" (" + Error.ErrorText + ")" +
Environment.NewLine);
}
MessageBox.Show(ErrorText.ToString());
#endregion
}
}
}
catch (Exception E)
{
if (ShowErrors)
MessageBox.Show(E.ToString());
}
return null;
}
您可以考虑查看新的 Roslyn 编译器。您将字符串传递给脚本引擎 class 中的 Execute
方法,它将即时执行代码。
public class CSharpScriptEngine
{
private static Script _previousInput;
private static Lazy<object> _nextInputState = new Lazy<object>();
public static object Execute(string code)
{
var script = CSharpScript.Create(code, ScriptOptions.Default).WithPrevious(_previousInput);
var endState = script.Run(_nextInputState.Value);
_previousInput = endState.Script;
_nextInputState = new Lazy<object>(() => endState);
return endState.ReturnValue;
}
}
请参阅 this article 了解信用和完整实施。
我正在为项目创建/种类/自定义编译器。我正在做的是让用户在文本框中输入代码行,或者他们可以从文本文件中导入一些代码行。
过去几天我一直在尝试对此进行测试,但没有结果。我有一个名为 Pressure 的 class,其中我有一个名为 'emit' 的 public 方法,它只显示一个文本框,就像这样...
public void emit()
{
MessageBox.Show("HOORA!");
}
我有一个名为 "testCompile.txt" 的文本文件,如下所示:
PressureTransducer pt = new PressureTransducer(0,0);
pt.emit();
当插入到 VS 中时,它应该编译得很好。之后,我尝试像这样编译文件...
String sourceName = @"C:\Users\Devic\Desktop\CompileTester\testCompile.txt";
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = true;
//cp.OutputAssembly = null;
cp.GenerateInMemory = true;
cp.TreatWarningsAsErrors = false;
CompilerResults cr = provider.CompileAssemblyFromFile(cp,
sourceName);
if (cr.Errors.Count > 0)
{
// Display compilation errors.
Console.WriteLine("Errors building {0} into {1}",
sourceName, cr.PathToAssembly);
foreach (CompilerError ce in cr.Errors)
{
Console.WriteLine(" {0}", ce.ToString());
Console.WriteLine();
}
}
else
{
// Display a successful compilation message.
Console.WriteLine("Source {0} built into {1} successfully.",
sourceName, cr.PathToAssembly);
}
但是 VS 给我错误:
c:\Users\Devic\Desktop\CompileTester\testCompile.txt(1,29) : error CS1518: Expected class, delegate, enum, interface, or struct
The thread 0x1290 has exited with code 0 (0x0).
对发生的事情有什么想法吗?
您需要将文本文件中的代码封装到可用的 class 和方法中。
下面是我使用了几年的代码,它允许 C# 脚本在我的应用程序中 运行,它甚至可以传递用户定义的变量。我在我的代码中传递了其他参数,让脚本编写者可以完全访问其他现有的 class 实例,但我将它们删除,因为它们是我的软件所独有的。如果您想提供对应用中任何现有 class 元素或表单的访问权限,您也可以这样做。
要使用您的 class PressureTransducer
,您需要确保正确引用声明该类型的 DLL,并且命名空间包含在伪造代码封装的使用部分中。但是,我有一个内置部分可以自动引用您的 运行ning 程序当前引用的所有程序集,因此通常会自动处理所有事情。
此外,这将代码作为源代码的字符串并将程序集生成到内存中,因此没有磁盘访问 - 它 运行 非常快。
注意:其中使用了一个过时的函数,codeProvider.CreateCompiler();
,但它对我仍然有效。不过我可能最终应该更新它。
private static object RunCSharpCode(string CSharpCode, bool ShowErrors, string StringParameter)
{
try
{
#region Encapsulate Code into a single Method
string Code =
"using System;" + Environment.NewLine +
"using System.Windows.Forms;" + Environment.NewLine +
"using System.IO;" + Environment.NewLine +
"using System.Text;" + Environment.NewLine +
"using System.Collections;" + Environment.NewLine +
"using System.Data.SqlClient;" + Environment.NewLine +
"using System.Data;" + Environment.NewLine +
"using System.Linq;" + Environment.NewLine +
"using System.ComponentModel;" + Environment.NewLine +
"using System.Diagnostics;" + Environment.NewLine +
"using System.Drawing;" + Environment.NewLine +
"using System.Runtime.Serialization;" + Environment.NewLine +
"using System.Runtime.Serialization.Formatters.Binary;" + Environment.NewLine +
"using System.Xml;" + Environment.NewLine +
"using System.Reflection;" + Environment.NewLine +
"public class UserClass" + Environment.NewLine +
"{" + Environment.NewLine +
"public object UserMethod( string StringParameter )" + Environment.NewLine +
"{" + Environment.NewLine +
"object Result = null;" + Environment.NewLine +
Environment.NewLine +
Environment.NewLine +
CSharpCode +
Environment.NewLine +
Environment.NewLine +
"return Result;" + Environment.NewLine +
"}" + Environment.NewLine +
"}";
#endregion
#region Compile the Dll to Memory
#region Make Reference List
Assembly[] FullAssemblyList = AppDomain.CurrentDomain.GetAssemblies();
System.Collections.Specialized.StringCollection ReferencedAssemblies_sc = new System.Collections.Specialized.StringCollection();
foreach (Assembly ThisAssebly in FullAssemblyList)
{
try
{
if (ThisAssebly is System.Reflection.Emit.AssemblyBuilder)
{
// Skip dynamic assemblies
continue;
}
ReferencedAssemblies_sc.Add(ThisAssebly.Location);
}
catch (NotSupportedException)
{
// Skip other dynamic assemblies
continue;
}
}
string[] ReferencedAssemblies = new string[ReferencedAssemblies_sc.Count];
ReferencedAssemblies_sc.CopyTo(ReferencedAssemblies, 0);
#endregion
Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
System.CodeDom.Compiler.ICodeCompiler CSharpCompiler = codeProvider.CreateCompiler();
System.CodeDom.Compiler.CompilerParameters parameters = new System.CodeDom.Compiler.CompilerParameters(ReferencedAssemblies);
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
parameters.IncludeDebugInformation = false;
parameters.OutputAssembly = "ScreenFunction";
System.CodeDom.Compiler.CompilerResults CompileResult = CSharpCompiler.CompileAssemblyFromSource(parameters, Code);
#endregion
if (CompileResult.Errors.HasErrors == false)
{ // Successful Compile
#region Run "UserMethod" from "UserClass"
System.Type UserClass = CompileResult.CompiledAssembly.GetType("UserClass");
object Instance = Activator.CreateInstance(UserClass, false);
return UserClass.GetMethod("UserMethod").Invoke(Instance, new object[] { StringParameter });
#endregion
}
else // Failed Compile
{
if (ShowErrors)
{
#region Show Errors
StringBuilder ErrorText = new StringBuilder();
foreach (System.CodeDom.Compiler.CompilerError Error in CompileResult.Errors)
{
ErrorText.Append("Line " + (Error.Line - 1) +
" (" + Error.ErrorText + ")" +
Environment.NewLine);
}
MessageBox.Show(ErrorText.ToString());
#endregion
}
}
}
catch (Exception E)
{
if (ShowErrors)
MessageBox.Show(E.ToString());
}
return null;
}
您可以考虑查看新的 Roslyn 编译器。您将字符串传递给脚本引擎 class 中的 Execute
方法,它将即时执行代码。
public class CSharpScriptEngine
{
private static Script _previousInput;
private static Lazy<object> _nextInputState = new Lazy<object>();
public static object Execute(string code)
{
var script = CSharpScript.Create(code, ScriptOptions.Default).WithPrevious(_previousInput);
var endState = script.Run(_nextInputState.Value);
_previousInput = endState.Script;
_nextInputState = new Lazy<object>(() => endState);
return endState.ReturnValue;
}
}
请参阅 this article 了解信用和完整实施。