关于C#注册C++回调函数的问题

mjmkkk 2017-04-24 11:44:54
一个检测仪,COM转USB链接电脑,我需要取设备里的数据,厂家技术给了一个DLL和一个简单的说明文档就不搭理了,现在打开设备端口,控制通道都没问题,但是取到数据后点击确定就报错,折磨了2天了。跪求大家帮忙!!以下是厂家技术给的DLL说明:
—————————————————————————————————————————————————
//读取最后一条记录,通过回调函数返回数据
extern "C" _declspec(dllexport) BOOL ReadLastData() ;

//回调函数,const char *strPerc为内部字符指针,回调函数返回测量数据
typedef void (*PercCallback)(const char *strPerc);

//注册回调函数
extern "C" _declspec(dllexport) void RegisterPercCallback(PercCallback Callback);
_______________________________________________________________________________________

我的C#代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace PR202
{

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

}

[DllImport("PR202_10Dll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern Boolean OpenSerial(int nPort);
[DllImport("PR202_10Dll.dll", CharSet = CharSet.Ansi, EntryPoint = "RegisterPercCallback", CallingConvention = CallingConvention.Cdecl)]
public static extern Boolean RegisterPercCallback(PercCallback Callback);
[DllImport("PR202_10Dll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern Boolean ReadLastData();
[DllImport("PR202_10Dll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern Boolean CloseSerial();


private void Form1_Load(object sender, EventArgs e)

{

}
public delegate void PercCallback([MarshalAs(UnmanagedType.LPStr)] string pBuffer);
PercCallback callback = new PercCallback(CallBackFunction);
public static void CallBackFunction([MarshalAs(UnmanagedType.LPStr)] string pBuffer)
{
MessageBox.Show(pBuffer); //显示回调内容
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(OpenSerial(3).ToString());//打开串口
RegisterPercCallback(CallBackFunction); //注册C++ DLL 回调
}

private void button3_Click(object sender, EventArgs e)
{
ReadLastData();//读取最后一次的数据:回调数据能显示,但是点击Message.Show的确定后就抛出错误了。
}
private void button5_Click(object sender, EventArgs e)
{
MessageBox.Show(CloseSerial().ToString());
}
}

}

问题请教:
1:C++原文中:extern "C" _declspec(dllexport) void RegisterPercCallback(PercCallback Callback);
//注册回调函数
我这样注册正确吗?是先注册再调用回调?还是怎么用?
 private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(OpenSerial(3).ToString());//打开串口
RegisterPercCallback(CallBackFunction); //注册C++ DLL 回调
}


2:为什么点击这个确定后就报错?我自己判断是重复调用回调了,但是不知道怎么解决,直接ReadLastData()没反应,加上RegisterPercCallback(CallBackFunction)后又报错!!想直接执行ReadLastData()就得到数据该怎么写?

在线等,急急急!希望大家帮帮忙~!!
...全文
642 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
mjmkkk 2017-04-27
  • 打赏
  • 举报
回复
总结了一下,解决问题的方法有很多种,而自己却找了对于我这样的人来说比较难的一条路。 方法1.目前的方法:
 public partial class Form1 : Form
    {
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void PercCallback(string strResult);//窗体中 delegate,这样可以防止GC立即回收(自己理解的)
        public Form1()
         {
            InitializeComponent();
            PercCallback p = new PercCallback(TestCallBack);
            RegisterPercCallback(p);
         }
方法二:用GCHandle 或KeepAlive。。。此用法没做完。等功力深厚再继续。 非常感谢大家的帮助!谢谢!
mjmkkk 2017-04-27
  • 打赏
  • 举报
回复
自己搞定了。是我想的太过复杂!随后贴上代码。。
mjmkkk 2017-04-26
  • 打赏
  • 举报
回复
引用 19 楼 xuggzu 的回复:
stringbuilder和intptr用法怎么会不会?直接把你的回调函数参数类型改成这两个分别测试就行了。 那个marshal(unmanagedtype.lpstr)不需要,删掉。
谢谢你的解答。但是不是这个原因。 这两天一直纠结在这个问题中,昨天放松了一下。今天查阅了很多资料。终于发现一个解决这个问题的方法: GCHandle 或 KeepAlive!!!! 电梯直达:http://www.cnblogs.com/jxnclyk/archive/2010/06/09/1754456.html 我虽然明白了怎么回事,但是写不出来。程序员最痛苦的事莫过于此了吧?!(基础不好!易语言转C#来的)。 这个问题我必须要解决。要不真就废到这里了!我要进步啊!!! 就用我这个代码来说,怎么用GCHandle “钉”住 托管代码的对象,等我用完再GCFree。我必须必须要学会!希望大家帮帮忙!!!
mjmkkk 2017-04-25
  • 打赏
  • 举报
回复
引用 11 楼 zhujinqiang 的回复:
参考: http://bbs.csdn.net/topics/390623722 http://www.cnblogs.com/yisean/p/5028128.html
IntPtr Marshal.PtrToStringAnsi 后也报错。PadLeft 也报错。。 我想拿到 typedef void (*PercCallback)(const char *strPerc)的内部字符指针,然后申请内存,复制过来。用完后再释放。不知道思路对不,C# 能搞不?对C#内存不熟。求指点。。
zhujinqiang 2017-04-25
  • 打赏
  • 举报
回复
参考: http://bbs.csdn.net/topics/390623722 http://www.cnblogs.com/yisean/p/5028128.html
xuggzu 2017-04-25
  • 打赏
  • 举报
回复
stringbuilder和intptr用法怎么会不会?直接把你的回调函数参数类型改成这两个分别测试就行了。
那个marshal(unmanagedtype.lpstr)不需要,删掉。
mjmkkk 2017-04-25
  • 打赏
  • 举报
回复
引用 17 楼 xian_wwq 的回复:
[quote=引用 16 楼 mjmkkk 的回复:] [quote=引用 13 楼 xian_wwq 的回复:] 可以使用InteropSignatureToolkit帮助进行DllImport转换
public delegate void PercCallback([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string strPerc);
报错依旧。。。[/quote] 还有一处需要测试确认: CallingConvention = CallingConvention.Cdecl 改为 CallingConvention = CallingConvention.StdCall 如果参数出入栈的顺序和方法与约定不同,会导致AccessViolationException[/quote] StdCall后 在打开端口的时候就报错了,跑不下去:对 PInvoke 函数“PR202!PR202.Form1::RegisterPercCallback”的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。” Cdecl 又报 System.AccessViolationException:“尝试读取或写入受保护的内存。这通常指示其他内存已损坏。” 快奔溃了!!!
xian_wwq 2017-04-25
  • 打赏
  • 举报
回复
引用 16 楼 mjmkkk 的回复:
[quote=引用 13 楼 xian_wwq 的回复:] 可以使用InteropSignatureToolkit帮助进行DllImport转换
public delegate void PercCallback([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string strPerc);
报错依旧。。。[/quote] 还有一处需要测试确认: CallingConvention = CallingConvention.Cdecl 改为 CallingConvention = CallingConvention.StdCall 如果参数出入栈的顺序和方法与约定不同,会导致AccessViolationException
mjmkkk 2017-04-25
  • 打赏
  • 举报
回复
引用 13 楼 xian_wwq 的回复:
可以使用InteropSignatureToolkit帮助进行DllImport转换
public delegate void PercCallback([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string strPerc);
报错依旧。。。
mjmkkk 2017-04-25
  • 打赏
  • 举报
回复
引用 13 楼 xian_wwq 的回复:
可以使用InteropSignatureToolkit帮助进行DllImport转换 [quote=引用 8 楼 mjmkkk 的回复:] [quote=引用 7 楼 xuggzu 的回复:] 按常规处理,const char*是string,但如果dll内不按常规的话,就不好说了。
stringbuilder不会用,intptr返回一段数字,是内存地址吗?[/quote] stringbuilder和string的差别是: 1.使用stringbuilder需要预先分配空间, 2.stringbuilder可以处理返回值 const char*对应string没有问题。 看lz的代码有个小问题 CharSet.Ansi和CharSet.Auto同时出现了,这样可能会出现问题。 [/quote]InteropSignatureToolkit 怎么用?
mjmkkk 2017-04-25
  • 打赏
  • 举报
回复
谢谢答复。 [/quote] 按文档说明
typedef void (*PercCallback)(const char *strPerc);

//注册回调函数
extern "C" _declspec(dllexport) void RegisterPercCallback(PercCallback Callback);


 public delegate void PercCallback(string pBuffer);
         public static  void CallBackFunction(string str)
        { 
        //MessageBox.Show(str.ToString());
         }
private void button1_Click(object sender, EventArgs e)
        {

            MessageBox.Show(OpenSerial(3).ToString());

            
            RegisterPercCallback(CallBackFunction);
        }
我打开端口后 注册回调,然后CallBackFunction{}里什么都不写 ReadLastData() 就报错。 我严重怀疑DLL有问题。他们技术又联系不上。好扯。有没有不用委托 可以回调的方法?
xian_wwq 2017-04-25
  • 打赏
  • 举报
回复
可以使用InteropSignatureToolkit帮助进行DllImport转换
引用 8 楼 mjmkkk 的回复:
[quote=引用 7 楼 xuggzu 的回复:] 按常规处理,const char*是string,但如果dll内不按常规的话,就不好说了。
stringbuilder不会用,intptr返回一段数字,是内存地址吗?[/quote] stringbuilder和string的差别是: 1.使用stringbuilder需要预先分配空间, 2.stringbuilder可以处理返回值 const char*对应string没有问题。 看lz的代码有个小问题 CharSet.Ansi和CharSet.Auto同时出现了,这样可能会出现问题。
mjmkkk 2017-04-24
  • 打赏
  • 举报
回复
自己先顶一下。
mjmkkk 2017-04-24
  • 打赏
  • 举报
回复
引用 9 楼 angel6709 的回复:
要看看 ReadLastData() ; 怎么实现的了
封装好的。。看不到啊。。
angel6709 2017-04-24
  • 打赏
  • 举报
回复
要看看 ReadLastData() ; 怎么实现的了
mjmkkk 2017-04-24
  • 打赏
  • 举报
回复
引用 7 楼 xuggzu 的回复:
按常规处理,const char*是string,但如果dll内不按常规的话,就不好说了。
stringbuilder不会用,intptr返回一段数字,是内存地址吗?
xuggzu 2017-04-24
  • 打赏
  • 举报
回复
按常规处理,const char*是string,但如果dll内不按常规的话,就不好说了。
mjmkkk 2017-04-24
  • 打赏
  • 举报
回复
引用 5 楼 xuggzu 的回复:
如果去掉回调函数内部代码还崩溃,再看楼主帖子代码没发现什么不对的。 那只能说:是dll内回调函数对字符串处理欠考虑。 目前只能用不同类型的回调函数形参试,比如用stringbuilder,intptr测试
const char* 对应 C#就是srting 吧??
xuggzu 2017-04-24
  • 打赏
  • 举报
回复
如果去掉回调函数内部代码还崩溃,再看楼主帖子代码没发现什么不对的。
那只能说:是dll内回调函数对字符串处理欠考虑。
目前只能用不同类型的回调函数形参试,比如用stringbuilder,intptr测试
mjmkkk 2017-04-24
  • 打赏
  • 举报
回复
咋没人啊。有偿解决,顶顶~~
加载更多回复(2)

110,539

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧