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

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...的内容

问题根源分析

  1. 硬编码索引导致越界:你用I做循环计数,但直接访问TPointerList(elt)[1]——如果调用方请求的celt是1,索引1就超出了缓冲区的有效范围,直接触发访问违例。必须用循环变量I对应每个要填充的元素位置。
  2. 字符串内存管理不符合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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:16:21