Lua结合C#调用C++或者C的函数
来源:互联网 发布:jsp无法import java类 编辑:程序博客网 时间:2024/06/01 17:07
本文所用到的知识点:
1.Lua与C#交互,参考这里
2.C#调用非托管DLL的方法(dllImport或者kernel32.dll中的LoadLibrary)
3.C# 反射动态生成方法并调用。
最终想达到的效果是:在Lua中能直接填写Dll中的函数,并传递参数调用。
一、Lua与C#交互:
在上面的链接中,将NLua下载下来。NLua是C#和Lua交互的桥梁。有两种方式:
a.C#+NLua+KopiLua(KopiLua是Lua的纯C#实现库)
b.C#+NLua+KeraLua+Lua52.dll
采用以上两种方法中的一种,需要将NLua的编译条件改一下,例如我用的是.C#+NLua+KopiLua的方式,NLua条件编译截图如下:
交互如下:
class Program{ static void Main(string[] args) { NLua.Lua lua = new NLua.Lua();//初始化Lua虚拟机 lua.LoadCLRPackage();//加载clr,可以在Lua中随意调用C#的DLL Program p = new Program(); lua["this"] = p;//将对象传入Lua,对象的所有的公开方法都可以在lua中使用 lua.DoFile("test.lua");//调用文件test.lua,可以在文件中使用this } public void DebugOut(object msg) { Console.WriteLine(msg); }}
二、C#调用C++的DLL以及动态生成方法
无论采用DLLImport还是kernel32.dll中的三个函数的方法,都必须事先声明方法或者委托,由于我们想实现动态的生成委托或者方法来调用,所以必须动态的生成与C++DLL中的函数相同签名的方法。故在网上查询了一下,将别人的博文偷过来:原文请参考这里
/// <summary> /// 参数传递方式枚举,ByValue 表示值传递,ByRef 表示址传递 /// </summary> public enum ModePass { ByValue = 0x0001, ByRef = 0x0002 } /// <summary> /// 常见类型 /// </summary> public enum CLRType { String=0, Int32=1, Double=2, Bool=3 } public class DllHelper { [DllImport("kernel32.dll")] public static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProceName); [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)] public static extern bool FreeLibrary(IntPtr hModule); /// <summary> /// Loadlibrary 返回的函数库模块的句柄 /// </summary> private IntPtr hModule = IntPtr.Zero; /// <summary> /// GetProcAddress 返回的函数指针 /// </summary> private IntPtr farProc = IntPtr.Zero; /// <summary> /// 装载 Dll /// </summary> /// <param name="lpFileName">DLL 文件名 </param> public void LoadDll(string lpFileName) { hModule = LoadLibrary(lpFileName); if (hModule == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpFileName + ".")); } /// <summary> /// 获得函数指针 /// </summary> /// <param name="lpProcName"> 调用函数的名称 </param> public void LoadFun(string lpProcName) { // 若函数库模块的句柄为空,则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); // 取得函数指针 farProc = GetProcAddress(hModule, lpProcName); // 若函数指针,则抛出异常 if (farProc == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpProcName + " 这个函数的入口点 ")); } /// <summary> /// 卸载 Dll /// </summary> public void UnLoadDll() { FreeLibrary(hModule); hModule = IntPtr.Zero; farProc = IntPtr.Zero; } /// <summary> /// 调用所设定的函数 /// </summary> /// <param name="ObjArray_Parameter"> 实参 </param> /// <param name="TypeArray_ParameterType"> 实参类型 </param> /// <param name="ModePassArray_Parameter"> 实参传送方式 </param> /// <param name="Type_Return"> 返回类型 </param> /// <returns> 返回所调用函数的 object</returns> public object Invoke(object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return) { // 下面 3 个 if 是进行安全检查 , 若不能通过 , 则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); if (farProc == IntPtr.Zero) throw (new Exception(" 函数指针为空 , 请确保已进行 LoadFun 操作 !")); if (ObjArray_Parameter.Length != ModePassArray_Parameter.Length) throw (new Exception(" 参数个数及其传递方式的个数不匹配 .")); // 下面是创建 MyAssemblyName 对象并设置其 Name 属性 AssemblyName MyAssemblyName = new AssemblyName(); MyAssemblyName.Name = "InvokeFun"; // 生成单模块配件 AssemblyBuilder MyAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(MyAssemblyName, AssemblyBuilderAccess.Run); ModuleBuilder MyModuleBuilder = MyAssemblyBuilder.DefineDynamicModule("InvokeDll"); // 定义要调用的方法 , 方法名为“ MyFun ”,返回类型是“ Type_Return ”参数类型是“ TypeArray_ParameterType ” MethodBuilder MyMethodBuilder = MyModuleBuilder.DefineGlobalMethod("MyFun", MethodAttributes.Public | MethodAttributes.Static, Type_Return, TypeArray_ParameterType); // 获取一个 ILGenerator ,用于发送所需的 IL ILGenerator IL = MyMethodBuilder.GetILGenerator(); int i; for (i = 0; i < ObjArray_Parameter.Length; i++) {// 用循环将参数依次压入堆栈 switch (ModePassArray_Parameter[i]) { case ModePass.ByValue: IL.Emit(OpCodes.Ldarg, i); break; case ModePass.ByRef: IL.Emit(OpCodes.Ldarga, i); break; default: throw (new Exception(" 第 " + (i + 1).ToString() + " 个参数没有给定正确的传递方式 .")); } } if (IntPtr.Size == 4) {// 判断处理器类型 IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32()); } else if (IntPtr.Size == 8) { IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64()); } else { throw new PlatformNotSupportedException(); } IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, Type_Return, TypeArray_ParameterType); IL.Emit(OpCodes.Ret); // 返回值 MyModuleBuilder.CreateGlobalFunctions(); // 取得方法信息 MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod("MyFun"); return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 调用方法,并返回其值 } /// <summary> /// 调用方法,参数类型默认按照值传递,如果不是按照值传递,则需要全部写上按什么传递 /// </summary> /// <param name="paras">调用方法所需要的参数</param> /// <param name="returnType">方法的返回值类型</param> /// <param name="modePassArray_Parameter">参数按照什么传递</param> /// <returns></returns> public object Invoke(object[] paras,Type returnType, ModePass[] modePassArray_Parameter=null) { if (paras == null) { paras = new object[] { }; } Type[] paras_type; //传递的参数类型 if (paras.Length == 0) { paras_type = new Type[] { }; modePassArray_Parameter=new ModePass[]{}; } else { paras_type = new Type[paras.Length]; for (int i = 0; i < paras.Length; i++) { paras_type[i] = paras[i].GetType(); } if (modePassArray_Parameter == null || modePassArray_Parameter.Length == 0) { modePassArray_Parameter = new ModePass[paras.Length]; for (int i = 0; i < paras.Length; i++) { modePassArray_Parameter[i] = ModePass.ByValue;//默认按照值传递 } } } return Invoke(paras, paras_type, modePassArray_Parameter, returnType); } public object Invoke(NLua.LuaTable paraTable,NLua.LuaTable paraTypeTable, int returnType, NLua.LuaTable paraPassTable = null) { Type type=null; type = GetClrType((CLRType)returnType); if (paraTable == null || paraTable.Keys.Count == 0) { return Invoke(new object[] { }, new Type[] { }, new ModePass[] { }, type); } else { object[] paras = new object[paraTable.Keys.Count];//参数 Type[] parasType = new Type[paraTypeTable.Keys.Count];//传递参数类型 ModePass[] parasPassType = new ModePass[paraTable.Keys.Count];//以什么样的方式传递 Type returnValueType=GetClrType((CLRType)(returnType)); int count2 = 0; foreach (var key in paraTypeTable.Keys) { parasType[count2] = GetClrType((CLRType)int.Parse(paraTypeTable[key].ToString())); count2++; } int count1 = 0; foreach (var key in paraTable.Keys) { paras[count1] = ConvertToType(parasType[count1], paraTable[key]);//从Lua中传过来的数字都是double,所以要再次转一次 count1++; } if (paraPassTable == null ) { for (int i = 0; i < parasPassType.Length; i++) { parasPassType[i] = ModePass.ByValue;//默认按照值传递 } } else if (paraPassTable.Keys.Count == 0) { for (int i = 0; i < parasPassType.Length; i++) { parasPassType[i] = ModePass.ByValue;//默认按照值传递 } } else { int count3 = 0; foreach (var key in paraPassTable.Keys) { parasPassType[count3] = (ModePass)int.Parse(paraPassTable[key].ToString()); count3++; } } return Invoke(paras, parasType, parasPassType,returnValueType); } } private Type GetClrType(CLRType type) { switch (type) { case CLRType.String: return typeof(string); case CLRType.Int32: return typeof(int); case CLRType.Double: return typeof(double); case CLRType.Bool: return typeof(bool); default: return typeof(object); } } private object ConvertToType(Type type,object value) { if(type==typeof(string)) { return value.ToString(); } else if(type==typeof(int)) { return int.Parse(value.ToString()); } else if(type==typeof(double)) { return double.Parse(value.ToString()); } else if (type == typeof(bool)) { return bool.Parse(value.ToString()); } else { return value; } } }
三、演示:
现有test.lua在程序更目录下,test.lua的内容如下:
ClrType={String=0,Int32=1,Double=2,Bool=3};ModePass={ByValue=1,ByRef=2};function Main() dllHelper:LoadDll("MFCSuibian.dll"); dllHelper:LoadFun("Add"); local ret=dllHelper:Invoke({1,9},{ClrType.Int32,ClrType.Int32},ClrType.Int32); this:DebugOut(ret); ret=dllHelper:Invoke({1,100},{ClrType.Int32,ClrType.Int32},ClrType.Int32); this:DebugOut(ret);end
其中MFCSuibian.dll为C++的DLL,位于程序运行目录下,其中有两个函数,“Add”和“Devide”,传入两个int,分别返回其值得和与差。
然后使用C#来调用Lua中的Main函数:
class Program { static void Main(string[] args) { NLua.Lua lua = new NLua.Lua();//初始化Lua虚拟机 lua.LoadCLRPackage();//加载clr,可以在Lua中随意调用C#的DLL Program p = new Program(); lua["this"] = p;//将对象传入Lua,对象的所有的公开方法都可以在lua中使用 DllHelper dllHelper = new DllHelper(); lua["dllHelper"] = dllHelper; lua.DoFile("test.lua"); lua.DoString("return Main()"); Console.Read(); } public void DebugOut(object msg) { Console.WriteLine(msg); } }
最后运行结果:
再调用user32.dll中的CloseClipboard方法。该方法不需要传入参数,返回一个Bool值。修改test.lua的Main函数代码如下:
ClrType={String=0,Int32=1,Double=2,Bool=3};ModePass={ByValue=1,ByRef=2};function Main() dllHelper:LoadDll("user32.dll"); dllHelper:LoadFun("CloseClipboard"); local ret=dllHelper:Invoke(nil,nil,ClrType.Bool); this:DebugOut(ret);end
运行结果如下:
本文实例下载(含NLua源码)
- Lua结合C#调用C++或者C的函数
- [lua]C调用lua函数
- Lua调用C函数
- lua 调用c函数
- Lua调用C函数
- lua调用c函数
- Lua调用C函数
- lua调用C函数
- lua调用C函数
- lua调用C函数
- Lua调用C函数
- lua调用c函数
- lua调用C函数
- lua调用C函数
- Lua调用C函数
- lua 调用c函数
- Lua调用C函数
- Lua调用C函数
- 好玩的Python库tqdm
- leetcode编程记录1 #8 String to Integer (atoi)
- 阿里云服务器搭建SVN服务器
- 设计模式——单例模式
- 机器学习--无监督学习之K-means聚类方法
- Lua结合C#调用C++或者C的函数
- Weblogic三种部署方式
- 又来继续写博客了,搬家wuit.top
- 《大型网站技术架构》读书笔记(二)——大型网站系统架构图
- Windows 版 SourceTree 免登录跳过初始设置的方法
- 输入一个链表,反转链表后,输出链表的所有元素。
- 抽取Fragment 和对抽取的Fragment进行应用(心得用的时候稍微修改就可以了)
- 带你走进SAP项目实施过程——立项(1)
- php环境之WampServer(wamp)集成环境的搭建