When is it safe to Dispose a nil pointer? - delphi

In the following example:
program DisposeProblem;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
Person = record
name: string;
age: Integer;
end;
var
p: ^Person;
begin
p := nil;
Dispose(nil); // OK
Dispose(p); // AV
end.
Why is the first Dispose() call OK, while the second causes an access violation error? When I step through the code, the first Dispose() calls System._FreeMem(), while the second calls System._Dispose(), but I don't understand why this is the case. I would like to understand this behavior so I can know when it is safe to call Dispose() on a nil pointer.

I would like to understand this behavior so I can know when it is safe to call Dispose() on a nil pointer.
It is never OK to call Dispose() on a nil pointer variable. The RTL expects the variable to point at valid memory allocated with New(), and so will unconditionally try to finalize whatever data/object is being pointed at. Passing in a pointer variable that is nil leads to undefined behavior and will likely crash.
Dispose(nil) is effectively a no-op. The compiler knows the nil literal can't possibly point at a data type that needs to be finalized, so it doesn't need to call System._Dispose(), hence it calls System._FreeMem() instead (why it calls anything at all, I don't know).
System._FreeMem() allows nil as input, it will simply exit without doing anything. However, System._Dispose() on a pointer variable does not allow nil as input (and never has).

Related

When is correct to use the is operator in delphi?

How can I test that a not nil TObject does not hold an object of a specific class in the case below?
procedure TForm7.testme;
var
mystring: string;
obj: TObject;
begin
mystring := 'asd';
obj := TObject(mystring);
if assigned(obj) then
if obj is TestClass then // --> this is failing
// ...
end;
obj := TObject(mystring);
This is the mistake. A TObject can hold a valid object, or nil. Anything else leads to undefined behaviour. Which means that is, or indeed anything else, cannot be expected to behave in any meaningful way.
The implementation of any method operating on an object reference relies implicitly on the reference actually being an object. For something like is the implementation looks up the class of the instance, which depends on the memory being laid out as an object. A string does not fit the bill.
The is operator can tell you information about the type of an object. It is illegal to pass something that is not an object to it, other than the special value nil.
The solution is to stop putting invalid data into your object reference.

Why does freeing a bad object pointer throw EPrivilege instead of EAccessViolation?

Why do i get an "EPrivilege - Privileged instruction" when I execute this procedure instead of Access Violation?
{$Warnings OFF}
procedure TFrmMyTest.mnuCrashMeClick(Sender: TObject);
var t: TStringList;
begin
FreeAndNil(t);
end;
{$Warnings ON}
I know that I try to free an object that points randomly in memory. But I expect to get an access violation and not "Privileged instruction".
(Don't worry I don't intend to use the code above in a real program.)
FreeAndNil calls the non virtual method Free. Which first checks for nil(the variable likely isn't nil) and then calls the virtual destructor Destroy.
Calling a virtual method means looking at the beginning of an object to get the virtual-method-table(VMT). This can throw an access violation. But if the object is in allocated memory, it will instead return an undefined pointer as VMT.
Next a pointer sized value is read at a certain offset from the VMT. This once again can throw an access violation or return an undefined pointer.
Finally the memory where this pointer points is executed. And if it happen to contain invalid code, you get some variant of an invalid instruction exception.
Sometimes you will get an access violation and sometimes you will get EPrivilege and no doubt there are other modes of failure. And even sometimes the code will appear to work and the crash will happen later. It all depends on what value happens to be in t when you call Free on it.

Why do I have to cast to a specific pointer type before calling Dispose?

Let's suppose I have an instance of the TList class (BDS 2006, so this is a list of pointer types). Each pointer I put into the list references memory allocated by the New() function. So when I want to clear the list, I have to iterate through it and dispose each item, right? But how to do that properly? Do I have to cast each disposed item to the actual type it is?
type
TMyRec = record
Field1: string;
Field2: integer;
end;
PMyRec = ^TMyRec;
...
var
MyList: TList;
MyRecPointer: PMyRec;
begin
...
New(MyRecPointer);
...
MyList.Add(MyRecPointer);
...
for i := 0 to MyList.Count - 1 do
Dispose(PMyRec(MyList[x]));
MyList.Clear();
end;
Please look at the for loop at the end. I cast each item to PMyRec to dispose memory. Is that necessary? I know that the Dispose() function has a Pointer argument so casting seems stupid in this case, but still I'm not sure about it. Because how does the Dispose() function know how much memory to dispose when it gets a general Pointer type??
This is a second approach (without type casting):
for i := 0 to MyList.Count - 1 do
Dispose(MyList[x]);
I will be grateful if somebody explains me how it shoud be done and why. Thanks a lot.
Yes, it's necessary to cast to the proper pointer type. Without that, the RTL doesn't know that the record has a string member, so it won't dispose of the string. It will skip directly to freeing the record's own memory, and the string's contents will leak. It knows how much memory to free for the record the same way FreeMem knows how much to free from a GetMem call. The memory manager knows how much memory went to each of its allocations. (There are various ways of keeping track of that.)
Dispose is a "compiler magic" function. When the compiler sees you call it, it adds a hidden second parameter for the TTypeInfo record corresponding to the pointer type. That way, the RTL's Dispose function knows how to process the pointer it receives.

Free memory and nil in Delphi using a single function

I have a lot of memory allocations and the same number of FreeMem calls. What I didn't have though is a check before calling freemem to see if the pointer was nil, and a line after freeing to set the pointer to nil.
I tried to create a function to do this
procedure FreeMemAndNil(p: Pointer; size: Integer = -1);
begin
if p <> nil then
begin
if size > -1 then
FreeMem(p, size)
else
FreeMem(p);
p := nil;
end;
end;
But there's a problem. It can't set the origional pointer to nil because the parameter isn't variable (var p: Pointer). I can't use var though because if I do the compiler complains the type has to be the exact same type (Pointer). The pointers I'm passing could be pointers to any type (PChar, regular pointer, etc.).
What can I do to fix this? Is there a better solution?
To be able to pass arbitrary pointer values to that function, you need to follow the same model as FreeAndNil and pass in an untyped parameter. Otherwise, the compiler correctly complains about actual and formal parameter types not being identical. Type-cast the untyped parameter to Pointer when you call FreeMem on it.
You're doing a couple of pointless things in that function.
First of all is that freeing a nil pointer is always safe, so there's no reason to check for that before calling FreeMem. It's freeing a non-nil pointer that you need to worry about, but no function can protect you from that.
Next, the size parameter to FreeMem has been ignored for many years. It used to be that if you provided that parameter, it needed to match the size passed to GetMem, but nowadays, FreeMem completely ignores that parameter — the compiler doesn't even pass that parameter to the function.
With all of the above in mind, your function boils down to this:
procedure FreeMemAndNil(var P);
var
Tmp: Pointer;
begin
Tmp := Pointer(P);
Pointer(P) := nil;
FreeMem(Tmp);
end;
Be careful not to accidentally call that function on anything that isn't a pointer allocated with GetMem. The compiler won't catch it for you like it could if you were using typed parameters. If you attempt to free something that wasn't allocated with GetMem, you'll probably get an EInvalidPointer exception, but the variable you passed in will still be nil afterward. That's the same way FreeAndNil works.
There's a procedure in SysUtils called FreeAndNil that does this for objects. It does it by using an untyped var parameter which it casts to TObject, and it's up to you to ensure you don't pass it something that's not a TObject. You could do something similar here if you needed to. Just be careful; there's no type safety if you do that.
Like Mason Wheeler said you should use the same trick as FreeAndNil in the SysUtils unit does on object references.
So I modified your code, unit tested it, and this works fine:
procedure FreeMemAndNil(var ptr; size: Integer = -1);
var
p: Pointer;
begin
p := Pointer(ptr);
if p <> nil then
begin
if size > -1 then
FreeMem(p, size)
else
FreeMem(p);
Pointer(ptr) := nil;
end;
end;
--jeroen
PS: Rob Kennedy wrote a nice answer on untyped var parameters that has a link to his untyped parameter page on the internet.
PS2: For reference: The Kylix version of SysUtils.pas is on-line, and the FreeAndNil there is identical to how it is in Delphi.
I tend to work with ReallocMem a lot for pointer/memory operation.
Calling
ReallocMem(P,0)
will set the pointer to Nil.
1 thing you need to know about using it, P needs to be initialized before being passed to ReallocMem.

Comparing a pointer to function's value in Delphi

How can I compare the value of a variable that contains a pointer to a function with a function address?
I'm maintaining some code, and it is failing in Delphi 2007. The declaration is:
var
EditorFrameWindow: Function: HWnd Of Object = Nil;
In a form activation, I've got:
procedure TEditForm.FormActivate(Sender: TObject);
begin
EditorFrameWindow := GetFrameWindow;
end;
And in the form deactivation I've got:
procedure TEditForm.FormDeactivate(Sender: TObject);
begin
if EditorFrameWindow = GetFrameWindow then
EditorFrameWindow := nil;
end;
So what is happening is that the form is being deactivated twice, and it is failing as nothing else got activated. The FormDeactivate is called, it matches, and the EditorFrameWindow global is set to (nil,nil) (according to the debugger). Then it is being called again, and the function stored in the variable is called, but of course there isn't one stored so it jumps through nil and creates an exception.
What should I do to stop this happening? (The framework has been changed to a tabbed system, so the operation probably changed.)
Would
procedure TEditForm.FormDeactivate(Sender: TObject);
begin
if Assigned(EditorFrameWindow) and (EditorFrameWindow = GetFrameWindow) then
EditorFrameWindow := nil;
end;
work per chance?
Edit:
You don't compare function addresses, you compare the results of those functions. So even though the fixed code above can no longer cause an exception it may still not do what you want it to. Another function that returns the same result would also reset the event handler.
To really check whether the variable is set to a specific event handler you will need to compare both elements in the TMethod record. Something like:
procedure TEditForm.FormDeactivate(Sender: TObject);
begin
if (TMethod(EditorFrameWindow).Code = #TForm1.GetFrameWindow)
and (TMethod(EditorFrameWindow).Data = Self)
then
EditorFrameWindow := nil;
end;
There are two ways you might want to compare method pointers. Method pointers consist of two pointers, a code pointer and an object pointer. Delphi's native way of comparing method pointers compares only the code pointers, and it looks like this:
if #EditorWindowMethod = #TEditForm.GetFrameWindow then
EditorWindowMethod := nil;
It checks whether the code pointer in the EditorWindowMethod variable matches the starting address of the GetFrameWindow method in TEditForm. It does not check whether the object reference in EditorWindowMethod is the same as Self. If you want to make the the object references are the same, too, then you need to break apart the method pointer into its constituent parts with the TMethod record, which Mghie's answer demonstrates. (And you probably do want to compare the object references since it sounds like you have multiple edit forms. They all have the same GetFrameWindow code pointer, but they have different object references.)
The reason for the # in the code is to tell the compiler that you want to refer to the method pointers. Without it, the compiler will try to call the method pointers, and that's what was getting you into trouble. The first time the window was deactivated, you called EditorWindowMethod and compared the resulting window handle with the return value from calling GetFrameWindow. They matched, of course, so you unassigned EditorWindowMethod. The next time the form was deactivated, you tried to call EditorWindowMethod again, but it was a null pointer.
You should consider getting rid of your dependence on activation and deactivation notification. Instead, simply check whether the form is active inside GetFrameWindow.

Resources