C++ dll 回调C# 程序总结

Posted lite

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ dll 回调C# 程序总结相关的知识,希望对你有一定的参考价值。

最近开发过程中遇到一个调用C++ dll失败的问题,抛出异常“尝试读取或写入受保护的内存。这通常指示其他内存已损坏。”,

“AccessViolation 0xc0000005”。网上找了一些资料,千篇一律,没能解决问题。由于没办法对dll 进行调试,导致出现什么问题都不清楚。

具体程序是这样的,C++ dll 提供一个导出函数,供上层应用调用,函数参数是一个结构体,

 

技术分享图片
 1 [StructLayout(LayoutKind.Sequential, Pack = 1)]  
 2 public struct InitParam{  
 3     public IntPtr hcontext;  
 4     [MarshalAs(UnmanagedType.FunctionPtr)]  
 5     public CheckCallBack checkCallback;  
 6     [MarshalAs(UnmanagedType.FunctionPtr)]  
 7     public TestCallBack CallBack;  
 8     [MarshalAs(UnmanagedType.FunctionPtr)]  
 9     public CheckCtrlCallBack checkCtrlCallBack;  
10 }  
11 
12 public delegate uint CheckCallBack(IntPtr hcontext,ref CheckRec Check);  
13 
14 public delegate uint TestCallBack(IntPtr hcontext,ref TestRec test);  
15 
16 public delegate uint CheckCtrlCallBack(IntPtr hcontext, uint dwType,ref CheckCtrlRec ctrl);  
View Code

 

结构体包含有三个回调函数,并且每个函数也包含有一个结构体(具体不在给出)。上层程序调用dll 的导出函数,

把包含函数指针的InitParam 结构体传给dll, dll 处理完数据后会自动调用相应的 回调函数触发上层程序(C#程序事件被触发)。

总结如下:

1.结构体基本采用  [StructLayout(LayoutKind.Sequential, Pack = 1)] 的属性声明,涉及函数指针的要声明 [MarshalAs(UnmanagedType.FunctionPtr)]  属性;

2.  CallBack 函数采用委托的形式;

3.  所有涉及结构体的都要加 ref (重要)

4 . 调用导出函数 可以采用动态和静态的两种方式,静态写起来比较方便,动态麻烦点

 

技术分享图片
 1 //[DllImport("Test.dll")]  
 2 //public extern static uint Test(ref InitParam param);  
 3 
 4 public static class NaticeMethed  
 5 {  
 6     [DllImport("kernel32.dll")]  
 7     public extern static IntPtr LoadLibrary(string path);  
 8   
 9     [DllImport("kernel32.dll")]  
10     public extern static IntPtr GetProcAddress(IntPtr lib, string funcName);  
11   
12     [DllImport("kernel32.dll")]  
13     public extern static bool FreeLibrary(IntPtr lib);  
14 }  
View Code

 

动态调用流程 

1. 载入dll,

2. 获取要调用的函数指针

3. 使用 Marshal.GetDelegateForFunctionPointer 把函数指针转换为委托形式。

4. 触发委托实例

5. 程序结束前把dll free掉

 

技术分享图片
 1 public delegate uint Test(ref InitParam param);  
 2      private static IntPtr libhandle;  
 3      public static IntPtr LoadDll() {  
 4          //  
 5          libhandle = NaticeMethed.LoadLibrary(@"Test.dll");  
 6          if ((UInt64)libhandle == 0L) {  
 7              throw new Exception("没有载入Test.dll,请检查dll路径");  
 8          }  
 9   
10          IntPtr funchandle = NaticeMethed.GetProcAddress(libhandle, "Test");  
11          if ((UInt64)funchandle == 0L) {  
12              NaticeMethed.FreeLibrary(libhandle);  
13              throw new Exception("Test 初始化失败");  
14          }  
15          return funchandle;  
16      }  
View Code

 

技术分享图片
1 public static bool FreeDll() {  
2     if((UInt64)libhandle!=0L)  
3     NaticeMethed.FreeLibrary(libhandle);  
4     return true;          
5 }  
View Code

 

技术分享图片
 1 IntPtr ptr = LoadDll();  
 2 if ((UInt64)ptr == 0L) {  
 3 throw new Exception("初始化dll失败");  
 4 }  
 5 Test test= (Test)Marshal.GetDelegateForFunctionPointer(ptr, typeof(Test));  
 6   
 7 InitParam Init;  
 8 Init.hcontext = this.Handle;  
 9 Init.CheckCallback = new CheckCallBack(LoginForm_CheckCallback);  
10 Init.TestCallBack = null;  
11 Init.CheckCtrlCallBack = new CheckCtrlCallBack(LoginForm_CheckCtrlCallBack);  
View Code

 

技术分享图片
1 try {   
2      Test(ref Init);  // a Test Dialogs  
3 }  
4 catch(Exception ex) {  
5      Console.WriteLine(ex.Message);  
6 }  
7 finally {  
8      FreeDll();  
9 }  
View Code

 

6.  char* 直接使用string,如果有乱码在调整;

7.  尽量不要使用unsafe 

 

以上是关于C++ dll 回调C# 程序总结的主要内容,如果未能解决你的问题,请参考以下文章

将带有参数的 c# 回调方法传递给 c++ dll 会导致 System.ExecutionEngineException

C#调用C++ dll 回调

在通过 P/Invoke 获得的 C++ 结构上设置 C# 回调

C++ DLL 和 C# 应用程序 WPF:System.DllNotFoundException

C++ 到 C# 回调结构编组

C++ 回调函数到 C#