惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - sPhinX

如何解决在Win11下卸载McgsPro失败的问题 如何离线安装WinDbg Preview 敏捷软件开发 原则、模式与实践 第9章的例子程序(C#版) Akavache简明使用指南 Oracle存储过程解析XML内容 P/Invoke继续谈 有意思的案例: 的问题 dnSpy - 让调试镜像文件的工作变得轻松点 dnSpy调试IIS(w3wp进程) Xilium.CefGlue与SingleProcess rocketmq-client-cpp(2.0.1)编译指南 RocketMQ .NET客户端的那些坑 .NET编译问题汇总 动态的世界 使用ProcDump自动生成Dump文件 Process.Start可能无法选中指定文件的问题 获取本地IP 将exe和dll打包为一个exe文件 .NET异步资料收集
P/Invoke今日谈
sPhinX · 2020-03-14 · via 博客园 - sPhinX

没想到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);