"Use of Win32" demonstrations
Memory APIs

   Varying size of memory
   Releasing memory
   Viewing memory heaps

Varying size of memory
This is a demonstration of the use of the API VirtualAlloc. There are two tests. In each case a large block of memory addresses is reserved (1M), then memory is allocated (committed) at 1 "page" at a time and then released at 1 page at a time. A "page" is usually 4K, but it is good practice if working in pages to ask the system how big a page is by calling GetSystemInfo. When allocating and releasing memory you need to keep track of the amount of memory in use. The two tests differ slightly in how this is done.
Note that when committing pages VirtualAlloc is given the required starting address for the new page in the lpAddress. The SDK states that this is not necessary, and you need only provide the base address of the memory area. In tests, this did work in W98, but not in W95.
Use the breakpoint MEMORY_VARYSIZE.

MEMORY_VARYSIZE:
MOV EBX,ADDR SYSTEM_INFO
PUSH EBX
CALL GetSystemInfo
MOV EAX,[EBX+4]           ;get page size
MOV [PAGE_SIZE],EAX       ;keep that
PUSH 4h,2000h,100000h,0   ;reserve 1M of read/write memory for variable memory
CALL VirtualAlloc
MOV [VARIDUMP],EAX        ;keep base address of this area for variable memory
MOV [VARIDUMP_END],EAX    ;keep end of memory so far
CALL MEM_ADD_PAGE
CALL MEM_ADD_PAGE
CALL MEM_ADD_PAGE
CALL MEM_REMOVE_PAGE
CALL MEM_REMOVE_PAGE
CALL MEM_REMOVE_PAGE
PUSH 8000h,0,[VARIDUMP]
CALL VirtualFree          ;free area completely
;********************************
PUSH 4h,2000h,100000h,0   ;reserve 1M of read/write memory for variable memory
CALL VirtualAlloc
MOV [VARIDUMP],EAX        ;keep base address of this area for variable memory
MOV D[VARIDUMP_SIZE],0
CALL MEM_ADD_PAGE1
CALL MEM_ADD_PAGE1
CALL MEM_ADD_PAGE1
CALL MEM_REMOVE_PAGE1
CALL MEM_REMOVE_PAGE1
CALL MEM_REMOVE_PAGE1
PUSH 8000h,0,[VARIDUMP]
CALL VirtualFree          ;free area completely
RET
;
MEM_ADD_PAGE:
PUSH 4h,1000h,[PAGE_SIZE],[VARIDUMP_END]    ;commit a page of read/write memory
CALL VirtualAlloc
MOV EAX,[VARIDUMP_END]
ADD EAX,[PAGE_SIZE]
MOV [VARIDUMP_END],EAX    ;keep new end of memory
RET
;
MEM_REMOVE_PAGE:
MOV EAX,[VARIDUMP_END]    ;get current end of memory
SUB EAX,[PAGE_SIZE]
MOV [VARIDUMP_END],EAX    ;keep new end of memory
PUSH 4000h,[PAGE_SIZE],[VARIDUMP_END]  ;decommit a page of memory
CALL VirtualFree
RET
;
MEM_ADD_PAGE1:
MOV EAX,[VARIDUMP_SIZE]
ADD EAX,[PAGE_SIZE]
MOV [VARIDUMP_SIZE],EAX
PUSH 4h,1000h,EAX,[VARIDUMP]   ;commit a page of read/write memory
CALL VirtualAlloc
RET
;
MEM_REMOVE_PAGE1:
MOV EAX,[VARIDUMP_SIZE]
SUB EAX,[PAGE_SIZE]
MOV [VARIDUMP_SIZE],EAX
ADD EAX,[VARIDUMP]
PUSH 4000h,[PAGE_SIZE],EAX     ;decommit a page of memory
CALL VirtualFree
RET

Releasing memory
The SDK (Software Development Kit) when Win32 was first instroduced suggested that before memory can be released using VirtualFree, all the memory area had to have the same status, ie. all committed or all not committed. This test, which works under Windows 95, showed that this is not necessarily the case. Later the SDK was amended to say this did not matter.
At about the same time the SDK was also amended to say that you can use both the MEM_DECOMMIT (4000h) and the MEM_RELEASE (8000h) flags when calling VirtualFree. I found this to work, but to return an API error.
These matters do not fill you with confidence and in my code unless I have one single-size memory block I always play safe by calling VirtualFree twice, once to decommit and once to release. This avoids any chance of my programs eating up memory when in use.
The first test reserves 1M of memory addresses, commits the first page and then releases all the memory in one go. Use the breakpoint MEMORY_RELEASE. Note that the return from VirtualFree is EAX=1 (return TRUE, meaning success). Then IsBadReadPtr is called to check that the memory has disappeared.
This API needs some explanation. Firstly it probably shows in the debugger as IsBadHugeReadPtr, which is the same thing. Secondly it works in a way which only makes sense when you think about it. It actually reads from the address to be checked. If an exception occurs then the address is not properly readable. In fact, therefore, when IsBadReadPtr is called in this code, an exception occurs in Kernel32.Dll. However, GoBug ignores the exception because it occured in shared memory. GoBug only responds to exceptions which were caused by the application being debugged. IsBadReadPtr returns EAX=0 if the address can properly be read and EAX=1 if it cannot, as here.
Finally VirtualFree is called again twice. Firstly using flag 4000h (DECOMMIT) and then using flag 8000h (RELEASE). This is done simply to accord with the original SDK. Note, however, that since the memory has alread been released albeit the wrong way according to the original SDK these APIs return EAX=0 (fail).
Test 2 does the same as test 1 but uses not the first page of memory but the second. Therefore, the first page of memory is not committed but the second is committed. However, the system again permits release of all memory with a single call to VirtualFree with flag 8000h (RELEASE).
The third test does the same as the first two tests, but calls VirtualFree with the flag 0C000h (DECOMMIT and RELEASE). This combination is acceptable according to later versions of the SDK, but was found to return an error in Windows 95 and Windows 98. Try it. Although VirtualFree might return an error, it seems actually to release the memory anyway. If the IsBadReadPtr test returns EAX=1 then the memory was released.
The moral of these tests is that unless you have made an area of memory in one block, all of which is committed or decommitted, it is probably prudent to call VirtualFree twice, once to decommit and once to release.

MEMORY_RELEASE:
MOV EBX,ADDR SYSTEM_INFO
PUSH EBX
CALL GetSystemInfo
MOV EAX,[EBX+4]         ;get page size
MOV [PAGE_SIZE],EAX     ;keep that
;*********************************** TEST 1
;commit area of memory in reserved address space then release the whole lot
PUSH 4h,2000h,100000h,0 ;reserve 1M of read/write memory for variable memory
CALL VirtualAlloc
MOV [VARIDUMP],EAX      ;keep base address of this area for variable memory
MOV [VARIDUMP_END],EAX  ;keep end of memory so far
CALL MEM_ADD_PAGE
MOV EBX,[VARIDUMP]
MOV [EBX],33333333h     ;make use of memory (causes use of physical memory)
PUSH 8000h,0,[VARIDUMP]
CALL VirtualFree        ;free area completely (works although should not)
;******* test to see if memory area still available
PUSH 1,[VARIDUMP]
CALL IsBadReadPtr       ;if access available return value is zero
;******* just in case, do it the way the SDK (Win95 anyway) says it should be done
PUSH 4000h,[PAGE_SIZE],[VARIDUMP]
CALL VirtualFree        ;decommit the page first
PUSH 8000h,0,[VARIDUMP]
CALL VirtualFree        ;free area completely 
;*********************************** TEST 2
;******* try the same committing an intermediate page
PUSH 4h,2000h,100000h,0 ;reserve 1M of read/write memory for variable memory
CALL VirtualAlloc
MOV [VARIDUMP],EAX      ;keep base address of this area for variable memory
MOV EBX,EAX
ADD EBX,[PAGE_SIZE]
PUSH 4h,1000h,[PAGE_SIZE],EBX  ;commit 2nd page of memory
CALL VirtualAlloc
MOV [EBX],33333333h     ;make use of memory (causes use of physical memory)
PUSH 8000h,0,[VARIDUMP]
CALL VirtualFree        ;free area completely (still works)
;******** here comes the proof if works
;******* test to see if memory area still available
PUSH 1,[VARIDUMP]
CALL IsBadReadPtr       ;if access available return value is zero
;******* just in case: the way the SDK (Win95 anyway) says it should be done
PUSH 4000h,[PAGE_SIZE],EBX
CALL VirtualFree        ;decommit the page first
PUSH 8000h,0,[VARIDUMP]
CALL VirtualFree        ;free area completely 
;*********************************** TEST 3
;******* at some point the SDK was changed to say you can use both flags
PUSH 4h,2000h,100000h,0 ;reserve 1M of read/write memory for variable memory
CALL VirtualAlloc
MOV [VARIDUMP],EAX      ;keep base address of this area for variable memory
MOV EBX,EAX
ADD EBX,[PAGE_SIZE]
PUSH 4h,1000h,[PAGE_SIZE],EBX  ;commit 2nd page of memory
CALL VirtualAlloc
MOV [EBX],33333333h     ;make use of memory (causes use of physical memory)
PUSH 0C000h,0,[VARIDUMP]  ;MEM_DECOMMIT and MEM_RELEASE
CALL VirtualFree        ;free area completely (might not work on Win95)
;******** here comes the proof
;******* test to see if memory area still available
PUSH 1,[VARIDUMP]
CALL IsBadReadPtr       ;if access available return value is zero
;******* just in case: the way the SDK (Win95 anyway) says it should be done
PUSH 4000h,[PAGE_SIZE],EBX
CALL VirtualFree        ;decommit the page first
PUSH 8000h,0,[VARIDUMP]
CALL VirtualFree        ;free area completely 
RET

Viewing memory heaps test
This test proves that memory areas created by HeapCreate (or provided by GetProcessHeap) and allocated by HeapAlloc are controlled quite differently from those created by VirtualAlloc. Memory areas created by the "Heap" functions rely on a heap header which keeps information about the heap, and each area of memory is divided into blocks headed by a heap "arena". For a full description of these headers see Matt Pietrek's book "Windows 95 System Programming Secrets". The size of the heap header various from version to version, but is usually 116 bytes ending with the signature "HI". Each heap arena is 4 bytes. The first word (apart from bits 0 and 1 which are used for flags) is the size of the heap block and the most significant byte is the signature A0h. HeapCreate and GetProcessHeap return the address of the heap header. HeapAlloc returns the address of the beginning of the usable block of memory ie. at the end of the heap arena.
Set the breakpoint to VIEWING_HEAP_TEST and single-step down this code. The API RtlFillMemory fills the memory area with the value 78 and then RtlZeroMemory zeroes the same area. You can watch this by making a data inspector in GoBug.
The test is repeated, but this time using the heap provided as part of the heap addresses automatically allocated for the process on start-up (the "default heap" available using GetProcessHeap). The handle returned by GetProcessHeap is the address of the heap header.
Finally VirtualAlloc is used for comparison. Note there is no heap header. Control of the memory area is carried out by Windows internally.
One other interesting thing about this test is that the call to HeapFree (after using the default heap) does not immediately release the physical memory allocated to the memory area. Keep the data inspector open for viewing the address of the usable part of the default keep returned by HeapAlloc. See how it is used again later. It appears that the system may well keep some areas of memory permanently allocated to a process because the memory is likely to be in constant use as text, menus and window titles come and go.

VIEWING_HEAP_TEST:
PUSH 4096        ;maximum size of memory
PUSH 4096        ;amount allocated now
PUSH 0           ;flag
CALL HeapCreate
MOV [hHeap2],EAX
PUSH 100h,0,EAX
CALL HeapAlloc   ;get permissible memory area
MOV EDI,EAX
PUSH 78h,80h,EDI
CALL RtlFillMemory
PUSH 80h,EDI
CALL RtlZeroMemory
PUSH [hHeap2]    ;handle to heap
CALL HeapDestroy
CALL GetProcessHeap     ;get default heap
MOV [hHeap2],EAX
PUSH 100h,0,EAX
CALL HeapAlloc   ;get permissible memory area
MOV EDI,EAX
PUSH 78h,80h,EDI
CALL RtlFillMemory
PUSH 80h,EDI
CALL RtlZeroMemory
MOV ECX,20h
MOV AL,'X'
PUSH EDI
REP STOSB        ;write over it
PUSH 0,[hHeap2]  ;edi already pushed
CALL HeapFree
PUSH 4h          ;flag read and write access
PUSH 1000h       ;commit memory flag
PUSH 800h        ;size of memory to be committed 32K
PUSH 0           ;where to put memory
CALL VirtualAlloc
MOV [hHeap2],EAX
MOV EDI,EAX
MOV ECX,20h
MOV AL,'X'
REP STOSB
PUSH 8000h       ;release flag
PUSH 0           ;all memory to go
PUSH [hHeap2]    ;address of memory area
CALL VirtualFree
PUSH 40h         ;information+ok button only
PUSH 'Testbug - viewing heap test'
PUSH 'This test has already finished!  To watch it with the debugger make breakpoint on VIEWING_HEAP_TEST.'
PUSH [hWnd]
CALL MessageBoxA ;wait till ok pressed
RET