【问题标题】:Delphi, edit Auto-Complete access violation in tenumstring at TPointerlist()Delphi,在 TPointerlist() 处编辑 Tenumstring 中的自动完成访问冲突
【发布时间】:2018-04-27 23:03:06
【问题描述】:

在@KenWhite 的this autocomplete example 中,当调用TPointerList()[] 时(通过Windows 自动完成界面),Next 函数存在访问冲突。

D10.1u2、Win10.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] := CoTaskMemAlloc(8);  //access violation
     TPointerList(elt)[I] := CoTaskMemAlloc(2 * (Length(wStr) + 1)); //access violation
     StringToWideChar(wStr, TPointerList(elt)[I], 2 * (Length(wStr) + 1));
     Inc(I);
     Inc(FCurrIndex);
  end;
 if pceltFetched <> nil then
  pceltFetched^ := I;
 if I = celt then
 Result := S_OK
else
  Result := S_FALSE;
end;

【问题讨论】:

  • 你的问题是什么。你的其余代码在哪里? minimal reproducible example
  • 那么,您的问题是什么,minimal reproducible example 在哪里?
  • 我在这里也没有看到问题。我只看到一个问题、一个功能和另一个问题的链接。我也看不出大卫的评论有什么敌意。
  • @Henry 我阅读了您链接的整个博客文章,但我仍然不知道如何处理网站上发布的答案的错误报告。

标签: delphi autocomplete delphi-10.1-berlin


【解决方案1】:

在较新的版本(IIRC XE2 及更高版本)中,您可以按照 Remy 所说的去做,但在 IMO 中您不应该这样做。

在 XE2 之前的版本(或任何版本)中,TPointerList 的定义是:

type
  ...
  TPointerList = array[0..MaxListSize] of Pointer;

在较新的版本中,它是:

type
  TPointerList = array of Pointer;

也就是说,从static数组类型(值类型),现在变成了动态数组类型(引用类型)。将无类型输出参数的地址转换为这样的数组可能会变得很棘手。

定义上的差异解释了为什么在较新的版本中,代码不能正常工作:有一个额外的间接级别。

现在,如果您将以下声明添加到 uAutoComplete.pas 文件:

type
  TPointerList = array[0..65535] of Pointer; // assuming 65536 (2^16) entries are enough

然后文件的其余部分可以保持原来的状态。那么:

TPointerList(elt)[I] := ...

有效,并且不需要您在实际上不是的东西上使用稍微棘手的间接强制转换为 Delphi 动态数组。请注意,这也适用于旧版本。

【讨论】:

  • 不错的收获。确实,这回答了问题“链接是一个完整的工作吗?示例。”这是唯一类似于 OP 发布的问题的问题,尽管它在 cmets 中 询问虽然不完全确定这意味着什么。
【解决方案2】:

(elt) 需要是(@elt)[1] 需要是[I]

TPointerList(@elt)[I]

那么代码就不再是AV了。

此外,必须使用SysAllocString...()CoTaskMemAlloc() 分配输出字符串,因为调用者将使用COM 内存管理器来释放它们。您可以使用 RTL 的 ComObj.StringToLPOLESTR() 函数为您处理该问题,这会生成 Delphi String 的 COM 分配的宽字符串副本:

TPointerList(@elt)[I] := StringToLPOLESTR(FStrings[FCurrIndex]);

或者,您可以简单地获得WideString 的数据指针的所有权,而不是在WideString 已经创建一个副本之后在内存中创建另一个副本:

wStr := FStrings[FCurrIndex];
TPointerList(@elt)[I] := Pointer(wStr);
Pointer(wStr) := nil;

【讨论】:

  • 感谢 Remy,TPointerList(@elt)[I] := StringToLPOLESTR(FStrings[FCurrIndex]); 已修复。奇怪的是原始代码似乎已被多人成功使用,包括我们尊敬的 DH
  • 请注意 TPointerList 的定义已更改,Delphi XE2 中的 IIRC。在 XE2 之前它是 TPointerList = array[0..MaxListSize-1] of Pointer;。在 XE2 中,它变成了TPointerList = array of Pointer;。这可以解释为什么原来的示例代码在新版本中不再工作:有一个额外的间接级别。在这里,IMO 将其转换为动态数组也有点问题。
  • @RudyVelthuis 是真的,这就是为什么我根本不喜欢使用TPointerList。我可能只是将elt 转换为PPointer 并在每次循环迭代时递增+取消引用它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多