SwingDancer
asked on
How does VB6 manage memory allocated by API calls?
Hello,
From my understanding, VB6 cleans up memory when there are no longer any variables pointing to that piece of memory anymore (either those variables are out of scope or are now pointing elsewhere), but what happens when an API call returns a pointer to somewhere in memory. If VB only knows about the variable containing the pointer and not the actual memory the pointer is referring to, will it ever clean up the memory being pointed to? Or do I need to do something else to free up the memory?
Here's an example of what I'm talking about, where I tried to get the name of a printer using the GetPrinter API:
In the code above (in the GetPrinterName function) lbuffer is passed in place of a Printer_Info_4 type as defined below:
typedef struct _PRINTER_INFO_4 {
LPTSTR pPrinterName;
LPTSTR pServerName;
DWORD Attributes;
} PRINTER_INFO_4, *PPRINTER_INFO_4;
So the first two longs in lbuffer are pointers to strings in memory. But I never access the second string as I have no need for it. So does it just sit there in memory? Am I right to assume VB will handle the first string's memory?
Thanks in advance for your help.
Regards,
Aaron
From my understanding, VB6 cleans up memory when there are no longer any variables pointing to that piece of memory anymore (either those variables are out of scope or are now pointing elsewhere), but what happens when an API call returns a pointer to somewhere in memory. If VB only knows about the variable containing the pointer and not the actual memory the pointer is referring to, will it ever clean up the memory being pointed to? Or do I need to do something else to free up the memory?
Here's an example of what I'm talking about, where I tried to get the name of a printer using the GetPrinter API:
Private Declare Function GetPrinter Lib "winspool.drv" Alias "GetPrinterA" (ByVal hPrinter As Long, _
ByVal Level As Long, pPrinter As Any, ByVal cbBuf As Long, pcbNeeded As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, _
Source As Any, ByVal Length As Long)
Private Declare Function lstrlenA Lib "kernel32" (ByVal lpString As Long) As Long
Private Function GetPrinterName(hdc As Long) As String
Dim lResult As Long
Dim lBuffer() As Long
Dim lBufferSize As Long
Dim lSize As Long
lBufferSize = 12
ReDim lBuffer(0 To lBufferSize - 1) As Long
lResult = GetPrinter(hdc, 4, lBuffer(0), lBufferSize, lBufferSize)
If lResult = 0 Then
'Try resizing the buffer
If Err.LastDllError = ERROR_INSUFFICIENT_BUFFER Then
ReDim lBuffer(0 To lBufferSize) As Long
lResult = GetPrinter(hdc, 4, lBuffer(0), lBufferSize, lBufferSize)
If lResult = 0 Then
'Did not work
Exit Function
End If
Else
'Did not work
Exit Function
End If
End If
GetPrinterName = PointerToStringA(lBuffer(0))
End Function
Private Function PointerToStringA(ByVal lpStringA As Long) As String
Dim Buffer() As Byte
Dim nLen As Long
If lpStringA Then
nLen = lstrlenA(ByVal lpStringA)
If nLen Then
ReDim Buffer(0 To (nLen - 1)) As Byte
CopyMemory Buffer(0), ByVal lpStringA, nLen
PointerToStringA = StrConv(Buffer, vbUnicode)
End If
End If
End Function
In the code above (in the GetPrinterName function) lbuffer is passed in place of a Printer_Info_4 type as defined below:
typedef struct _PRINTER_INFO_4 {
LPTSTR pPrinterName;
LPTSTR pServerName;
DWORD Attributes;
} PRINTER_INFO_4, *PPRINTER_INFO_4;
So the first two longs in lbuffer are pointers to strings in memory. But I never access the second string as I have no need for it. So does it just sit there in memory? Am I right to assume VB will handle the first string's memory?
Thanks in advance for your help.
Regards,
Aaron
ASKER
Thank you egl1044 for your answer. The heap functions look interesting. I think I need to do a fair bit of reading before I fully understand how they work and what they do.
However, right now I want to understand what Visual Basic 6 does with memory allocated by API calls such as GetPrinter. Does it automatically clean up the memory and if so how/when? If not, what do I need to do to free it up?
However, right now I want to understand what Visual Basic 6 does with memory allocated by API calls such as GetPrinter. Does it automatically clean up the memory and if so how/when? If not, what do I need to do to free it up?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Here is a full example using heap functions, some minor modifications from the previous post, you could also just use GetDefaultPrinter() API or even the Printer Class in VB directly >> (Printer.Name) If your just after the local printer name.
Option Explicit
Private Const HEAP_ZERO_MEMORY = &H8&
Private Type PRINTER_INFO_4W
pPrinterName As Long
pServerName As Long
Attributes As Long
End Type
Private Declare Function GetProcessHeap Lib "kernel32.dll" () As Long
Private Declare Function HeapAlloc Lib "kernel32.dll" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function HeapReAlloc Lib "kernel32.dll" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal lpMem As Long, ByVal dwBytes As Long) As Long
Private Declare Function HeapSize Lib "kernel32.dll" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal lpMem As Long) As Long
Private Declare Function HeapFree Lib "kernel32.dll" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal lpMem As Long) As Long
Private Declare Function OpenPrinterW Lib "winspool.drv" (ByVal pPrinterName As Long, ByRef phPrinter As Long, ByVal pDefault As Long) As Long
Private Declare Function GetPrinterW Lib "winspool.drv" (ByVal hPrinter As Long, ByVal Level As Long, ByVal pPrinter As Long, ByVal cbBuf As Long, ByRef pcbNeeded As Long) As Long
Private Declare Function ClosePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long
Private Declare Sub RtlMoveMemory Lib "kernel32.dll" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)
Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal ptr As Long) As Long
Private Sub Form_Load()
Dim printerInfo4 As PRINTER_INFO_4W
Dim Buffer As Long ' heap buffer
Dim hPrinter As Long
Dim cbNeeded As Long
If OpenPrinterW(StrPtr("HP Deskjet 3920/3940"), hPrinter, 0) Then
'// Allocate memory
Buffer = HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, 12)
Call GetPrinterW(hPrinter, 4, Buffer, 12, cbNeeded)
'// Re-allocate the required size
Buffer = HeapReAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, Buffer, cbNeeded)
'// Get the printer level 4 information
Call GetPrinterW(hPrinter, 4, Buffer, HeapSize(GetProcessHeap, 0, Buffer), cbNeeded)
'// Make the pointers easily accessible.
RtlMoveMemory printerInfo4, ByVal Buffer&, Len(printerInfo4)
' If you were to free this memory before you pulled the data
' it would fail because you (released) the memory. The string
' are allocated within this memory buffer so you must do the
' grunt work before releasing the memory.
Debug.Print printerInfo4.pPrinterName
Debug.Print printerInfo4.pServerName
Debug.Print printerInfo4.Attributes
MsgBox WidePointerString(printerInfo4.pPrinterName)
' When the memory is freed here the pointers are no longer
' valid within the allocated buffer. Nothing to point towards.
'// free memory
HeapFree GetProcessHeap, 0, Buffer
' The memory is no longer valid shouldn't produce a result.
' Note: You may need compile to executable and not test from IDE.
MsgBox WidePointerString(printerInfo4.pPrinterName)
ClosePrinter hPrinter
End If
End Sub
Private Function WidePointerString(ByVal lpPointer As Long) As String
' Return WIDE pointer string
Dim Buffer() As Byte
Dim lpSize As Long
If lpPointer = 0 Then
WidePointerString = vbNullString
Exit Function
End If
lpSize = lstrlenW(lpPointer) * 2
If lpSize <> 0 Then
ReDim Buffer(lpSize) As Byte
RtlMoveMemory Buffer(0), ByVal lpPointer, lpSize
WidePointerString = Buffer
End If
Erase Buffer
End Function
<< Does it automatically clean up the memory and if so how/when? If not, what do I need to do to free it up? >>
My Bad.. I missed this but simply YOU are allocating the memory not the API in this case, although some API that allocate there own memory will have a way to free that memory, but this isn't one of these cases. When you allocate the array the data simply is copied into it and if your byte array is located at the local level it will be cleaned up when the call exits the method. However if you allocated that same byte array at the module level it would retain in memory.
My Bad.. I missed this but simply YOU are allocating the memory not the API in this case, although some API that allocate there own memory will have a way to free that memory, but this isn't one of these cases. When you allocate the array the data simply is copied into it and if your byte array is located at the local level it will be cleaned up when the call exits the method. However if you allocated that same byte array at the module level it would retain in memory.
ASKER
Thanks egl1044.
The more I read of the MSDN documentation the more sense the heaps make. (When I first looked I was like, 'What's a heap' and 'what's a process' and 'what does it mean to allocate').
I do have a couple of questions.
1. In your example you called GetProcessHeap multiple times instead of calling it once and storing it in a variable. Does the value returned from GetProcessHeap change?
2. Why would someone choose to use the default heap over a private heap and vice-versa?
3. I'm still unsure about what VB does if I am not using heaps. Eg. If I were not using heaps and the GetPrinterName function (from my original code sample) was called lots of times would I start to run out of memory? Would this memory get cleared once the program was closed?
The more I read of the MSDN documentation the more sense the heaps make. (When I first looked I was like, 'What's a heap' and 'what's a process' and 'what does it mean to allocate').
I do have a couple of questions.
1. In your example you called GetProcessHeap multiple times instead of calling it once and storing it in a variable. Does the value returned from GetProcessHeap change?
2. Why would someone choose to use the default heap over a private heap and vice-versa?
3. I'm still unsure about what VB does if I am not using heaps. Eg. If I were not using heaps and the GetPrinterName function (from my original code sample) was called lots of times would I start to run out of memory? Would this memory get cleared once the program was closed?
<< In your example you called GetProcessHeap multiple times instead of calling it once and storing it in a variable. >>
Good question! I have been doing that way for awhile it will point to the same default heap value almost all the MSDN examples that use the heap functions always call GetProcessHeap() so I think it stuck with me to use it that way.
<< Why would someone choose to use the default heap over a private heap and vice-versa >>
Creating a private heap is more for threading, something you don't have to worry about in VB6 since it's single threaded. If you had multiple threads you might create a private heap for each thread so different threads do not attempt to access the same memory.
<< I'm still unsure about what VB does if I am not using heaps. >>
No way! You have lots of memory yet and it won't run out especially with your example. When you allocate the array in VB6 it probrably uses LocalAlloc() internally im fairly certain that any local members are freed when the method returns. If you allocate the array at the module level it would retain in memory. There is not much difference other than with a heap you have the ability to allocate a block of bytes , re-allocate a block, and deallocate the block. It provides an easier means to manage the memory but you can still use a byte array in VB6 without issues.
Your code would allocate say maybe 64 bytes (enough to hold the string data to be copied into the buffer) for the printer information and then cleanup when routine exits the method. Thus the only memory footprint you have when calling the function is the size of the buffer you allocated before calling the API. If you on the other hand read a large file into a string or byte array then your application is going to use as much memory as you read into that variable until it's released.
Good question! I have been doing that way for awhile it will point to the same default heap value almost all the MSDN examples that use the heap functions always call GetProcessHeap() so I think it stuck with me to use it that way.
<< Why would someone choose to use the default heap over a private heap and vice-versa >>
Creating a private heap is more for threading, something you don't have to worry about in VB6 since it's single threaded. If you had multiple threads you might create a private heap for each thread so different threads do not attempt to access the same memory.
<< I'm still unsure about what VB does if I am not using heaps. >>
No way! You have lots of memory yet and it won't run out especially with your example. When you allocate the array in VB6 it probrably uses LocalAlloc() internally im fairly certain that any local members are freed when the method returns. If you allocate the array at the module level it would retain in memory. There is not much difference other than with a heap you have the ability to allocate a block of bytes , re-allocate a block, and deallocate the block. It provides an easier means to manage the memory but you can still use a byte array in VB6 without issues.
Your code would allocate say maybe 64 bytes (enough to hold the string data to be copied into the buffer) for the printer information and then cleanup when routine exits the method. Thus the only memory footprint you have when calling the function is the size of the buffer you allocated before calling the API. If you on the other hand read a large file into a string or byte array then your application is going to use as much memory as you read into that variable until it's released.
ASKER
Thank you egl1044 for all your explanations.
- Aaron
- Aaron
No problem buddy, Welcome to EE we hope you stick around =)
1) Allocate the memory on the heap
2) Pull the data you want from the memory.
3) Free the memory leaving only the pulled data in memory.
This gives you complete control over both allocating and deallocation of the memory, the only remaining memory is what you copied from the memory before releasing it.
Heap Functions
http://msdn.microsoft.com/en-us/library/aa366711(v=VS.85).aspx