记一次被非托管代码支配的恐惧「C#对接企微会话存档SDK问题修复」

这个问题是我目前在做企微服务商开发以来周期最长、最折磨人的一个问题了;
从3月开始着手排查问题(其实在开发之初就发现了该问题,迫于进度,就暂时搁置了),其中断断续续去尝试解决,并且没有企微对接人,只能社区咨询以及demo调试,加之期间需求不断,也就拖到了当下;
加之企微并没有提供C#对接SDK的demo,我开发起初也仅能根据Java的demo进行开发(此处埋下伏笔);

问题

在使用会话存档SDK(C++开发的SDK)拉取会话媒体文件(视频、文件、语音、图片)时,导致程序崩溃;后排查发现,仅当媒体文件大于512k时,也就是走分片拉取是时才会引发崩溃;且在本地调试时,可能成功,且成功后后续大多都成功;

排查

1、确定崩溃异常信息;崩溃后捕获到的信息为:double free,甚至使用了WinDbg;
2、排查异常点:涉及内存释放的有一下地方FreeSlice()FreeMediaData(),再综合实际情况,是在拉取媒体文件时导致的,故可定位到该方法上;
3、定位具体代码行:经过具体调试,确定只有经过GetOutIndexBuf(),再进行FreeMediaData()才会崩溃;

解决

问题点是定位了,但是,哪里引起问题呢?这是一个最大的问题,当时我就蒙圈了;来来回回尝试了很多次,始终是没有解决:

  1. 从异步方法转为同步方法;
  2. 降级、升级 .NET 版本,甚至使用 Framework 4.8 版本;

最后,还是出动了我司架构师大佬帮忙,在我描述我排错过程以及最终错误定位后,展开了“诊断”;果不其然,架构师就是架构师,三两下给我治得服服帖帖;
话不多说,直接上代码:

// 存在问题代码
[DllImport(DllName)]
public static extern string GetOutIndexBuf(IntPtr mediaData);

// 调用
var outIndexBuf = FinanceAdapter.GetOutIndexBuf(mediaData);
// 调整后代码
[DllImport(DllName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetOutIndexBuf(IntPtr mediaData);

// 调用
var oibPtr = FinanceAdapter.GetOutIndexBuf(mediaData);
var outIndexBuf = Marshal.PtrToStringAnsi(oibPtr);

想必做 P/Invoke 开发的大佬看到这已经笑出的猪叫,这什么玩意?哈哈哈哈,我也是哭笑不得;

微软文档也说明了这一问题:互操作指南-字符串参数,以及相关文档:Passing Strings Between Managed and Unmanaged Code
也就是在我调用GetOutIndexBuf返回string后,导致了mediaData被销毁了,也就引发了后边进行释放内存的异常,进而导致崩溃;

且回头仔细一看c的案例,不难发现,实际在我们调用GetOutIndexBuf时返回的是char*,是指针变量,而我用string去接收,这哪能没错呀......

typedef struct MediaData {
    char* outindexbuf;
    int out_len;
    char* data;    
    int data_len;
    int is_finish;
} MediaData_t;

char* GetOutIndexBuf(MediaData_t* media_data);

不过,这得怪java (:(手动滑稽)

public native static String GetOutIndexBuf(long mediaData);

总结

至此,这个无从下手的问题得以解决,问题的产生根源还是自己太菜了以及不了解 P/Invoke ;解决该问题的过程也让我涨了不少知识,还简单的使用了WinDbg(这玩意是真的牛,前提得看得懂),收获颇丰;
目前C#版本的会话存档SDK对接也达到了可用的状态(至少目前对于我们的业务来说是没问题了),在此我们也提供了相关源码,相关.NETer也可以参考参考,少走些弯路;

源码地址:
wework-finance-sdk-csharp
相关文章:
1、C#与C++ dll 之间传递字符串string wchar_t* char* IntPtr
2、c#调用c++ dll const char* String类型转换问题。传值,与接收返回值问题

posted @ 2022-09-02 23:51  Memoyu  阅读(145)  评论(0编辑  收藏  举报