Reputation: 3440
I would like to program something that helps me to free memory automatically as soon as a pointer to an dynamically allocated address space leaves the stack. An example would be:
procedure FillMemory (var mypointer);
begin
// CopyMemory, Move, etc... with data
end;
procedure MyProcedure;
var
MyPointer : Pointer;
begin
MyPointer := VirtualAlloc (NIL, 1024, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
FillMemory (MyPointer);
VirtualFree(MyPointer, 0, MEM_RELEASE); // I would like to avoid this...
end;
I could use strings but I would also like to avoid them (which I'm not so sure if Strings in the stack get freed anyway...) Any ideas?
Upvotes: 0
Views: 687
Reputation: 613491
Managed types have their references counted and when the count drops to zero, they are finalized. If you have a local variable, then when it goes out of scope, its reference count will drop to zero.
So, you can create a descendent of TInterfacedObject
which you refer to using an interface. Something like this:
type
TLifetimeWatcher = class(TInterfacedObject)
private
FDestroyProc: TProc;
public
constructor Create(const DestroyProc: TProc);
destructor Destroy; override;
end;
constructor TLifetimeWatcher.Create(const DestroyProc: TProc);
begin
inherited Create;
FDestroyProc := DestroyProc;
end;
destructor TLifetimeWatcher.Destroy;
begin
if Assigned(FDestroyProc) then
FDestroyProc();
inherited;
end;
You can then use it like this:
procedure MyProcedure;
var
MyPointer: Pointer;
LifetimeWatcher: IInterface;
begin
MyPointer := VirtualAlloc (NIL, 1024, MEM_COMMIT or MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
LifetimeWatcher := TLifetimeWatcher.Create(
procedure
begin
VirtualFree(MyPointer, 0, MEM_RELEASE);
end;
)
FillMemory(MyPointer);
end;
When LifetimeWatcher
leaves scope, the implementing object is destroyed and the procedure that you passed to TLifetimeWatcher.Create
is executed.
It would be easy enough to specialise this idea to be dedicated to your use case. And that would make the code at the call site more concise.
That would look like this:
function VirtualAllocAutoRelease(Size: SIZE_T; Protect: DWORD;
out LifetimeCookie: IInterface): Pointer;
var
Ptr: Pointer;
begin
Ptr := VirtualAlloc(nil, Size, MEM_COMMIT or MEM_RESERVE, Protect);
Win32Check(Ptr<>nil);
LifetimeCookie := TLifetimeWatcher.Create(
procedure
begin
VirtualFree(Ptr, 0, MEM_RELEASE);
end
);
Result := Ptr;
end;
And you'd use it like this:
procedure MyProcedure;
var
MyPointer: Pointer;
LifetimeWatcher: IInterface;
begin
MyPointer := VirtualAllocAutoRelease(1024, PAGE_EXECUTE_READWRITE,
LifetimeWatcher);
FillMemory(MyPointer);
end;
Upvotes: 3
Reputation: 5975
To expand on my comments and discussion with Arioch,
If you just want a raw memory block, use a dynamic array of byte
. The compiler will generate code to release this memory at the end of the method:
type
TBytes = array of Byte; // omit for newer Delphi versions
procedure FillMemory(var Bytes: TBytes);
begin
{ passing in here will increase the reference count to 2 }
// CopyMemory, Move, etc... with data
end; // then drop back to 1
procedure MyProcedure;
var
Buffer : TBytes;
begin
SetLength(Buffer, 1024); // buffer has reference count of 1
FillMemory (Buffer);
end; // reference count will drop to 0, and Delphi will free memory here
Hope this all makes sense. It is midnight here, so I'm not feeling the most awake...
Upvotes: 4