Delphi7调用Delphi11编写的DLL字符串函数触发无效指针错误求助
问题根源
你遇到的问题核心在于Delphi 7和Delphi 11的字符串类型不兼容:
- Delphi 7默认使用
AnsiString(单字节字符),而Delphi 11默认使用UnicodeString(双字节字符),两者内部存储结构完全不同。 - 虽然两边都引入了
ShareMem共享内存管理器,但ShareMem仅能保证同版本Delphi的字符串内存管理兼容。跨版本场景下,宿主(Delphi7)尝试释放DLL(Delphi11)分配的字符串内存时,因内存块元数据不匹配,直接触发EInvalidPointer错误。 - 整数相加函数正常,是因为整数是值类型,不需要跨模块的内存分配/释放操作,不存在内存管理冲突。
解决方案
以下是几种可行的解决方式,按推荐程度排序:
方案1:使用PChar+标准内存分配函数(最稳妥的跨版本方案)
避免直接传递Delphi原生字符串,改用PChar作为参数和返回值,同时用系统标准内存分配函数(如CoTaskMemAlloc/CoTaskMemFree)管理内存,确保跨模块内存操作一致。
DLL端(Delphi11)代码:
library MergeDLL; uses SysUtils, Classes, ActiveX; function MergeText(t1, t2: PChar): PChar; stdcall; var MergedStr: string; begin MergedStr := string(t1) + string(t2); // 用CoTaskMemAlloc分配内存,宿主可用CoTaskMemFree释放 Result := CoTaskMemAlloc(Length(MergedStr) * SizeOf(WideChar) + 1); StringToWideChar(MergedStr, Result, Length(MergedStr) + 1); end; exports MergeText name 'MergeText'; begin end.
宿主端(Delphi7)代码:
type TMergeText = function(t1, t2: PChar): PChar; stdcall; procedure TForm1.btnMergeClick(Sender: TObject); var handle: THandle; MergeText: TMergeText; LNewText: string; P: PChar; begin handle := LoadLibrary(PChar(dll_path)); if handle <> 0 then begin @MergeText := GetProcAddress(handle, 'MergeText'); if @MergeText <> nil then begin P := MergeText(PChar(edText1.Text), PChar(edText2.Text)); if P <> nil then begin LNewText := P; CoTaskMemFree(P); // 必须释放DLL分配的内存 edMerged.Text := LNewText; end; end; FreeLibrary(handle); end; end;
方案2:统一使用AnsiString(简单但仅支持ANSI字符)
在Delphi11中显式使用AnsiString作为参数和返回值,让两边字符串类型一致,配合ShareMem实现内存管理兼容。
DLL端(Delphi11)代码:
library MergeDLL; uses ShareMem, SysUtils, Classes; function MergeText(t1, t2: AnsiString): AnsiString; stdcall; begin Result := t1 + t2; end; exports MergeText name 'MergeText'; begin end.
宿主端(Delphi7)代码:
// 注意:TMergeText的参数和返回值要改成AnsiString type TMergeText = function(t1, t2: AnsiString): AnsiString; stdcall; procedure TForm1.btnMergeClick(Sender: TObject); var handle: THandle; MergeText: TMergeText; LNewText: AnsiString; begin handle := LoadLibrary(PChar(dll_path)); if handle <> 0 then begin @MergeText := GetProcAddress(handle, 'MergeText'); if @MergeText <> nil then begin LNewText := MergeText(AnsiString(edText1.Text), AnsiString(edText2.Text)); edMerged.Text := string(LNewText); end; FreeLibrary(handle); end; end;
注意:此方案仅适用于不需要Unicode字符的场景,因为AnsiString无法正确存储多字节Unicode字符。
方案3:使用COM接口(面向对象的优雅方案)
通过COM接口封装字符串操作,COM的内存管理模型跨平台/跨版本兼容,Delphi对COM接口的支持完善。
DLL端(Delphi11)代码:
library MergeDLL; uses SysUtils, Classes, ComObj; type IMergeService = interface(IUnknown) ['{YOUR-GUID-HERE}'] // 用Delphi工具生成唯一GUID function MergeText(const t1, t2: WideString): WideString; stdcall; end; TMergeService = class(TInterfacedObject, IMergeService) public function MergeText(const t1, t2: WideString): WideString; stdcall; end; function TMergeService.MergeText(const t1, t2: WideString): WideString; begin Result := t1 + t2; end; function CreateMergeService: IMergeService; stdcall; begin Result := TMergeService.Create; end; exports CreateMergeService name 'CreateMergeService'; begin end.
宿主端(Delphi7)代码:
type IMergeService = interface(IUnknown) ['{YOUR-GUID-HERE}'] // 必须和DLL中的GUID完全一致 function MergeText(const t1, t2: WideString): WideString; stdcall; end; TCreateMergeService = function: IMergeService; stdcall; procedure TForm1.btnMergeClick(Sender: TObject); var handle: THandle; CreateMergeService: TCreateMergeService; MergeService: IMergeService; LNewText: WideString; begin handle := LoadLibrary(PChar(dll_path)); if handle <> 0 then begin @CreateMergeService := GetProcAddress(handle, 'CreateMergeService'); if @CreateMergeService <> nil then begin MergeService := CreateMergeService; if Assigned(MergeService) then begin LNewText := MergeService.MergeText(WideString(edText1.Text), WideString(edText2.Text)); edMerged.Text := string(LNewText); end; end; FreeLibrary(handle); end; end;
注意:生成GUID可使用Delphi的
Tools->Create GUID工具,确保两边接口的GUID完全一致。
内容的提问来源于stack exchange,提问作者Stefan van Roosmalen
相关产品推荐
相关产品推荐

