Win32C++是非托管的,C#是托管的,不管出于什么目的,有时候需要在非托管的C++里调用现存的C#类库。我经常使用的方法是修改C# il 代码,然后重新编译il以供C++调用。
有一段cs代码:
有一段cs代码:
1 using System; 2 3 namespace ManagedLib 4 5 { 6 7 public classExport 8 9 { 10 publics tatic int Add(int a, intb)//函数修饰符限定为public和static,这里是必须的 11 12 { 13 14 return a + b; 15 16 } 17 18 } 19 20 }
编译后生成ManagedLib.dll文件,用ildasm工具打开该文件,然后保存为ManagedLib.il文件,修改以下代码:
.method public hidebysig static int32 Add(int32 a, int32 b) cil managed { // 代码大小 9 (0x9) .maxstack 2 .locals init ([0] int32 V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldarg.1 IL_0003: add IL_0004: stloc.0 IL_0005: br.s IL_0007 IL_0007: ldloc.0 IL_0008: ret }
修改后的代码:
.method public hidebysig static int32 modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall) Add(int32 a, int32 b) cil managed { .vtentry 1 : 1 .export [0] as Add // 代码大小 9 (0x9) .maxstack 2 .locals init (int32 V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldarg.1 IL_0003: add IL_0004: stloc.0 IL_0005: br.s IL_0007 IL_0007: ldloc.0 IL_0008: ret }
用ilasm工具编译il文件,编译成功后.corflags 自动变成了0x00000002,同时增加了如下指令
.vtfixup [1] int32 fromunmanaged at D_00004000 .data D_00004000 = bytearray (05 00 00 06)
增加.vtfixup 和.data两条指令负是为了责非托管导出。
现在,假设有x个函数,在需要导出的函数内部分别增加两条指令,其中,x对应要导出的函数个数
.vtentry 1 : 2 .export [1] as 函数名 ...... .vtentry 1 : x .export [x-1] as 函数名
在C++中动态调用Add函数
把ManagedLib.dll文件拷贝到C++应用程序目录,在C++文件中增加以下代码:
1 typedef int(_stdcall *Dllfun)(int,int);//必须设置_stdcall,否则运行时出错。 2 3 void ManagedCall() 4 5 { 6 Dllfun addfun; 7 8 HINSTANCE hdll; 9 10 hdll= LoadLibrary(_T("ManagedLib.dll")); 11 12 if(hdll != NULL) 13 14 { 15 16 addfun= (Dllfun)GetProcAddress(hdll, "Add"); 17 18 if(addfun != NULL) { 19 20 int result = addfun(1, 2); 21 22 LPTSTR szBuffer = new TCHAR[100]; 23 24 _snwprintf_s(szBuffer,100, 100, L"a+b:%d",result); 25 26 MessageBoxEx(NULL,szBuffer, _T("ManagedLib.dll"),0, 0); 27 28 } 29 30 FreeLibrary(hdll); 31 32 } 33 34 }
后记:
在VS2015中,NuGet程序包里有自动编译工具UnmanagedExports,设置目标平台为X86或X64,编译时IDE可以很方便的完成以上麻烦过程,不过在中文的VS2015里,程序总是编译不成功。只有自己写一个重编译工具,难度不高。