|
Memory
32-bit pointer
About objects and memory usage
TList allocating memory
Heaping objects
Finding total global variable size
DLL's and Memory Management
TotalSystemMemory
Freeing Pointers to Constants
Destroy dynamically-created components
Arrays bigger than 64k
32-bit pointer
Question
Does anyone know how to define a 32-bit pointer? If I want to use
LPBitMapInfo, defined in the WinApi as a 32bit pointer to the TBitMapInfo
class, but not reconized by delphi (why?), I will have to create my own
type:
My_LPBitMapInfo =3D ^TBitMapInfo
It should be a 16bit pointer, right?
Answer
A:
All pointers in Delphi are 32-bits. Here is a simple test.
Place the following code into a buttons onclick event handler and you
will get all the verification you need.
ShowMessage( Format('Pointers are %d bytes long or %d-bits', [sizeof(pointer),8*sizeof(pointer)]) );
A:
I think you're confusing the size of the data element with how it's interpreted into a physical address. "Pointers" even on the old 8086/8088 processors were 32 bits in size; including a 16-bit segment and a 16-bit offset, which were finally combined to create a 20-bit physical address; some different combinations of segment and offset could therefore resolve to the same physical address, which led to the need to "normalize" pointers for some purposes.
For 16-bit Enhanced mode Windows, pointers in all of Borland's Pascal products have been intra-segment, that is, the "segment" portion of a given pointer should always resolve to the same extended memory selector; then, given a 16-bit "offset", this seg/ofs model gives a maximum of 64kb for a data structure. Windows provides methods whereby
to overcome this limitation (using the _AHIncr procedure for instance), but it's not automatic like using C's "Huge" data directive. 16-bit Delphi doesn't have "Huge" pointers in that sense. 32-bit Delphi is supposed to fix this.
As far as I can tell, the VCL code that allows reading of greater than 64k bitmaps (for instance) uses (and well conceals) these Windows kludges; this would be a good resource for seeing how this can be done in Delphi.
A:
Yes, but they're 16:16 pointers, as opposed to the 16:32 pointers he probably
wanted.
A:
Gentlemen, we are talking apples and oranges here. If the C DLL is compiled by a 16 bit compiler, the pointers in Delphi are ABSOLUTELY compatible with the 16:16 bit far and huge pointers in C (or C++). There is not problem whatsoever. Just pass them.
On the other hand, if the C (or C++)application is a Win32 application, it uses 32 bit offsets only. The segments (more appropiately selectors) are invisible and pressumed of
zero linear base address.
Note that i said application. Ring 0 components also use 32 bit pointers but might rarely use 48 bit pointers. VXD's are the ring 0 components.
To call a 16 bit application you just do it. Nothing special. To call or be called by a Win32 application you need a thunk. Not just for the pointers but also to change from
a 16 bit stack to a 32 bit stack. And much more. If this is what you need to do, check the
file CALL32NT.ZIP, available in many places in the web.
About objects and memory usage
Question
It's well known that memory allocation under Windows shouldn't
be made at a very low granularity because of the limited system
handles available.
If I have to create several instances of objects at runtime
- Does Delphi allocate them in global memory?
- Am I restricted ... to the local heap size?
- Should I apply any special programming technique in order to
avoid problems with allocation, or just freeing the objects?
Answer
A:
1. Objects are definitely allocated on (from?) the global heap,
although I can't find a clear statement to this effect in
either manuals or on-line help. (I'm 99% sure it's there;
I just can't find it right now).
2. Delphi has a memory sub-allocator which deals with the
granularity issue. Do a search on "Memory" in the on-line
help, and select the topic "Heap Manager" for a description
of this. I have assumed that the description given there
applies to object instantiation as well as memory allocated
with New or GetMem. (I don't have the source for TObject's
Create method, and without it I can't be 100% sure).
3. My general philosophy for freeing in Delphi is to Free
objects which I Create, and leave Delphi components to Free
anything which they Create. Unusual situations may require
a different approach.
TList allocating memory
Question
My question is do I need to allocate a new record for each Add() to the TList
... and ... does deleting an item from the TList also free the copy of
the record?
Answer
A:
If you are going to create your own list class you must call NEW &
DISPOSE EVERY TIME or horrible things will happen on your machine.
The way I would handle that is in the add, delete and free methods.
A:
I don't get this. In this example he allocates spaces for a
record, then adds the pointer to that record to the list. How in the
world would TList i.e. Delphi then know how to deallocate the space
again? I still recommend using a TClass descendant instead of the
record. Then each element is responsible for deallocating its own
space. And TList knows how to call a TClass descendants Free method,
thus there is no need override TLists constructor/destructor.
Heaping objects
Question
I have allocated objects on the heap, created pointer to them, but I cannot
call any of their methods.
My Object is TAnimal, here is my syntax:
ptrAnimal^.Run;
This does not seem to work, although the same syntax works for setting
properties. What am I doing wrong?
Answer
A:
If you created this class your self and inherited it from someone else you
should try the following:
If you want to access the method, say Click, you'll have to assign the
procedure to yours object's event click, that is:
ptrAnimal^.OnClick:= OnMyClick;
This is the procedure wich gets the event, looking something like this:
procedure [FILE].OnMyClick(SENDER: TObject);
BEGIN
(SENDER as TAnimal).YourProperty:= Value;
END;
Finding total global variable size
Question
Is there a way to determine how much of the data segment all the global
variables/constants in my program are occupying?
Answer
You can create a map file in the linker options, recompile and then take a
look at it. Look for the globals and the data segment size. Select a detailed
map file.
DLL's and Memory Management
Question
Does anyone know how Delphi manages it's memory?
I have an app written in Delphi, which calls a DLL, also written in Delphi. I
have done a lot of work on both, and now whenever I try to run the two, I get
a GPF (I now suspect it is a memory problem).
First, how can I guestimate how much memory is used by my DLL and how much is
used by the EXE?
Where does the DLL get its memory from?
Answer
I doubt if the GPF is being caused by a shortage of memory. If it was stack
shortage you would get Error 202. In the case of heap memory, Windows supports
virtual global memory so the normal symptom of a shortage of real memory is a
drastic slow-down rather than a GPF.
From the "Writing DLLs" on-line help:
Global Variables: A DLL has its own data segment and any variables declared
in a DLL are private to that DLL. A DLL cannot access variables declared by
modules that call the DLL, and it is not possible for a DLL to export its
variables for use by other modules. Such access must take place through a
procedural interface.
DLLs and Stack Segments: A DLL does not have its own stack segment, so it uses
the stack segment of the application that called the DLL.
Also note the following advice from "Run-time errors in DLLs"
If a run-time error does occur in a DLL, the safest thing to do is to exit
Windows entirely. If you simply try to modify and rebuild the faulty DLL code,
when you run your program again, Windows will not load the new version of the
DLL if the faulty one is still in memory. Exiting Windows and then restarting
Windows and Delphi ensures that your corrected version of the DLL is loaded.
I would try and re-create the problem with the DLL code in a normal unit so
that the debugger can help you.
A:
If you've ever developed a serious DLL, you'd know that exiting Windows every
time you get a GPF is totally impractical. You can generally just kill it
from memory and recompile and everything is fine. I've done this many, many
times. Also, once you have your DLL working, it is often impractical to move
the offending code to a 'normal' unit within an EXE to debug it. So, the
answer to this problem is Turbo Debugger for Windows, which comes in the RAD
pack. To debug a DLL with it, you can simply include a 'hard' breakpoint by
adding 'inline($cc);' to your code near the offending area, or immediately
after the entry point to the DLL. This will wake up the debugger and you can
step through your code.
TotalSystemMemory
Question
Answer
Unit MemInfo;
Interface
Procedure FreeMemory(Var lTotalMemory: LongInt; Var lFreeMemory: LongInt);
Implementation
Uses WinTypes, WinProcs, ToolHelp;
Function Min(Number1, Number2 : LongInt) : LongInt;
{Returns the minimum of Number1 & Number2}
Begin
If (Number1 <= Number2) Then
Min := Number1
Else
Min := Number2;
End; {end Function, Min()}
Procedure FreeMemory(Var lTotalMemory: LongInt; Var lFreeMemory: LongInt);
{Calculates and returns the amount of Total & Free Memory in bytes
(ie. divide each by 1024 of Kilobytes)
NB: Total Memory will be 0 if windows is running in Standard Mode since
actual Total Memory is not able to be determined.}
Var
lWinFlags : LongInt;
mmiMemManInfo : TMemManInfo;
Begin
{Initialise Variables}
lTotalMemory := 0;
lFreeMemory := 0;
lWinFlags := GetWinFlags;
If (0 <> (lWinFlags And WF_ENHANCED)) Then
Begin
{Initialise MemManInfo structure}
mmiMemManInfo.dwSize := SizeOf(TMemManInfo);
mmiMemManInfo.dwLargestFreeBlock := 0;
mmiMemManInfo.dwMaxPagesAvailable := 0;
mmiMemManInfo.dwMaxPagesLockable := 0;
mmiMemManInfo.dwTotalLinearSpace := 0;
mmiMemManInfo.dwTotalUnlockedPages := 0;
mmiMemManInfo.dwFreePages := 0;
mmiMemManInfo.dwTotalPages := 0;
mmiMemManInfo.dwFreeLinearSpace := 0;
mmiMemManInfo.dwSwapFilePages := 0;
mmiMemManInfo.wPageSize := 0;
MemManInfo(@mmiMemManInfo); {Get Memory Manager Information}
{Calculate Total Memory}
lTotalMemory := (Min(mmiMemManInfo.dwTotalLinearSpace,
mmiMemManInfo.dwTotalPages +
mmiMemManInfo.dwSwapFilePages)
* mmiMemManInfo.wPageSize);
{Calculate Free Memory}
lFreeMemory := GetFreeSpace(0);
End
Else
Begin
{Total Memory = 0}
lTotalMemory := 0;
{Calculate Free Memory}
lFreeMemory := GetFreeSpace(0);
End; {end If-Then-Else}
End; {End Procedure, TotalMemory()}
End. {end of Unit, MemInfo}
Freeing Pointers to Constants
Question
I have been using using a listbox to store a database string and long
integer. The longint is store in the Object field:
MyList.AddObject(sDescription, POINTER(lKey) );
Now, how do I reclaim the memory used by the lKeys stored in the Listbox?
Answer
You don't have to. The Data property of TList is simply a space to put an
address. Since your just storing a number there and the number doesn't point to
any memory that you have allocated there is nothing to free/reclaim. When the
TList items are deleted the space allocated to hold your pointer will be freed.
Destroy dynamically-created components
Question
I use a few dynamically-created buttons:
I destroy them using this code since I don't have any names assigned
to them at runtime.
for i := 0 to ComponentCount-1 do
if (Components[i] is TButton) then TButton(Components[i]).Free;
However, it gives an "Index out of bounds" error. this is really
surprising since I thought by using "if (Components[i] is....",
I destroy the dynamic button ONLY when the specific component
is confirmed to be a "TButton" type.
Also, this error seems to appear only when I call the "Free" method
for this dynamic button.
If I use something such as showing its name:
for i := 0 to ComponentCount-1 do
if (Components[i] is TButton) then
ShowMessage( TButton(Components[i]).NAME );
The above will work fine! so I thought there may be some caveats
when it comes to freeing dynamically-created buttons.
Answer
A:
You have to think about the Components as a List, so when you cancel out
an item at a certain position, the position left vacant WON'T REMAIN EMPTY,
but it will be replace with the item immediately after and the ComponentCount
property, to which you index base the upper bound of your loop, should be
decreased as well!. A safer (and working way) is to walk thru lists beginning
from the bottom as in:
for i := Pred(ComponentCount) downto 0 do
if (Components[i] is TButton) then TButton(Components[i]).Free;
A:
I don't have an answer to the question "Why?" but I have had the same
problem when destroying all MDI child windows in a program I've been
working on. The solution that I came up with, which may also
work for you is:
{ Remove current MDI child windows }
; WHILE MDIChildCount > 0
DO BEGIN
; MDIChildren[0].Close
; Application.ProcessMessages
END
In the MDI child window OnClose event it sets the action to "caFree" which
will free the window.
The problem I had was that the windows weren't being freed.
*FLASH* I just thought of this: Is the freeing of components tied
to Application.ProcessMessages? I know that there are events triggered
when they get free'd, etc. My guess is that the messages need to be
processed before the free is completed. I think that this may be the
case because the following code produced an infinite loop:
{ Remove current MDI child windows }
; WHILE MDIChildCount > 0
DO MDIChildren[0].Close
When I added Application.ProcessMessages it worked and I moved on to
finish the section of code around it. I didn't question why it worked,
but was glad that it did.
A:
There's a very subtle mistake here...
for i := 0 to ComponentCount-1 do
if (Components[i] is TButton) then TButton(Components[i]).Free;
While you are freeing your components, the component list is updated, but the
upperbound of your for loop isn't. This means your freeing components that
don't exist any longer. Use:
for i := 0 to ComponentCount-1 do
if (Components[0] is TButton) then TButton(Components[0]).Free;
*** Index i changed to 0 ! ***
To delete them all.
Arrays bigger than 64k
Question
I can't manage arrays bigger than 64k.
There is a problem to cross this barrier I really cannot solve.
Look at the code that follows, I fill a big array of bytes with
random(100) and calculate the sum of the values added (ie Res1).
Then I read the array and recalculate the sum of the values it
contains (ie res2).
If the size of the array is <65535 then
it's ok,
else
it's not ok...
Answer
There is no way to directly access an array with over 65520 worth of data.
Either you use GlobalAlloc or a TMemoryStream to allocate the memory and
build a class to access it or you do it directly by hand.
You can get to subsequent segments on a GlobalAlloc'ed object by using
SelectorInc to build the pointer. The easiest way is to use TMemoryStream.
Type
Tmyarr = Class
buffer : TMemoryStream;
elsize : LongInt;
Constructor Create(esize, number : Word);
Destructor Free;
Procedure SetElement(index : Word; p : Pointer);
Procedure GetElement(index : Word; p : Pointer);
End;
Implementation
Constructor Tmyarr.Create(esize, number : Word);
Var
size : LongInt;
Begin
Inherited Create;
buffer := TMemoryStream.Create;
elsize := esize;
size := esize * number;
buffer.SetSize(size);
End;
Procedure Tmyarr.Free;
Begin
If Self <> Nil Then
Begin
buffer.Free;
Destroy;
End;
End;
Procedure GetElement(index : Word; p : Pointer);
Begin
buffer.Seek(elsize * index, 0);
buffer.Read(p^, elsize);
End;
Procedure SetElement(index : Word; ptr : Pointer);
Begin
buffer.Seek(elsize * index, 0);
buffer.Write(p^, elsize);
End;
|