We need you!

We're working hard on the next version of Developer Fusion. Let us know what you think we should be up to!

Members

Technology Zones

Articles

Hosted By

MaximumASP

Info

Rated
Read 48,756 times

Contents

Downloads

Related Categories

CopyMemory and Arrays: Proper Use - Datatype Exceptions

webjose

Datatype Exceptions

Although CopyMemory achieves great speeds in comparison to the good ol' For..Next loop, there are some datatypes you just can't include in the UDT (or the element type for the array) without special handling. These are variable-length strings, and COM object variables.

The String Datatype

The variable string datatype (... As String) occupies 4 bytes. These 4 bytes are filled with a pointer that points to the first character of the string in memory. Visual Basic handles automatically the allocation/deallocation of the memory occupied by the string characters. But, if you override the pointer by using CopyMemory, you will most likely get a Access Violation error when Visual Basic accesses the supposedly not-used pointer. In order to avoid this, you must get rid of any "residual" pointer as well as any memory allocated, if necessary (not necessary in the examples treated here). The following example shows how to override a string safely with CopyMemory. Note that you must do this cleanup for every single string variable you "free" for use with CopyMemory.

Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)

Public Sub Main()

Dim strString1 As String
Dim strString2 As String

    strString1 = "This string will get moved from strString1 to strString2."
    MsgBox "strString1:" & vbCrLf & vbCrLf & strString1, vbInformation
    CopyMemory strString2, strString1, 4
    'Override with 0 the string pointer stored in strString1.
    'If this is not done, when the variables strString1 and strString2 go out
    'of scope, Visual Basic will attempt deallocation of the memory associated
    'with the strings, assuming that no two variables can point to the same string,
    'which is normally true if you don't hack the variable contents using
    'CopyMemory.
    CopyMemory strString1, 0&, 4
    MsgBox "From strString2:" & vbCrLf & vbCrLf & strString2, vbInformation
End Sub

COM Object Datatypes

The other restriction when using CopyMemory are the object datatypes. Any object variable, like Picture, Font, Object, Form, etc. will present a Access Violation error if you do not clean up the same as you would with strings. This is because of the hidden calls to the AddRef() and Release() methods of the IUnknown interface for the object. I cover a little more in-depth about this problem in my article Circular Referencing to COM Objects. And by the way, the soft referencing method explained in that article can be used in place of the following cleanup method to avoid the Access Violation error.

Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)

Public Sub Main()

Dim oFont1 As Font
Dim oFont2 As Font

    Set oFont1 = New StdFont
    oFont1.Name = "Tahoma"
    MsgBox "oFont1.Name: " & oFont1.Name, vbInformation
    CopyMemory oFont2, oFont1, 4
    'Clean the first variable, just the same as you would with a string
    CopyMemory oFont1, 0&, 4
    MsgBox "oFont2.Name: " & oFont2.Name, vbInformation
End Sub

Summarizing...

Wrapping up these exceptions should be straightforward for you now, but just to make things clear, I will pose a version of PopulateArrayAPI in the case the UDT had any of the exception datatypes. Note the use of the API function ZeroMemory. This function effectively clean up the variables inside the UDT array element, wiping away any problem.

Private Declare Sub ZeroMemory Lib "kernel32.dll" Alias "RtlZeroMemory" (Destination As Any, ByVal Length As Long)

'This is the modified version of PopulateArrayAPI to take into account the use of strings or object
'datatypes in the UDT TypeID
'
'Example:
'
'Public Type TypeID
'    lID As Long
'    strName As String
'    oDispFont As Font
'    bTask1 As Boolean
'    bTask2 As Boolean
'End Type
Public Sub PopulateArrayAPI(ByRef arrData() As TypeID, ByVal lTotal As Long, ByVal oCB As ICallBack)

Dim lCount As Long
Dim lID As Long
Dim lPos As Long
Dim bFound As Boolean

    Randomize
    For lCount = 1 To lTotal
        'Get an ID number that is not in the array
        Do
            lID = oCB.NewID(lTotal)
            'Find the ID in the array
            lPos = QuickSortFindID(lID, arrData, bFound)
        Loop Until Not (bFound)
        ReDim Preserve arrData(1 To lCount)
        If (lPos = -1) Or (lPos = lCount) Then
            'First element in the array
            'No cleanup necessary here
            arrData(lCount).lID = lID
        Else
            'lPos contains the nearest index whose ID is greater than lID,
            'which is the position where the new ID must be.
            CopyMemory arrData(lPos + 1), arrData(lPos), (lCount - lPos) * LenB(arrData(LBound(arrData)))
            'Now the data has been moved and position lPos is free to use!
            'You MUST clean up the lPos slot before the assigntion.
            'If you don't do it, a Access Violation error will arise
            ZeroMemory arrData(lPos), LenB(arrData(LBound(arrData)))
            arrData(lPos).lID = lID
        End If
        oCB.ProgressChange lCount
        Next lCount
End Sub

Conclusion

From this discussion, we can safely state that CopyMemory is a great tool for speeding up the processing of data in Visual Basic, but it is also a two-sided sword as it can make the program crash with a beautiful Access Violation error. Therefore, it is very important to follow the guidelines exposed in order to avoid major headaches.

It can also be concluded that a solid concept of a pointer is required in order to facilitate the use of CopyMemory and other memory-related API functions.

This code is for everyone's use and, although I have tested it the best I can, I cannot be held responsible for its use or misuse, or for any other type of damage WHATSOEVER. Use this code at your own risk. If you find an error or can improve it, feel

Comments

  • ReDimm statement is innefficient

    Posted by kwadrofonik on 26 Jun 2004

    Thanks. Definately some good information, however...

    It is not very practical to use the ReDim statement each iteration of a loop since it copies the entire array to a different memory location in ...

  • Interesting case

    Posted by webjose on 01 Mar 2003

    I just tested and yes, you are correct.

    Private Type MyType
    var1 As Integer
    var2 As Byte
    var3 As Boolean
    End Type

    The above type will get padded to a WORD, not a DWORD. However,...

  • DWORD padding.

    Posted by BitShifter on 01 Mar 2003

    I'm willing to bet that if he first variable in the UDT had been an integer, the following Bytes would have been padded to WORD size and no more.

    This being the start of the week-end, I'll play wit...