如何解决PJSUA C#(DLL PINVOKE)中LogWriter释放时的System.AccessViolationException
解决方案
核心原因
当你将自定义LogWriter赋值给epConfig.logConfig.writer后,PJSUA2的非托管代码会接管该对象的内存所有权。但SWIG生成的默认Dispose方法仍会尝试调用delete_LogWriter释放内存,导致重复释放或访问已被非托管代码销毁的内存,从而抛出AccessViolationException。
方案1:重写Dispose方法阻止.NET释放非托管内存
在自定义的MyLogWriter类中重写Dispose方法,跳过SWIG生成的delete_LogWriter调用:
public class MyLogWriter : LogWriter { protected override void Dispose(bool disposing) { lock (this) { if (swigCPtr.Handle != IntPtr.Zero) { // 标记不再拥有内存所有权,避免触发非托管delete操作 swigCMemOwn = false; // 重置句柄,防止后续重复处理 swigCPtr = new HandleRef(null, IntPtr.Zero); } // 不要调用base.Dispose(disposing),避免执行原有的delete逻辑 } } }
方案2:手动转移内存所有权给非托管代码
在将LogWriter赋值给配置后,直接修改swigCMemOwn字段,让.NET放弃内存管理权限:
MyLogWriter writer = new MyLogWriter(); epConfig.logConfig.writer = writer; // 转移所有权至非托管代码,.NET不再尝试释放 writer.swigCMemOwn = false;
方案3:调整清理顺序确保非托管代码先释放
在应用关闭时,先让Endpoint停止使用LogWriter,再执行销毁和释放操作:
// 第一步:移除Endpoint对LogWriter的引用 epConfig.logConfig.writer = null; // 第二步:销毁PJSUA2库 ep.libdestroy(); // 第三步:安全释放托管侧的LogWriter(如果所有权仍在.NET) writer.Dispose();
额外注意事项
- 如果修改SWIG接口文件,可以通过
%nodefaultdtor禁用默认析构函数,或用%delobject明确指定内存释放的责任方,从根源避免冲突。 - 确保所有PJSUA2对象的释放顺序符合官方文档要求:先销毁会话、呼叫等资源,再调用
ep.libdestroy(),最后处理全局配置对象。
内容的提问来源于stack exchange,提问作者lukas
相关产品推荐
相关产品推荐

