如何从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); }
关键注意事项
- 跨语言内存管理:绝对不要在Delphi里分配内存让PHP直接释放,反之亦然,必须用同一语言的内存管理器处理,或者提供对应的分配/释放函数对。
- 字符串结束符:C风格字符串必须以
'\0'结尾,Delphi的StrNew会自动添加,方案1里的StrPCopy也会处理,确保PHP能正确识别为字符串。 - 大内存处理:避免在栈上分配固定大小的缓冲区(比如你的
char imgdata[AS_MAXCH];),栈空间有限,大字符串必须用堆内存,PHP扩展里用emalloc/efree,这些函数会和PHP的内存池集成,避免泄漏。
内容的提问来源于stack exchange,提问作者Graham
相关产品推荐
相关产品推荐

