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

如何从Delphi DLL向PHP扩展传递大字符串?

解决Delphi DLL向PHP扩展传递大Base64字符串的问题

首先得指出你当前代码里的核心问题:

  • Delphi函数中,你把局部ansistring的指针赋值给imgdata参数,但这个局部字符串在函数返回后会被Delphi的内存管理器回收,PHP扩展拿到的是野指针,访问会导致崩溃或者乱码。
  • PHP扩展里预分配的char imgdata[AS_MAXCH];是固定大小的栈内存,对于大Base64图片来说很容易溢出,造成内存错误。

下面提供两种可靠的解决方案,你可以根据需求选择:


方案1:调用方(PHP扩展)分配内存,DLL填充内容

这种方式更安全,内存由PHP扩展管理,避免跨语言内存管理的冲突问题。

步骤1:修改Delphi DLL函数

修改函数,让它接收缓冲区、缓冲区大小两个参数,返回实际需要的字符串长度(如果缓冲区不足,返回所需长度,不填充内容):

function dem_get_map_img(imgdata: PAnsiChar; bufSize: Int32): Int32; stdcall;
var
  base64str: AnsiString;
begin
  // 实际逻辑:生成Base64编码的图片字符串
  base64str := '这里替换为实际的大Base64字符串';
  
  if bufSize >= Length(base64str) + 1 then // +1是为了存储字符串结束符'\0'
  begin
    StrPCopy(imgdata, base64str); // 把AnsiString复制到缓冲区
    Result := Length(base64str); // 返回实际字符串长度
  end
  else
  begin
    Result := Length(base64str) + 1; // 返回需要的缓冲区大小(含结束符)
  end;
end;

步骤2:修改PHP扩展代码

先调用一次DLL获取所需的缓冲区大小,用PHP的内存分配函数分配足够内存,再调用一次填充内容:

PHP_FUNCTION(dem_get_map_img) {
    HMODULE hGetProcIDDelphiDLL = LoadLibrary(_T("delphiDLL.dll"));
    if (!hGetProcIDDelphiDLL) {
        RETURN_STRING("dll not loaded");
    }

    if (ZEND_NUM_ARGS() != 0) {
        FreeLibrary(hGetProcIDDelphiDLL);
        RETURN_STRING("wrong param count");
    }

    FARPROC lpfnGetProcessID = GetProcAddress(hGetProcIDDelphiDLL, "dem_get_map_img");
    if (!lpfnGetProcessID) {
        FreeLibrary(hGetProcIDDelphiDLL);
        RETURN_STRING("function not found");
    }

    // 定义函数指针类型
    typedef int32(__stdcall * pICFUNC)(char *imgdata, int32 bufSize);
    pICFUNC dem_get_map_img = (pICFUNC)lpfnGetProcessID;

    int32 requiredSize;
    // 第一次调用:传入空缓冲区,获取所需大小
    requiredSize = dem_get_map_img(NULL, 0);
    if (requiredSize <= 0) {
        FreeLibrary(hGetProcIDDelphiDLL);
        RETURN_STRING("failed to get required buffer size");
    }

    // 用PHP的内存分配函数分配内存(会被PHP内存池管理,避免泄漏)
    char *imgdata = (char *)emalloc(requiredSize);
    if (!imgdata) {
        FreeLibrary(hGetProcIDDelphiDLL);
        RETURN_STRING("memory allocation failed");
    }

    // 第二次调用:填充内容
    int32 rc = dem_get_map_img(imgdata, requiredSize);
    FreeLibrary(hGetProcIDDelphiDLL);

    // 构建返回数组
    array_init(return_value);
    add_assoc_long(return_value, "retflag", rc);
    add_assoc_string(return_value, "imgdata", imgdata, 1); // 最后一个参数1表示让PHP接管内存,不需要手动free

    // 注意:因为add_assoc_string已经接管内存,这里不需要手动efree(imgdata)
}

方案2:DLL分配内存,返回指针,PHP扩展使用后释放

如果不确定字符串长度,或者不想两次调用DLL,可以让Delphi分配内存,但必须提供对应的释放函数,避免内存泄漏(因为Delphi和PHP的内存管理器不同,不能直接用PHP的free释放Delphi分配的内存)。

步骤1:修改Delphi DLL代码

添加一个释放内存的函数,确保用同一个内存管理器分配和释放:

function dem_get_map_img(out imgdata: PAnsiChar): Int32; stdcall;
var
  base64str: AnsiString;
begin
  base64str := '这里替换为实际的大Base64字符串';
  // 分配内存并复制字符串(用StrNew,会在堆上分配,且包含结束符)
  imgdata := StrNew(PAnsiChar(base64str));
  Result := Length(base64str); // 返回字符串长度
end;

procedure dem_free_img_data(imgdata: PAnsiChar); stdcall;
begin
  if imgdata <> nil then
    StrDispose(imgdata); // 释放StrNew分配的内存
end;

步骤2:修改PHP扩展代码

调用DLL获取指针,使用后调用释放函数:

PHP_FUNCTION(dem_get_map_img) {
    HMODULE hGetProcIDDelphiDLL = LoadLibrary(_T("delphiDLL.dll"));
    if (!hGetProcIDDelphiDLL) {
        RETURN_STRING("dll not loaded");
    }

    if (ZEND_NUM_ARGS() != 0) {
        FreeLibrary(hGetProcIDDelphiDLL);
        RETURN_STRING("wrong param count");
    }

    // 获取两个函数的地址
    FARPROC lpfnGetImg = GetProcAddress(hGetProcIDDelphiDLL, "dem_get_map_img");
    FARPROC lpfnFreeImg = GetProcAddress(hGetProcIDDelphiDLL, "dem_free_img_data");
    if (!lpfnGetImg || !lpfnFreeImg) {
        FreeLibrary(hGetProcIDDelphiDLL);
        RETURN_STRING("required function not found");
    }

    // 定义函数指针类型
    typedef int32(__stdcall * pICFUNC_GET)(char **imgdata);
    typedef void(__stdcall * pICFUNC_FREE)(char *imgdata);
    pICFUNC_GET dem_get_map_img = (pICFUNC_GET)lpfnGetImg;
    pICFUNC_FREE dem_free_img_data = (pICFUNC_FREE)lpfnFreeImg;

    char *imgdata = NULL;
    int32 rc = dem_get_map_img(&imgdata);
    if (!imgdata || rc <= 0) {
        FreeLibrary(hGetProcIDDelphiDLL);
        RETURN_STRING("failed to get image data");
    }

    // 构建返回数组,先复制字符串到PHP管理的内存
    zval img_zval;
    ZVAL_STRINGL(&img_zval, imgdata, rc, 1); // 复制字符串,PHP接管内存

    // 调用DLL的释放函数释放原内存
    dem_free_img_data(imgdata);
    FreeLibrary(hGetProcIDDelphiDLL);

    array_init(return_value);
    add_assoc_long(return_value, "retflag", rc);
    add_assoc_zval(return_value, "imgdata", &img_zval);
}

关键注意事项

  1. 跨语言内存管理:绝对不要在Delphi里分配内存让PHP直接释放,反之亦然,必须用同一语言的内存管理器处理,或者提供对应的分配/释放函数对。
  2. 字符串结束符:C风格字符串必须以'\0'结尾,Delphi的StrNew会自动添加,方案1里的StrPCopy也会处理,确保PHP能正确识别为字符串。
  3. 大内存处理:避免在栈上分配固定大小的缓冲区(比如你的char imgdata[AS_MAXCH];),栈空间有限,大字符串必须用堆内存,PHP扩展里用emalloc/efree,这些函数会和PHP的内存池集成,避免泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:02:15