malloc in VB?
A COM object you can use in VB providing equivalent functions to C runtime's malloc, free and delete
Of course you're not supposed to allocate your own memory in Basic. Don't be so silly, what are you thinking of? And you should stop using that CopyMemory call and certainly those pointers should not be there at all.
Just in case you're not feeling like any sort of Basic purist, this article is here to show you how to create little chunks of memory completely independently of variables or classes, how to access their contents, reallocate them and free them.
Just don't tell your boss that this is what you're doing...
What's It Good For?
This code has got three main uses:
Getting a Malloc Object
IMalloc is a COM interface which defines the methods and properties a COM Malloc object will support. Anyone is free to design their own implementation of this interface and share it between objects. One such implementation is provided by the Windows Shell, and you can get it using the ShGetMalloc call.
The first problem is how to use this call. The API declaration can be written like this:
Private Declare Function SHGetMalloc Lib "shell32" (ppMalloc As Object) As Long
You could just pass a standard VB Object variable into the ppMalloc variable, but that means you end up with an untyped object which will refer to the Shell's Malloc object through the comparatively slow late-binding (Automation) method. In addition you don't get any design-time auto-complete nor do you get any compile-time syntax checking.
A much better way to use this object is through a Type Library. Within a Type Library you can define the IMalloc interface so you can then use IMalloc as if it were a predefined object in Visual Basic. You can then rewrite the API call to get early-binding and all the advantages that go with that:
Private Declare Function SHGetMalloc Lib "shell32" (ppMalloc As IMalloc) As Long
Remember that Type Libraries are only required when working in the VB IDE. You don't have to ship them with any binaries you build from VB, because VB will already have compiled in the Type Library information.
This sample, and other samples at this site, use Brad Martinez's IShellFolder Extended Type Library (v1.2), however there are other type libraries available containing the IMalloc interface, notably Win.TLB and WinU.TLB shipped with Bruce McKinney's Hardcore VB. I chose this type library simply because it is smaller than the Hardcore VB versions size, making it a more reasonable download!
Now to get the IMalloc interface you use this code:
Private Declare Function SHGetMalloc Lib "shell32" (ppMalloc As IMalloc) As Long ' Defined as an HRESULT that corresponds to S_OK. Private Const NOERROR = 0 Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _ lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long) ' Returns a reference to the IMalloc interface. Public Function isMalloc() As IMalloc Static im As IMalloc If (im Is Nothing) Then If Not (SHGetMalloc(im) = NOERROR) Then ' Fatal error Err.Raise 7 End If End If Set isMalloc = im End Function
The isMalloc property is made public and put in a module to encourage all code in the same project to use the same instance of the object.
Allocating and Freeing Memory
Once you have a Malloc object you can then start creating and destroying memory at will! The following table summarises the available methods:
Examples Of Use
The following examples demonstrate how to move data from Visual Basic data types to a pointer allocated through the Malloc object:
To save and restore a Byte Array
' To Save the array b(): Public Function SaveToMemory(ByRef b() as Byte) As Long Dim lSize As Long Dim lPtr As Long On Error Resume Next ' Error checking: UBound/LBound can fail. lSize = UBound(b) - LBound(b) If Err.Number=0 Then lSize = lSize + 1 Else ' No Data: lSize = 0 End If On Error Goto 0 lPtr = isMalloc.Alloc(lSize + 4) If Not (lPtr=0) Then ' Store the size of the array: CopyMemory ByVal lPtr, lSize, 4 If lSize > 0 Then ' Store the array: CopyMemory ByVal lPtr + 4, b(0), lSize End If End If ' Return the Value of lPtr SaveToMemory = lPtr End Function ' To Restore the array from the Pointer lPtr: Public Sub RestoreFromMemory(ByVal lPtr As Long, ByRef b() as Byte) Dim lSize As Long Erase b If Not (lPtr = 0) Then ' Get the size of the array: CopyMemory lSize, ByVal lPtr, 4 If lSize > 0 Then ReDim b(0 To lSize-1) As Byte CopyMemory b(0), ByVal lPtr, lSize End If End If End Sub
To save and restore a User-Defined Type
Provided there are no variable length strings in the type, then you can save and restore a type in exactly the same way as for a ByteArray. Just replace the last argument with Len(udtThis) where udtThis is your Type.
To save and restore a String
' To Save the String sThis Public Function SaveToMemory(ByRef sThis as String) As Long Dim lSize As Long Dim lPtr As Long ' Size of sThis in Bytes: lSize = LenB(sThis) lPtr = isMalloc.Alloc(lSize + 4) If Not (lPtr=0) Then ' Store the size of the string: CopyMemory ByVal lPtr, lSize, 4 If lSize > 0 Then ' Store the Unicode String: CopyMemory ByVal lPtr + 4, ByVal StrPtr(sThis), lSize End If End If ' Return the Value of lPtr SaveToMemory = lPtr End Function ' To Restore the array from the Pointer lPtr: Public Function RestoreFromMemory(ByVal lPtr As Long) As String Dim lSize As Long Dim sThis As String If Not (lPtr = 0) Then ' Get the size of the array: CopyMemory lSize, ByVal lPtr, 4 If lSize > 0 Then sThis = String$(lSize\2,0) CopyMemory ByVal StrPtr(sThis), ByVal lPtr + 4, lSize End If End If End Sub
Other VB types can be moved using similar techniques.
The sample code in the download illustrates four different ways to create a Stack object capable of storing strings. A Stack is a simple data structure in which there are only two operations - pushing an item onto the stack and poping it back off again. The last item to be added to the stack is the first to be retrieved.
Whilst such a simple data structure is most easily achieved using arrays (and in fact this method runs quickest for most amounts of data), allocating data using IMalloc always comes a close second and in fact beats the array method for 50,000 elements: