Strange AV while saving Delphi interface link

I get an unexpected access violation error in the following code:

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. 

I know that interfaces should be used in different ways. However, I am working on an outdated codebase that uses this approach. And I was very surprised to see that it is not enough to reserve SizeOf (ITest) memory to place ITest there.

Now I wonder if I changed the first line to

 GetMem(p, 21); 

than AV disappeared. (20 bytes or less). What is the explanation for this?

(I am using Delphi XE2 Update 4 + HotFix)

Please do not comment on how awful the code is or tell me how it can be encoded correctly. Instead, answer why it is necessary to reserve 21 bytes instead of SizeOf (ITest) = 4?

+6
source share
1 answer

What you effectively wrote does the following logic behind the scenes:

 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() not guaranteed to have zero allocation of what it allocates. When you assign an instance of a new object to the varaiable interface, if the bytes are not zeros, RTL will think that an existing interface reference already exists and will try to call its _Release() method, calling AV, because it is not backed up by an instance of the real object. You need to reset the allocated bytes in advance, then RTL will see a link to the nil interface and will not try to call its _Release() method anymore:

 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. 
+26
source

Source: https://habr.com/ru/post/917834/


All Articles