C#中调用C的DLL中的回调函数,想实现消息响应机制
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#中调用C的DLL中的回调函数,想实现消息响应机制相关的知识,希望对你有一定的参考价值。
已经可以通过C#实现DLL中回调函数的调用,代码如下
//实时信息回调函数
[System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.Cdecl)]
public delegate void REALTIME_ROADPOINT_CALLBACK(ref wayPoint_S waypoint, IntPtr arg);
[DllImport("libserviceinterface.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void rs_setcallback_realtime_roadpoint(UInt16 rshd, [MarshalAs(UnmanagedType.FunctionPtr)]REALTIME_ROADPOINT_CALLBACK CurrentPositionCallback, IntPtr arg);
//回调函数
static void CurrentPositionCallback(ref wayPoint_S waypoint, IntPtr arg)
PrintWaypoint(waypoint);
static void PrintWaypoint(wayPoint_S point)
Console.Out.WriteLine("pos.x=0 y=1 z=2", point.cartPos.x, point.cartPos.y, point.cartPos.z);
//主函数
static void Main(string[] args)
//函数指针实例化
REALTIME_ROADPOINT_CALLBACK RobotPosCallBack = new REALTIME_ROADPOINT_CALLBACK(CurrentPositionCallback);
rs_setcallback_realtime_roadpoint(rshd, RobotPosCallBack, IntPtr.Zero);
//打印路点信息
while(true)
PrintWaypoint(waypoint);
但是这种用WHILE循环读取的方式实在不好,能不能有哪位高手指点下如何改为响应消息的机制,最好直接在我的代码上修改一下,谢谢
我主要是想能够实时得到该消息,又不想依靠WHILE循环无休止的跑去获取,类似于中断的方式获取
1.模仿linux中的管道(PIPE),产生虚拟文件,而C++阻塞直到文件产生或本身超时。这样可以实现数据交换,既能实现传数据又可以使C++程序及时得到通知。缺点就是IO密集,降低效率。
2.使用TCP通信,监听端口获取消息,这是跨平台程序通信最常用也是最正统的方法。一般是跨操作系统编程常用的,比如windows和linux ubuntu server程序进行交互。你所述情况虽然不算跨操作系统通信,但是也是跨平台了。而且想想就知道C++作为先出现的语言而且是编译型语言,调用C#托管区的函数还要设回调,其难度可想而知。
3.如果你的C++是基于winform的,可以在C#中P/Invoke调用winAPI的SendMessage发送窗体消息从而间接实现回调。
4.自然还有一些更底层的Dirty方法,比如嵌汇编更改上下文程序指针地址。。。不过不适合正统的程序,因为太Dirty啊。。。而且出错概率大。
除此之外,如果不采用多线程通信,C#的反射(reflection)特性可以参考,但是控制权依然在C#。况且C#无从知道C++的内部结构,自然难以直接调用。你想想吧,是不是这个道理~ 参考技术A
你这样写代码不是会疯狂输出吗,下面这样就行了,回调函数只有在需要的时候自动触发
下面是按ESC退出,把你的while循环改一下
while(Console.ReadKey(true).Key != ConsoleKey.Escape)追问
谢谢回答,我就是希望做成消息响应的方式,但何时消息会传递过来我并不知道啊,如何才能够知道呢。你写的这个是等待我的键盘输入,而不是真的消息传递过来了啊
本回答被提问者采纳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);
结构体包含有三个回调函数,并且每个函数也包含有一个结构体(具体不在给出)。上层程序调用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 }
动态调用流程
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 }
1 public static bool FreeDll() { 2 if((UInt64)libhandle!=0L) 3 NaticeMethed.FreeLibrary(libhandle); 4 return true; 5 }
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);
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 }
6. char* 直接使用string,如果有乱码在调整;
7. 尽量不要使用unsafe
以上是关于C#中调用C的DLL中的回调函数,想实现消息响应机制的主要内容,如果未能解决你的问题,请参考以下文章