簡體   English   中英

存儲Delphi接口參考時的奇怪AV

[英]Strange AV when storing an Delphi interface reference

我在以下代碼中收到意外的訪問沖突錯誤:

program Project65;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  SysUtils;

type
  ITest = interface
  end;

  TTest = class(TInterfacedObject, ITest)
  end;

var
  p: ^ITest;

begin
  GetMem(p, SizeOf(ITest)); 
  p^ := TTest.Create; // AV here
  try
  finally
    p^ := nil;
    FreeMem(p);
  end;
end.

我知道接口應該以不同的方式使用。 但是我正在研究使用這種方法的遺留代碼庫。 而且我很驚訝地發現,保留SizeOf(ITest)內存並將ITest放在那里是不夠的。

現在有趣的是,如果我改變第一行

GetMem(p, 21);

比AV消失了。 (20個字節或更少的失敗)。 對此有何解釋?

(我使用的是Delphi XE2 Update 4 + HotFix)

請不要評論代碼是多么可怕或建議如何正確編碼。 請回答為什么有必要保留21個字節而不是SizeOf(ITest)= 4?

你有效寫的是在幕后做以下邏輯:

var
  p: ^ITest;
begin
  GetMem(p, SizeOf(ITest));
  if p^ <> nil then p^._Release; // <-- AV here
  PInteger(p)^ := ITest(TTest.Create);
  p^._AddRef;
  ...
  if p^ <> nil then p^._Release;
  PInteger(p)^ := 0;
  FreeMem(p);
end;

GetMem()不保證將其分配的內容清零。 當你將新對象實例分配給varaiable接口時,如果字節不是零,RTL會認為已經有一個現有的接口引用,並會嘗試調用它的_Release()方法,導致AV因為它沒有支持一個真實的對象實例。 您需要預先將分配的字節清零,然后RTL將看到一個nil接口引用而不再嘗試調用其_Release()方法:

program Project65;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  SysUtils;

type
  ITest = interface
  end;

  TTest = class(TInterfacedObject, ITest)
  end;

var              
  p: ^ITest;              

begin              
  GetMem(p, SizeOf(ITest));               
  try
    FillChar(p^, SizeOf(ITest), #0); // <-- add this!
    p^ := TTest.Create; // <-- no more AV
    try
      ...
    finally
      p^ := nil;
    end;
  finally
    FreeMem(p);
  end;
end.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM