Delphi, отредактируйте нарушение автозаполнения доступа в tenumstring в TPointerlist () - PullRequest
0 голосов
/ 28 апреля 2018

В этом примере автозаполнения от @KenWhite, функция Next имеет нарушение прав доступа при вызове TPointerList()[] (через интерфейс автозаполнения Windows.)

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;

Ответы [ 2 ]

0 голосов
/ 29 апреля 2018

В более новых версиях (IIRC XE2 и выше) вы можете делать то, что говорит Реми, но IMO не следует.

В версиях до XE2 (или любой другой версии) определение TPointerList было:

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

В более новых версиях это:

type
  TPointerList = array of Pointer;

Другими словами, вместо статического типа массива (тип значения) теперь он стал динамическим типом массива (ссылочным типом). Приведение адреса нетипизированного параметра к такому массиву может оказаться сложным.

Разница в определении объясняет, почему в более новых версиях код не работает должным образом: существует один дополнительный уровень косвенности.

Теперь, если вы добавите следующее объявление в файл uAutoComplete.pas :

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

тогда остальная часть файла может остаться такой, какой она была раньше. Тогда:

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

работает и не требует от вас использования слегка хитрого, косвенного приведения к динамическому массиву Delphi для чего-то, чего на самом деле нет. Обратите внимание, что это также будет работать в более старых версиях.

0 голосов
/ 28 апреля 2018

(elt) должно быть (@elt), а [1] должно быть [I]:

TPointerList(@elt)[I]

Тогда код больше не будет AV.

Кроме того, выходные строки должны быть выделены либо SysAllocString...(), либо CoTaskMemAlloc(), так как вызывающая сторона собирается использовать диспетчер памяти COM для их освобождения. Вы можете использовать функцию RTL ComObj.StringToLPOLESTR(), чтобы сделать это для вас, что делает выделенную COM широкоформатную копию Delphi String:

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

В качестве альтернативы, вы можете просто стать владельцем указателя данных WideString вместо создания еще одной копии в памяти после того, как WideString уже сделал ее:

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