Lua结合C#调用C++或者C的函数

2017-09-11 14:46:28来源:网络收集作者:程序诗人人点击

分享

本文所用到的知识点:
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条件编译截图如下:
Lua结合C#调用C++或者C的函数
交互如下:


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中的函数相同签名的方法。故在网上查询了一下,将别人的博文偷过来:原文请参考这里


///
/// 参数传递方式枚举,ByValue 表示值传递,ByRef 表示址传递
///

public enum ModePass
{
ByValue = 0x0001,
ByRef = 0x0002
}
///
/// 常见类型
///

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);
///
/// Loadlibrary 返回的函数库模块的句柄
///

private IntPtr hModule = IntPtr.Zero;
///
/// GetProcAddress 返回的函数指针
///

private IntPtr farProc = IntPtr.Zero;
///
/// 装载 Dll
///

/// DLL 文件名
public void LoadDll(string lpFileName)
{
hModule = LoadLibrary(lpFileName);
if (hModule == IntPtr.Zero)
throw (new Exception(" 没有找到 :" + lpFileName + "."));
}
///
/// 获得函数指针
///

/// 调用函数的名称
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 + " 这个函数的入口点 "));
}
///
/// 卸载 Dll
///

public void UnLoadDll()
{
FreeLibrary(hModule);
hModule = IntPtr.Zero;
farProc = IntPtr.Zero;
}
///
/// 调用所设定的函数
///

/// 实参
/// 实参类型
/// 实参传送方式
/// 返回类型
/// 返回所调用函数的 object
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);// 调用方法,并返回其值
}
///
/// 调用方法,参数类型默认按照值传递,如果不是按照值传递,则需要全部写上按什么传递
///

/// 调用方法所需要的参数
/// 方法的返回值类型
/// 参数按照什么传递
///
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);
}
}

最后运行结果:
Lua结合C#调用C++或者C的函数
再调用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

运行结果如下:
Lua结合C#调用C++或者C的函数
本文实例下载(含NLua源码)




最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台