Free memory and nil in Delphi using a single function - delphi

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.

Related

How can I modify and return a variable of type PChar in a function call

I need store a variant value (which always return a string) in a PChar variable now i'm using this code
procedure VariantToPChar(v:variant; p : PChar);
Var
s : String;
begin
s:=v;
GetMem(p,Length(s)*Sizeof(Char));
StrCopy(p, PChar(s));
end;
But i'm wondering if exist a better way
Do you really, really have to create a PChar? As long as possible i would use Strings, and only if an external library (like the Windows API) requires a PChar, i would cast it.
uses
Variants;
var
vText: Variant;
sText: String;
begin
vText := 'Hello world';
// VarToStr() can handle also null values
sText := VarToStr(vText);
// If absolutely necessary, cast it to PChar()
CallToExternalFunction(PChar(sText));
Doing it like this you can avoid problems with memory (de)allocation, null values, and Ansi/Unicode chars. If the external function wants to write into the string, you can use SetLength() before casting. Maybe the article Working with PChar could give you some ideas.
Update: You really shouldn't do this or use this code as you're likely to encourage people to write code that leaks. People will call this and fail to free the memory since they don't know that this function allocates memory.
If you want to store something in a PChar size buffer, and have that value still be associated with p (the pointer p is modified and is different when you return from the procedure), then you need to make the parameter a var (by-reference instead of by-value) parameter like this:
procedure AllocPCharBufFromVariant(v:variant; var p : PChar);
Var
s : String;
begin
try
s:=v;
GetMem(p,(Length(s)+1)*Sizeof(Char)); // fixed to add 1 for the nul
StrCopy(p, PChar(s));
except
on E:EVariantError do
begin
p := nil;
end;
end;
end;
I have also shown above handling EVariantError, which I have chosen to handle by returning nil in the p parameter, but you should think about how you want it to work, and then deal with it somehow.
The above code also leaks memory which is awful, so I renamed it AllocPChar. It seems like your original code has so many problems that I can't recommend a good way to do what looks like a giant pile of bad things and the name you chose is among the most awful choices.
At least the name Alloc gives me a hint so I'm thinking "I better free this when I'm done with it".
I suspect just a
PChar(string(v))
expression will do the trick.
And the memory used to store the converted string content will be available in the scope of this code (i.e. as long as the string(v) will be referenced - so you may want to use an explicit string variable to ensure that your PChar memory is still allocated).

Could omission of "^" when accessing a record pointer's members cause an access violation?

In VirtualTreeview, I am storing my data in the PVirtualNodes. I have experienced several Access Violations (typically with "Read of adress 00000000") in my App, and they mostly (I'd actually dare to say Always) occur when I am doing something with my Node Data.
However, the thing is, I declare my stuff & use it like this:
// DUMMY CODE - Not written or tested in IDE
var
MyNode : PVirtualNode;
MyData : PMyNodeData;
Begin
MyNode := VST.GetFirstSelected;
if Assigned(MyNode) then
Begin
MyData := VST.GetNodeData(MyNode);
if Assigned(MyData) then
Begin
MyData.DummyProperty := 'Test';
End;
End;
End;
As you probably noticed, I do not "dereference" (correct?) my "MyData" by doing MyData^! The reason I don't is that I have been told it was not necessary to add the caret to the pointer name, however I have a feeling that it has something to do with it. If I knew, I wouldn't be posting on here. ;)
So my question is: Is it in the end necessary to add the little ^ to MyData? And is it possible that by not doing that, I may provoke an Access Violation?
When you have a pointer to a record, then you can omit the ^. The following are equivalent:
MyData.DummyProperty
MyData^.DummyProperty
This is also the case for the deprecated Turbo Pascal object. I would expect it to be so for Delphi classes, although I have never tried with them since they are already reference types.
Sadly, this is not the explanation for your AV.
Using ^ to dereference records is optionnal as it is assumed implicitly by the compiler. When not using any hard typecast, any situation that would requires the "^" would not compile. But only 1 level of dereferencing is implicit.
type
TMyRecord = record
MyField : Integer;
end;
PMyRecord = ^TMyRecord;
PPMyRecord = ^PMyRecord;
procedure DoSomething;
var vMyField : PPMyRecord;
begin
vMyField.MyField; <---Won't compile
vMyField^.MyField; <---Will compile
end;
As for your access violation, here's my best guess based on what you wrote... Assuming your exemple is representative (i.e. that is, crash on assigning a string), and assuming PMyNodeData points to a record. I'd guess that PMyNodeData's memory was reserved with "GetMem" instead of "New", making the string field of the record uninitialized.
There is an exception where Data.xx and Data^.xx are not the same: when the field pointed at is of the same pointer type or the generic pointer type:
var
x: PPointer;
y: Pointer;
begin
x := GetPPointer();
y := x;
y := x^;
end;
I consider it best practice to always add the operator ^ when the pointed value is used to avoid ambiguous situations like above.
Given your example: The problem is possibly memory corruption. Did you set NodeDataSize correctly?

Is there a compile time flag to guard against accessing objects from StringList without using "objects"

I can't count how many times I may have erroneously done this:
for i := 0 to MyList.count-1 do begin
myobject := TMyObject(MyList[i])
.......
end;
when it should be this:
for i := 0 to MyList.count-1 do begin
myobject := TMyObject(MyList.objects[i])
.......
end;
Note the objects in the second snippet of code.
The erroneous snippet of code will run, and will obviously throw an access violation when I try to make use of myobject. But it isn't always apparent what it is I am doing wrong.
Is there a compiler option which will guard against this?
Delphi has 2 ways to typecast, hardcast using TObject(var) and a softcast using the As operator.
It is a good practice to always use the As operator unless you are 100% sure.
In your specific example
(MyList[i]) as TMyObject does not compile
where as
(MyList.objects[i]) as TMyObject does.
No. The compiler assumes when you type-cast that you know what you're doing. It will allow pretty much any type-cast where the two types are the same size, with the notable exception of casting between integral and floating-point types.
For this particular instance, try getting yourself into the habit of using the as operator instead. Then the compiler will catch you when you forget to use the Objects property:
myobject := MyList[i] as TMyObject; // compiler error
myobject := MyList.Objects[i] as TMyObject; // OK
It looks like you are using a TStringList to hold a string/object pair. If you are using Delphi 2009 or later an alternative suggestion is to replace your TStringList with a generic TDictionary like so:
uses
Generics.Collections;
...
MyDictionary: TDictionary<String, TMyObject>;
...
for MyObject in MyDictionary.Values do
begin
...
end;
Then the entire operation is type safe and you won't need to cast at all.
Note: You can't reassign the iteration variable inside a for..in loop (but you can call its methods and change the value of its properties.

How to leak a string in Delphi

I was talking to a co-worker the other day about how you can leak a string in Delphi if you really mess things up. By default strings are reference counted and automatically allocated, so they typically just work without any thought - no need for manual allocation, size calculations, or memory management.
But I remember reading once that there is a way to leak a string directly (without including it in an object that gets leaked). It seems like it had something to do with passing a string by reference and then accessing it from a larger scope from within the routine it was passed to. Yeah, I know that is vague, which is why I am asking the question here.
I don't know about the issue in your second paragraph, but I was bitten once by leaked strings in a record.
If you call FillChar() on a record that contains strings you overwrite the ref count and the address of the dynamically allocated memory with zeroes. Unless the string is empty this will leak the memory. The way around this is to call Finalize() on the record before clearing the memory it occupies.
Unfortunately calling Finalize() when there are no record members that need finalizing causes a compiler hint. It happened to me that I commented out the Finalize() call to silence the hint, but later when I added a string member to the record I missed uncommenting the call, so a leak was introduced. Luckily I'm generally using the FastMM memory manager in the most verbose and paranoid setting in debug mode, so the leak didn't go unnoticed.
The compiler hint is probably not such a good thing, silently omitting the Finalize() call if it's not needed would be much better IMHO.
No, I don't think such a thing can happen. It's possible for a string variable to obtain a value that you didn't expect, but it won't leak memory. Consider this:
var
Global: string;
procedure One(const Arg: string);
begin
Global := '';
// Oops. This is an invalid reference now. Arg points to
// what Global used to refer to, which isn't there anymore.
writeln(Arg);
end;
procedure Two;
begin
Global := 'foo';
UniqueString(Global);
One(Global);
Assert(Global = 'foo', 'Uh-oh. The argument isn''t really const?');
end;
Here One's argument is declared const, so supposedly, it won't change. But then One circumvents that by changing the actual parameter instead of the formal parameter. Procedure Two "knows" that One's argument is const, so it expects the actual parameter to retain its original value. The assertion fails.
The string hasn't leaked, but this code does demonstrate how you can get a dangling reference for a string. Arg is a local alias of Global. Although we've changed Global, Arg's value remains untouched, and because it was declared const, the string's reference count was not incremented upon entry to the function. Reassigning Global dropped the reference count to zero, and the string was destroyed. Declaring Arg as var would have the same problem; passing it by value would fix this problem. (The call to UniqueString is just to ensure the string is reference-counted. Otherwise, it may be a non-reference-counted string literal.) All compiler-managed types are susceptible to this problem; simple types are immune.
The only way to leak a string is to treat it as something other than a string, or to use non-type-aware memory-management functions. Mghie's answer describes how to treat a string as something other than a string by using FillChar to clobber a string variable. Non-type-aware memory functions include GetMem and FreeMem. For example:
type
PRec = ^TRec;
TRec = record
field: string;
end;
var
Rec: PRec;
begin
GetMem(Rec, SizeOf(Rec^));
// Oops. Rec^ is uninitialized. This assignment isn't safe.
Rec^.field := IntToStr(4);
// Even if the assignment were OK, FreeMem would leak the string.
FreeMem(Rec);
end;
There are two ways to fix it. One is to call Initialize and Finalize:
GetMem(Rec, SizeOf(Rec^));
Initialize(Rec^);
Rec^.field := IntToStr(4);
Finalize(Rec^);
FreeMem(Rec);
The other is to use type-aware functions:
New(Rec);
Rec^.field := IntToStr(4);
Dispose(Rec);
Actually, passing string as CONST or non const are the same in term of reference count in Delphi 2007 and 2009. There was a case that causing access violation when string is passed as CONST. Here is the problem one
type
TFoo = class
S: string;
procedure Foo(const S1: string);
end;
procedure TFoo.Foo(const S1: string);
begin
S:= S1; //access violation
end;
var
F: TFoo;
begin
F:= TFoo.create;
try
F.S := 'S';
F.Foo(F.S);
finally
F.Free;
end;
end.
Another way to leak a string is to declare it as a threadvar variable. See my question for details. And for the solution, see the solution on how to tidy it.
I think this might have been similar to what I was thinking of. It is the reverse of a string leak, a string that gets collected early:
var
p : ^String;
procedure InitString;
var
s, x : String;
begin
s := 'A cool string!';
x := s + '. Append something to make a copy in' +
'memory and generate a new string.';
p := #x;
end;
begin
{ Call a function that will generate a string }
InitString();
{ Write the value of the string (pointed to by p) }
WriteLn(p^); // Runtime error 105!
{ Wait for a key press }
ReadLn;
end.

How to know what type is a var?

TypeInfo(Type) returns the info about the specified type, is there any way to know the typeinfo of a var?
var
S: string;
Instance: IObjectType;
Obj: TDBGrid;
Info: PTypeInfo;
begin
Info:= TypeInfo(S);
Info:= TypeInfo(Instance);
Info:= TypeInfo(Obj);
end
This code returns:
[DCC Error] Unit1.pas(354): E2133 TYPEINFO standard function expects a type identifier
I know a non instantiated var is only a pointer address.
At compile time, the compiler parses and do the type safety check.
At run time, is there any way to know a little more about a var, only passing its address?
No.
First, there's no such thing as a "non-instantiated variable." You instantiate it by the mere act of typing its name and type into your source file.
Second, you already know all there is to know about a variable by looking at it in your source code. The variable ceases to exist once your program is compiled. After that, it's all just bits.
A pointer only has a type at compile time. At run time, everything that can be done to that address has already been determined. The compiler checks for that, as you already noted. Checking the type of a variable at run time is only useful in languages where a variable's type could change, as in dynamic languages. The closest Delphi comes to that is with its Variant type. The type of the variable is always Variant, but you can store many types of values in it. To find out what it holds, you can use the VarType function.
Any time you could want to use TypeInfo to get the type information of the type associated with a variable, you can also directly name the type you're interested in; if the variable is in scope, then you can go find its declaration and use the declared type in your call to TypeInfo.
If you want to pass an arbitrary address to a function and have that function discover the type information for itself, you're out of luck. You will instead need to pass the PTypeInfo value as an additional parameter. That's what all the built-in Delphi functions do. For example, when you call New on a pointer variable, the compiler inserts an additional parameter that holds the PTypeInfo value for the type you're allocating. When you call SetLength on a dynamic array, the compiler inserts a PTypeInfo value for the array type.
The answer that you gave suggests that you're looking for something other than what you asked for. Given your question, I thought you were looking for a hypothetical function that could satisfy this code:
var
S: string;
Instance: IObjectType;
Obj: TDBGrid;
Info: PTypeInfo;
begin
Info:= GetVariableTypeInfo(#S);
Assert(Info = TypeInfo(string));
Info:= GetVariableTypeInfo(#Instance);
Assert(Info = TypeInfo(IObjectType));
Info:= GetVariableTypeInfo(#Obj);
Assert(Info = TypeInfo(TDBGrid));
end;
Let's use the IsClass and IsObject functions from the JCL to build that function:
function GetVariableTypeInfo(pvar: Pointer): PTypeInfo;
begin
if not Assigned(pvar) then
Result := nil
else if IsClass(PPointer(pvar)^) then
Result := PClass(pvar).ClassInfo
else if IsObject(PPointer(pvar)^) then
Result := PObject(pvar).ClassInfo
else
raise EUnknownResult.Create;
end;
It obviously won't work for S or Instance above, but let's see what happens with Obj:
Info := GetVariableTypeInfo(#Obj);
That should give an access violation. Obj has no value, so IsClass and IsObject both will be reading an unspecified memory address, probably not one that belongs to your process. You asked for a routine that would use a variable's address as its input, but the mere address isn't enough.
Now let's take a closer look at how IsClass and IsObject really behave. Those functions take an arbitrary value and check whether the value looks like it might be a value of the given kind, either object (instance) or class. Use it like this:
// This code will yield no assertion failures.
var
p: Pointer;
o: TObject;
a: array of Integer;
begin
p := TDBGrid;
Assert(IsClass(p));
p := TForm.Create(nil);
Assert(IsObject(p));
// So far, so good. Works just as expected.
// Now things get interesting:
Pointer(a) := p;
Assert(IsObject(a));
Pointer(a) := nil;
// A dynamic array is an object? Hmm.
o := nil;
try
IsObject(o);
Assert(False);
except
on e: TObject do
Assert(e is EAccessViolation);
end;
// The variable is clearly a TObject, but since it
// doesn't hold a reference to an object, IsObject
// can't check whether its class field looks like
// a valid class reference.
end;
Notice that the functions tell you nothing about the variables, only about the values they hold. I wouldn't really consider those functions, then, to answer the question of how to get type information about a variable.
Furthermore, you said that all you know about the variable is its address. The functions you found do not take the address of a variable. They take the value of a variable. Here's a demonstration:
var
c: TClass;
begin
c := TDBGrid;
Assert(IsClass(c));
Assert(not IsClass(#c)); // Address of variable
Assert(IsObject(#c)); // Address of variable is an object?
end;
You might object to how I'm abusing these functions by passing what's obviously garbage into them. But I think that's the only way it makes sense to talk about this topic. If you know you'll never have garbage values, then you don't need the function you're asking for anyway because you already know enough about your program to use real types for your variables.
Overall, you're asking the wrong question. Instead of asking how you determine the type of a variable or the type of a value in memory, you should be asking how you got yourself into the position where you don't already know the types of your variables and your data.
With generics, it is now possible to get the type info without specifying it.
Certain users indicated the following code doesn't compile without errors.
As of Delphi 10 Seattle, version 23.0.20618.2753, it compiles without errors, as seen below in the screenshot.
program TypeInfos;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.TypInfo;
type
TTypeInfo = class
class procedure ShowTypeInfo<T>(const X: T);
end;
{ TTypeInfo }
class procedure TTypeInfo.ShowTypeInfo<T>(const X: T);
var
LTypeInfo: PTypeInfo;
begin
LTypeInfo := TypeInfo(T);
WriteLn(LTypeInfo.Name);
end;
var
L: Exception;
B: Boolean;
begin
// Console output
TTypeInfo.ShowTypeInfo(L); // Exception
TTypeInfo.ShowTypeInfo(B); // Boolean
end.
Not that I know of. You can get RTTI (Run Time Type Information) on published properties of a class, but not for "normal" variables like strings and integers and so forth. The information is simply not there.
Besides, the only way you could pass a var without passing a type is to use either a generic TObject parameter, a generic type (D2008, as in ), or as an untyped parameter. I can't think of another way of passing it that would even compile.

Resources