using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; using Newtonsoft.Json.Linq; using System.Diagnostics; using System.Reflection; namespace LB_VisionProcesses.Processes { public class ScriptTool : BaseProcess { //public string[] SupportTypeArray = new string[] //{ // "Int32", "Double", "String", "Boolean","Object","Array", "List`1", "Dictionary`2" //}; MemoryStream ms = new MemoryStream(); Type programType = null; // 定义带ref参数的委托(必须完全匹配签名) delegate bool CustomDelegate(ref Dictionary dicInputs, ref Dictionary dicOutputs, ref string Msg); // 处理带两个ref参数的方法签名:bool Method(ref int a, ref string b) CustomDelegate delegateCall = null; // 获取方法并创建委托 MethodInfo method = null; Assembly assembly = null; // 需要先创建UserScript实例 object instance = null; public ScriptTool() { strProcessClass = "LB_VisionProcesses.Processes.ScriptTool"; strProcessName = "脚本"; Params.Inputs.Add("Scrip", $"/*\nTime: {DateTime.Now.ToLocalTime()}\nUser: {Environment.UserName}\nLogs: \n*/\n" + "public partial class UserScript\n{\n " + "bool ScripCode(ref Dictionary inputs, ref Dictionary outputs, ref string msg)\n " + "{\n Init(ref inputs, ref outputs);\n" + " // TODO: Add Script Here!\n" + " // public object GetInputs(string key);\n" + " // public object GetGlobalVars(string key);\n" + " // public void AddOutputs(string key, object value);\n" + " // public void AddGlobalVars(string key, object value);\n \n" + " return true;\n }\n}"); Params.Inputs.Add("OtherDllPaths", new List()); } public override bool Run() { try { InitRunParams(); // 通过反射,调用实例 if (ms == null) { Msg = "脚本未编译"; Result = false; return Result; } if (method == null) { Msg = "方法[ScripCode]不存在"; Result = false; return Result; } Dictionary dicInputs = GetInputs(); Dictionary dicOutputs = null; try { dicOutputs = Params.Outputs.GetAllByName(); if (dicOutputs == null) dicOutputs = new Dictionary(); } catch { dicOutputs = new Dictionary(); } // 初始化实例,传递输入输出字典和全局字典 InitializeInstance(dicInputs, dicOutputs); // 创建委托 delegateCall = (CustomDelegate)Delegate.CreateDelegate( typeof(CustomDelegate), instance, // 实例将在调用时传递 method); bool result = delegateCall(ref dicInputs, ref dicOutputs, ref Msg); foreach (var kvp in dicInputs) Params.Inputs.Add(kvp.Key, kvp.Value); foreach (var kvp in dicOutputs) Params.Outputs.Add(kvp.Key, kvp.Value); Result = result; } catch (Exception ex) { Msg = $"脚本异常,原因是:{ex.Message}"; Result = false; } return Result; } /// /// 获取脚本输入 /// /// public Dictionary GetInputs() { try { Dictionary dicInputs = new Dictionary(); for (int i = 0; i <= Params.Inputs.Count - 1; i++) { bool isFindKey = Params.Inputs.GetKey(i, out string key); //忽略不需要的参数 if (!isFindKey || key == "Scrip" || key == "OtherDllPaths") continue; dicInputs.TryAdd(key, Params.Inputs[i]); } return dicInputs; } catch { return new Dictionary(); } } /// /// 设置脚本输入 /// /// /// public bool SetInputs(Dictionary dicInputs) { try { //设置输入前将原先其他输入清除 if (!ClearInputs()) return false; foreach (var kvp in dicInputs) { string key = kvp.Key; //忽略不需要的参数 if (key == "Scrip" || key == "OtherDllPaths") continue; Params.Inputs.Add(key, kvp.Value); } return true; } catch { return false; } } public bool ClearInputs() { try { //设置输入前将原先其他输入清除 for (int i = 0; i <= Params.Inputs.Count - 1; i++) { bool isFindKey = Params.Inputs.GetKey(i, out string key); //忽略不需要的参数 if (!isFindKey || key == "Scrip" || key == "OtherDllPaths") continue; Params.Inputs.Remove(i); } return true; } catch { return false; } } public bool ClearOutputs() { try { //设置输入前将原先其他输入清除 for (int i = 0; i <= Params.Outputs.Count - 1; i++) { Params.Outputs.Remove(i); } return true; } catch { return false; } } public void CompileCode() { try { // 1. 定义程序集名称 var assemblyName = Path.GetRandomFileName(); // 3. 配置引用 var references = new List { // 添加基础程序集 MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Debug).Assembly.Location), MetadataReference.CreateFromFile(typeof(IProcess).Assembly.Location), // 添加 System.Collections.Concurrent 程序集 MetadataReference.CreateFromFile(typeof(System.Collections.Concurrent.ConcurrentDictionary).Assembly.Location), //MetadataReference.CreateFromFile(typeof(System.Collections.Generic.Dictionary).Assembly.Location), //MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location), // 添加其他必要程序集 MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Runtime.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "mscorlib.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Core.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Data.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Web.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Xml.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Xml.Linq.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Newtonsoft.Json.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Collections.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Linq.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Data.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Drawing.dll")), MetadataReference.CreateFromFile(Path.Combine( Path.GetDirectoryName(typeof(object).Assembly.Location)!, "System.Threading.Tasks.dll")) }; //compilerParams.ReferencedAssemblies.Add("halcondotnet.dll"); // 3. 添加自定义DLL引用 List dllPaths = (JArray.FromObject(Params.Inputs["OtherDllPaths"])).ToObject>(); if (dllPaths != null) { foreach (var dllPath in dllPaths) { if (File.Exists(dllPath)) references.Add(MetadataReference.CreateFromFile(dllPath)); } } // 4. 获取代码并构建完整代码 string code = ProcessParams.ConvertToString(Params.Inputs["Scrip"]); // 构建完整的代码,包含全局字典访问方法 string allCode = BuildCompleteCode(code); SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(allCode); var compilation = CSharpCompilation.Create( assemblyName, new[] { syntaxTree }, references, new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Debug, concurrentBuild: false)); // 5. 内存流编译 ms.Dispose(); ms = new MemoryStream(); EmitResult result = compilation.Emit(ms); // 6. 处理编译错误 if (!result.Success) { Msg = "编译失败:\r\n"; foreach (Diagnostic diagnostic in result.Diagnostics) { // 获取错误位置信息 FileLinePositionSpan lineSpan = diagnostic.Location.GetLineSpan(); // 格式化行号信息 string lineInfo = $"行号: {lineSpan.StartLinePosition.Line + 1}," + $"列: {lineSpan.StartLinePosition.Character + 1}"; Msg += $"{lineInfo} → {diagnostic.GetMessage()} [{diagnostic.Id}]\r\n"; } return; } // 7. 加载程序集 ms.Seek(0, SeekOrigin.Begin); assembly = Assembly.Load(ms.ToArray()); programType = assembly.GetType($"{strProcessClass}.UserScript"); if (programType == null) { Msg = $"类[{strProcessClass}.UserScript]不存在"; return; } method = programType.GetMethod("ScripCode", BindingFlags.NonPublic | BindingFlags.Instance); if (method == null) { Msg = "编译失败,方法[ScripCode]不存在"; return; } // 8. 创建实例并初始化 instance = Activator.CreateInstance(programType); if (instance == null) { Msg = $"实例化对象[{strProcessClass}.UserScript.ScripCode]不存在"; return; } Dictionary dicInputs = GetInputs(); Dictionary dicOutputs = null; try { dicOutputs = Params.Outputs.GetAllByName(); if (dicOutputs == null) dicOutputs = new Dictionary(); } catch { dicOutputs = new Dictionary(); } // 9. 初始化实例,传递输入输出字典和全局字典 InitializeInstance(dicInputs, dicOutputs); // 10. 创建委托 delegateCall = (CustomDelegate)Delegate.CreateDelegate( typeof(CustomDelegate), instance, // 实例将在调用时传递 method); Msg = "编译成功"; } catch (Exception ex) { Msg = $"编译发生异常错误:{ex.Message}【{ex.StackTrace}】"; } } /// /// 初始化实例,传递字典引用 /// private void InitializeInstance(Dictionary inputs, Dictionary outputs) { // 获取Init方法 var initMethod = programType.GetMethod("Init", BindingFlags.Public | BindingFlags.Instance); if (initMethod != null) { // 调用Init方法,传递字典引用 object[] initParams = new object[] { inputs, outputs // 传递字典引用 }; initMethod.Invoke(instance, initParams); } } /// /// 构建完整的代码,包含全局字典访问方法 /// private string BuildCompleteCode(string userCode) { return $@" using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.IO; using LB_VisionProcesses; namespace LB_VisionProcesses.Processes.ScriptTool {{ public partial class UserScript {{ private Dictionary inputs; private Dictionary outputs; public void Init( ref Dictionary inputs, ref Dictionary outputs) {{ this.inputs = inputs; this.outputs = outputs; }} public void AddInputs(string key, object value) {{ if (this.inputs.ContainsKey(key)) this.inputs[key] = value; else this.inputs.Add(key, value); }} public void AddOutputs(string key, object value) {{ if (this.outputs.ContainsKey(key)) this.outputs[key] = value; else this.outputs.Add(key, value); }} public object GetInputs(string key) {{ return this.inputs.TryGetValue(key, out object value) ? value : null; }} public object GetOutputs(string key) {{ return this.outputs.TryGetValue(key, out object value) ? value : null; }} // 全局字典操作方法 public void AddGlobalVars(string key, object value) {{ IProcess.dicGlobalVars?.AddOrUpdate(key, value, (k, v) => value); }} public object GetGlobalVars(string key) {{ return IProcess.dicGlobalVars.TryGetValue(key, out object value) ? value : null; }} }} {userCode} }} "; } /// /// 加载算法 /// /// 完整路径带.json /// public override bool Load(string fullPath = null) { try { if (!base.Load(fullPath)) return false; CompileCode(); return true; } catch { return false; } } } }