You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用DLL获取计算机名后执行FreeLibrary释放时出现Access violation错误

问题根因

这个问题是Delphi跨模块传递托管类型的典型错误,核心原因有两个:

  1. Delphi的原生String是托管内存类型,内存分配、引用计数、释放逻辑都绑定对应模块的内存管理器。你DLL侧构造的返回值String是用DLL的内存管理器申请的内存,当这个字符串的引用计数归0要释放时,会尝试用主程序的内存管理器释放不属于它管理的内存块,本身就存在内存访问风险。
  2. 你在字符串还没被主程序清理的前提下就先调用FreeLibrary卸载了DLL,如果后续字符串释放逻辑依赖DLL侧的内存管理器代码/数据段,就会直接触发访问违规。注释FreeLibrary后DLL一直驻留内存,所以不会触发崩溃,但会遗留DLL资源泄漏的问题。

修复方案

推荐使用兼容性最好的预分配缓冲区传参方案,完全规避跨模块托管内存传递的问题:

1. 修改DLL侧实现

// 显式指定stdcall通用调用约定,由主程序分配缓冲区,DLL仅负责写入内容
function NAMEPC(lpBuffer: PChar; var nSize: DWORD): BOOL; stdcall;
const Buff_Size = MAX_COMPUTERNAME_LENGTH + 1;
begin
  Result := False;
  // 先校验传入的缓冲区大小是否足够
  if nSize < Buff_Size then Exit;
  Result := GetComputerName(lpBuffer, nSize);
end;

exports
  NAMEPC;

2. 修改主程序调用逻辑

function CALLNAMEPC: String;
type
  // 声明和DLL侧匹配的函数原型,必须严格对应参数和调用约定
  TNamePCFunc = function(lpBuffer: PChar; var nSize: DWORD): BOOL; stdcall;
var
  hDll: THandle;
  pFunc: TNamePCFunc;
  arrBuffer: array[0..MAX_COMPUTERNAME_LENGTH] of Char;
  dwSize: DWORD;
begin
  Result := '';
  hDll := LoadLibrary('DLL.dll');
  if hDll = 0 then Exit;
  try
    pFunc := GetProcAddress(hDll, 'NAMEPC');
    if Assigned(pFunc) then
    begin
      dwSize := MAX_COMPUTERNAME_LENGTH + 1;
      if pFunc(@arrBuffer[0], dwSize) then
        Result := StrPas(@arrBuffer[0]);
    end
    else
      Application.MessageBox('ERROR!', 'Microsoft Windows', MB_ICONERROR);
  finally
    // 现在DLL仅做写入操作,不需要持有任何传给主程序的内存,释放不会有问题
    FreeLibrary(hDll);
  end;
end;

如果不想改函数签名,也可以选择在DLL和主程序的工程文件所有引用单元的最顶部引入ShareMem单元,让两个模块共享同一份内存管理器,也能解决问题,但该方案要求DLL和主程序必须使用相同版本的Delphi编译,不支持跨语言调用。

内容的提问来源于stack exchange,提问作者David Kennedy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 05:15:08