Delphi D10.1u2中TEnumString.Next函数TPointerList访问违例问题
解决TEnumString.Next中访问TPointerList(elt)引发的访问违例问题
我之前在Delphi 10.1u2的64位环境做自动补全时,也踩过一模一样的坑。先看你贴的代码片段,问题其实出在两个核心细节上:
原代码片段:
function TEnumString.Next(celt: Integer; out elt; pceltFetched: PLongint): HResult; var I: Integer; wStr: WideString; begin I := 0; while (I < celt) and (FCurrIndex < FStrings.Count) do begin wStr := FStrings[FCurrIndex]; TPointerList(elt)[1] := PWideChar('abcd'); //access violation TPointerList(elt)[1] := CoTaskMem...的内容
问题根源分析
- 硬编码索引导致越界:你用
I做循环计数,但直接访问TPointerList(elt)[1]——如果调用方请求的celt是1,索引1就超出了缓冲区的有效范围,直接触发访问违例。必须用循环变量I对应每个要填充的元素位置。 - 字符串内存管理不符合COM规范:直接赋值
PWideChar('abcd')是指向栈上的常量字符串,而IEnumString接口要求返回的字符串必须用COM的CoTaskMemAlloc分配内存,这样后续调用方才能用CoTaskMemFree正确释放。另外,Delphi的WideString内存由RTL管理,不能直接把它的指针传递出去,必须复制到COM分配的内存中。
修正后的完整代码
function TEnumString.Next(celt: Integer; out elt; pceltFetched: PLongint): HResult; var I: Integer; wStr: WideString; pWideStr: PWideChar; strLength: Integer; begin Result := S_FALSE; // 初始化实际获取的元素数量 if Assigned(pceltFetched) then pceltFetched^ := 0; I := 0; while (I < celt) and (FCurrIndex < FStrings.Count) do begin wStr := FStrings[FCurrIndex]; strLength := Length(wStr); // 分配包含终止符的足够内存 pWideStr := CoTaskMemAlloc((strLength + 1) * SizeOf(WideChar)); if Assigned(pWideStr) then begin // 将WideString内容复制到COM分配的内存 StringToWideChar(wStr, pWideStr, strLength + 1); // 用循环变量I作为索引,确保元素位置正确 TPointerList(elt)[I] := pWideStr; Inc(I); Inc(FCurrIndex); end else begin // 内存分配失败返回标准COM错误码 Result := E_OUTOFMEMORY; Break; end; end; // 成功获取元素则返回S_OK if I > 0 then Result := S_OK; // 更新实际获取的元素数量 if Assigned(pceltFetched) then pceltFetched^ := I; end;
关键修正点说明
- 用循环变量
I替换硬编码的1,确保每个请求的元素都被正确填充,避免缓冲区越界。 - 使用
CoTaskMemAlloc分配字符串内存,严格遵循COM接口的内存管理规则,杜绝内存泄漏和访问违例。 - 添加内存分配失败的错误处理,返回标准的
E_OUTOFMEMORY错误码,让调用方能正确处理异常。 - 严格按照
IEnumString.Next的规范设置Result和pceltFetched的值,保证接口行为符合预期。
另外要注意,64位环境下指针是8字节,TPointerList(elt)的强制转换是可行的,但需确保调用方已经分配了足够大的缓冲区(至少celt * SizeOf(Pointer)字节),你的代码只需要负责填充内容即可。
内容的提问来源于stack exchange,提问作者Henry Crun
相关产品推荐
相关产品推荐

