Delphi Berlin服务调用Delphi11 DLL触发内存访问冲突求助
问题分析与解决方案
核心原因
这类跨Delphi版本(Berlin → 11)的Windows服务调用DLL出现内存访问冲突(AV)的问题,绝大多数是内存管理器不兼容或Delphi特有类型跨版本传递冲突导致。服务运行在Session 0环境,和普通EXE的运行上下文差异放大了这类问题。
具体解决方案
1. 替换Delphi原生String参数为PWideChar
Delphi不同版本的String类型虽均为Unicode,但内存管理细节存在差异,跨版本直接传递会导致内存分配/释放时的管理器冲突。
修改DLL导出函数声明:
// DLL端(Delphi 11) function RESTAPICall(sURL: PWideChar; sDomain: PWideChar; sID: PWideChar; sJson: PWideChar; slLog: TStringList): Boolean; stdcall;
服务端调用声明:
// 服务端(Delphi Berlin) function RESTAPICall(sURL: PWideChar; sDomain: PWideChar; sID: PWideChar; sJson: PWideChar; slLog: TStringList): Boolean; stdcall; External 'Restcall.dll' name 'RESTAPICall' delayed;
调用时转换字符串:
var sURL, sDomain, sID, sJson: string; slLog: TStringList; begin slLog := TStringList.Create; try // 填充参数... RESTAPICall(PWideChar(sURL), PWideChar(sDomain), PWideChar(sID), PWideChar(sJson), slLog); finally slLog.Free; end; end;
2. 解决TStringList跨版本传递问题
TStringList是Delphi类,跨版本传递时VTable结构、内存管理逻辑可能不一致,极易触发AV。
方案A:避免直接传递TStringList
改用字符串数组或单字符串拼接日志,让服务端自行解析:
// DLL端改为返回日志字符串(用分隔符分隔) function RESTAPICall(sURL: PWideChar; sDomain: PWideChar; sID: PWideChar; sJson: PWideChar; out sLog: PWideChar): Boolean; stdcall;
方案B:统一内存管理器
如果必须传递TStringList,确保服务和DLL使用相同的内存管理器:
- 两者都链接FastMM4,且版本一致。
- 在DLL的
initialization段添加ShareMM := True;,让DLL共享宿主的内存管理器。
3. 适配Session 0运行环境
Windows服务运行在Session 0,无桌面上下文,DLL中的REST调用可能依赖桌面资源导致初始化失败:
- 检查DLL中的REST客户端(如TRESTClient)是否依赖UI组件,替换为纯后台可用的HTTP客户端(如Indy的TIdHTTP)。
- 确保DLL不依赖用户Session的注册表、环境变量,服务运行账户(如Local System)有权限访问所需资源。
4. 消除延迟加载与依赖问题
- 用Dependency Walker检查DLL的依赖项,确保服务运行机器上存在Delphi 11的RTL/BPL文件,或把DLL编译为静态链接(Project Options → Linking → Use dynamic RTL = False),避免依赖外部BPL。
- 去掉
delayed加载,确保DLL在服务启动时就完成加载,避免运行时加载失败导致的部分初始化问题。
5. 确保线程安全
服务的OnTimer事件可能在非主线程触发,若DLL中的REST调用未做线程安全处理:
- 每个DLL调用创建独立的REST客户端实例,不要共享全局实例。
- 对全局共享资源添加临界区(TCriticalSection)保护。
内容的提问来源于stack exchange,提问作者Sahaya Joni
相关产品推荐
相关产品推荐

