据我所知,这是Delphi的BUG,解决方法就是更正我所说的
MakeObjectInstance 跟 FreeObjectInstance方法,虽然你没有调用这两个方法:)
你试试看用下面的代码替换Forms.pas单元中的相应方法。
// Replacement for MakeObjectInstance and FreeObjectInstance
// This is the maximum number of object instances that can be
// put in one memory page, with other reference data.
Const InstanceCount = 312;
// The following 4 types are used for double linked lists of instance
// objects and blocks.
Type PObjectInstance = ^TObjectInstance;
TObjectInstance = Packed Record
Code :Byte;
Offset :Integer;
Case Integer Of
0
![Frown :( :(](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f641.png)
Next, Prev
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
ObjectInstance);
1
![Frown :( :(](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f641.png)
Method :TWndMethod);
End; // 13 bytes in size. Note that it's packed!
// The size matters becuase we have to know how many of these records
// can be put inside one Instance Block (see below), for the block to
// be as close as possible to one memory page size (but less than it!).
Type PInstanceBlock = ^TInstanceBlock;
TInstanceBlock = Packed Record
Next
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
InstanceBlock; // Points to the previous Instance block
Code :Array[1..2] Of Byte; // Instruction bytes.
WndProcPtr
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
ointer; // Window Procedure Pointer
InstCount :Integer; // How many object instances are used in the block
Prev
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
InstanceBlock; // Points to the previous Instance block
// Used for faster list management.
Instances :Array[0..InstanceCount] Of TObjectInstance;
// ^ Instance objects.
End; // Size of the block is:
// 4 bytes for Next
// 2 bytes for Code
// 4 bytes for WndProcPtr
// 4 bytes for InstCount
// 4 bytes for Prev
// 312 * 13 bytes for Instances
// -----------------------------
// 18 + 313 * 13 = 18 + 4069 = 4807 bytes. Just 9 bytes less than
// a memory page
Var InstBlockList
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
InstanceBlock = Nil; // The list of blocks currently allocated
InstFreeList
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
ObjectInstance = Nil; // The list of free Object instances
// The below variables are only used if you need synchronization of the objects
// I suggest that you do not use these! It will only make a small
// overhead in the code, and due to the way VCL is made, it is not needed
{$IFDEF VCL_Synchronize}
ObjInstLock :TRTLCriticalSection; // Critical section used to guard the memory
ObjInstInit :Boolean = False; // Has the critical section been initialized?
{$ENDIF}
// CAUTION: There are three possible problems in the below proceudre:
// 1
![Smile :) :)](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f642.png)
Synchronization is not enabled by default (according to Borland
// the data is not intended to be thread safe, and I think that's OK, more
// or less, if you are using VCL thread objects and Synchronize method,
// when using VCL objects from the threads).
// 2
![Smile :) :)](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f642.png)
VirtualAlloc may fail! If this happens, the return value is undefined
// Regardless of how unlikely this is to happen, it can happen. The thing
// is that, if it does happen, the procedure that called MakeObjectInstance
// will raise an exception, when it wants to access the block, anyway.
// 3
![Smile :) :)](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f642.png)
I am not sure how portable is the way to get the block, based on the
// instance address (Address And Not 4095, which will essentially
// align the Address to a 4 kb value). This works under Win9x/Me,
// NT/2000 and XP. That's enough for now
Function MakeObjectInstance(Method :TWndMethod)
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
ointer;
Const
BlockCode :Array[1..2] Of Byte = (
$59, { POP ECX }
$E9); { JMP StdWndProc }
PageSize = 4096;
Var
Block
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
InstanceBlock;
Instance
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
ObjectInstance;
Begin
{$IFDEF VCL_Synchronize}
// Check if the synchronization object has been initialized
If Not ObjInstInit Then
Begin
// If not, initialize it, and tell that it's initialized
InitializeCriticalSection(ObjInstLock);
ObjInstInit := True;
End;
// The code after this line is synchronized between threads.
EnterCriticalSection(ObjInstLock);
// Since there might be a memory allocation failure, the try block
// makes sure that the critical section is released.
Try
{$ENDIF}
// Is there some object instance we can already use?
If InstFreeList = Nil Then
Begin
// No, so allocate a new block of object instances
Block := VirtualAlloc(Nil,
PageSize,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
// No instances from the block are used at the moment
Block^.InstCount := 0;
// Fill in the instruction bytes
Move(BlockCode,
Block^.Code,
SizeOf(BlockCode));
// Tell it where the procedure is
Block^.WndProcPtr := Pointer(CalcJmpOffset(@Block^.Code[2],
@StdWndProc));
// Now, loop, and initialize all object instances
Instance := @Block^.Instances;
Repeat
// The below 3 lines are initializing the instructions
Instance^.Code := $E8; { CALL NEAR PTR Offset }
Instance^.Offset := CalcJmpOffset(Instance,
@Block^.Code);
// This is linked list manipulation
Instance^.Prev := Nil;
Instance^.Next := InstFreeList;
If InstFreeList <> Nil Then
InstFreeList^.Prev := Instance;
InstFreeList := Instance;
Inc(Longint(Instance),
SizeOf(TObjectInstance));
Until Longint(Instance) - Longint(Block) >= SizeOf
(TInstanceBlock);
// Linked list of Instance blocks
Block^.Prev := Nil;
Block^.Next := InstBlockList;
If InstBlockList <> Nil Then
InstBlockList.Prev := Block;
InstBlockList := Block;
End;
// There's certainly some free block - let's get it, and use it!
Instance := InstFreeList;
InstFreeList := Instance^.Next;
Result := Instance;
Instance^.Method := Method;
// VirtualAlloc allocates memory on 4 KB boundary, so we are
// certain that this line will get the right block.
Block := PInstanceBlock(DWORD(Result) And Not 4095);
// Now, tell the program that we used another instance from the
// Instance block.
Inc(Block.InstCount);
{$IFDEF VCL_Synchronize}
Finally
// No need to syncronize any more.
LeaveCriticalSection(ObjInstLock);
End;
{$ENDIF}
End;
Procedure FreeObjectInstance(ObjectInstance
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
ointer);
Var Block
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
InstanceBlock;
Inst, Last
![Stick Out Tongue :P :P](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f61b.png)
ObjectInstance;
Begin
If ObjectInstance <> Nil Then
Begin
{$IFDEF VCL_Synchronize}
// Synchronize if needed
EnterCriticalSection(ObjInstLock);
Try
{$ENDIF}
// Linked list manipulation
PObjectInstance(ObjectInstance)^.Prev := Nil;
PObjectInstance(ObjectInstance)^.Next := InstFreeList;
InstFreeList^.Prev := ObjectInstance;
InstFreeList := ObjectInstance;
// Get the parent block
Block := PInstanceBlock(DWORD(ObjectInstance) And Not 4095);
// Decrement the number of used instances
Dec(Block^.InstCount);
// Is any instance used?
If Block^.InstCount = 0 Then
Begin
// If not, we can free this block
// The next while loop will remove all instances
// from the Free List, and only then can the block be freed
// Since the InstCount is 0, ALL of the instances ARE
// in the free list, so we don't need to worry about
// the validity of their fields.
Inst := @Block^.Instances[0];
Last := @Block^.Instances[InstanceCount];
While Inst <> Last Do
Begin
If Inst = InstFreeList Then
Begin
InstFreeList := InstFreeList^.Next;
If InstFreeList <> Nil Then
InstFreeList^.Prev := Nil;
End
Else
Begin
Inst^.Prev^.Next := Inst^.Next;
If Inst^.Next <> Nil Then
Inst^.Next^.Prev := Inst^.Prev;
End;
Inc(Inst); // Essentially, pointers are increased by
// the size of the structure they point ot
![Smile :-) :-)](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f642.png)
End;
If Inst = InstFreeList Then
Begin
InstFreeList := InstFreeList^.Next;
If InstFreeList <> Nil Then
InstFreeList^.Prev := Nil;
End
Else
Begin
Inst^.Prev^.Next := Inst^.Next;
If Inst^.Next <> Nil Then
Inst^.Next^.Prev := Inst^.Prev;
End;
// It's now time to remove the block from the list of
// Instance blocks
If Block = InstBlockList Then
Begin
InstBlockList := InstBlockList^.Next;
If InstBlockList <> Nil Then
InstBlockList^.Prev := Nil;
End
Else
Begin
Block^.Prev^.Next := Block^.Next;
If Block^.Next <> Nil Then
Block^.Next^.Prev := Block^.Prev;
End;
// The block is no longer references in the instance
// list nor Instance block list, and therefore can
// safely be removed
VirtualFree(Block, 0, MEM_RELEASE);
End;
{$IFDEF VCL_Synchronize}
Finally
// Release the lock.
LeaveCriticalSection(ObjInstLock);
// If no more blocks are left, we can delete the critical section
If InstBlockList = Nil Then
Begin
DeleteCriticalSection(ObjInstLock);
ObjInstInit := False;
End;
// NOTE: The section and ObjInstInit are not synchronized.
// However, we don't need to worry about them, since:
// The first form to be created, is certain to be the application
// form (and it's the first time the MOI will be called)
// and at the time, no threads are going to be created.
// Thus, the critical section will be initialized, in
// thread safe environment, and can be safely used after that.
// The last instance to be freed, is certainly the
// application instance. At that time, no threads are likely to
// exist, and after that the application terminates, and so
// the critical section can be safely deleted.
End;
{$ENDIF}
End;
End;