这个问题是我目前在做企微服务商开发以来周期最长、最折磨人的一个问题了;
从3月开始着手排查问题(其实在开发之初就发现了该问题,迫于进度,就暂时搁置了),其中断断续续去尝试解决,并且没有企微对接人,只能社区咨询以及demo调试,加之期间需求不断,也就拖到了当下;
加之企微并没有提供C#对接SDK的demo,我开发起初也仅能根据Java的demo进行开发(此处埋下伏笔);
在使用会话存档SDK(C++开发的SDK)拉取会话媒体文件(视频、文件、语音、图片)时,导致程序崩溃;后排查发现,仅当媒体文件大于512k时,也就是走分片拉取是时才会引发崩溃;且在本地调试时,可能成功,且成功后后续大多都成功;
1、确定崩溃异常信息;崩溃后捕获到的信息为:double free,甚至使用了WinDbg;
2、排查异常点:涉及内存释放的有一下地方FreeSlice()
、FreeMediaData()
,再综合实际情况,是在拉取媒体文件时导致的,故可定位到该方法上;
3、定位具体代码行:经过具体调试,确定只有经过GetOutIndexBuf()
,再进行FreeMediaData()
才会崩溃;
问题点是定位了,但是,哪里引起问题呢?这是一个最大的问题,当时我就蒙圈了;来来回回尝试了很多次,始终是没有解决:
最后,还是出动了我司架构师大佬帮忙,在我描述我排错过程以及最终错误定位后,展开了“诊断”;果不其然,架构师就是架构师,三两下给我治得服服帖帖;
话不多说,直接上代码:
// 存在问题代码 [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类型转换问题。传值,与接收返回值问题