zoukankan      html  css  js  c++  java
  • P/Invoke今日谈

    没想到10来年之后又要用到P/Invoke了,为了能用上RocketMQ,不得不用P/Invoke和rocketmq-client-cpp来打交道,花了两三天吧,把这条路彻底打通了,具体过程另外再写,这里就写一个和P/Invoke有关的小知识点。

    在用.NET客户端测试的时候,已经可以接收到消费消息的回调,但是在GetMessageBody的时候老是会出现0xc0000374的异常,查了下是内存损坏,这倒是在意料之中,P/Invoke最麻烦的就是内存损坏的问题了,各种花样层出不穷。

    幸好这次是把rocketmq-client-cpp编译通了,先是编译了个64位的Release版,后来出了内存损坏的问题,干脆一不做二不休,又花了点时间编了个Debug版出来跟踪错误。

    跟踪的结果发现是在rocketmq-client-cpp中返回Body之后出错的,而且在C++这边是没有报错的,Body数据也是有的,基本上确定问题是出在封送回.NET代码的时候,这里受了rocketmq-client-dotnet(一个封装了rocketmq-client-cpp的.NET库)的影响,

    最后才确定是这个库封装的方式有问题(但是很奇怪,难道库作者自己没发现吗?这个问题只要跑他的Demo就会发现,有点想不明白)

    下面是C++代码和原来的C#代码

    const char* GetMessageBody(CMessageExt* msg) {
    if (msg == NULL) {
    return NULL;
    }
    return ((MQMessageExt*)msg)->getBody().c_str();
    }

    [DllImport(ConstValues.RocketMQDriverDllName, CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.LPStr)]
    public static extern string GetMessageBody(IntPtr message);

    问题就是出在string::c_str()和P/Invoke对返回字符串的处理方式上。

    首先string::c_str()调用之后会在超出生存范围之后自动释放自己占用的内存;其次在这里可以看到这样的知识点:

    The CLR assumes the following two items about a PInvoke function which directly returns the string type

    • The native memory needs to be freed
    • The native memory was allocated with CoTaskMemAlloc

    仔细想想就可以得出结论,这里出现的问题在于CLR默认会去释放它认为使用CoTaskMemAlloc分配的内存,然后STL标准库再去二次释放必然就会造成问题了。(这里谁先释放谁后释放是我推测的)

    下面这段话也是点醒了我,的确用IntPtr来接收并用PtrToString来转换为字符串是最稳妥的,是否要释放内存需要视C++代码的实现来定。

    In order to get the correct semantics here you must return an IntPtr directly. Then use Marshal.PtrToString in order to get to a managed String value. You may still need to free the native memory but that will dependent upon the implementation of foo.

    所以最后的修改也比较简单,修改C#函数的签名,并自己负责转换字符串的工作

    [DllImport(ConstValues.RocketMQDriverDllName, CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr GetMessageBody(IntPtr message);

  • 相关阅读:
    【网易官方】极客战记(codecombat)攻略-森林-加农炮之舞forest-cannon-dancing
    【网易官方】极客战记(codecombat)攻略-森林-森林慢跑forest-jogging
    https://developer.android.com/codelabs/java-to-kotlin
    今日英语
    架构师技能图谱
    java接口防重提交如何处理
    看看人家那后端API接口写得,那叫一个优雅!
    MySQL不推荐使用uuid或者雪花id作为主键
    “12306”是如何支撑百万QPS的?
    阿里巴巴为什么能抗住90秒100亿?看完这篇你就明白了!
  • 原文地址:https://www.cnblogs.com/s5689412/p/12494430.html
Copyright © 2011-2022 走看看