Lock Me Away - February 1998

We recently encountered a rather nasty VB bug that took about 2 months to track down. The mystery message:


Problem Analysis

The problem began with a VB 5 native compiled program. We had not seen this problem with VB 4 or VB 5 compiled to p-code. And the bug was not occurring in the IDE. So we had to localize the problem. And localizing meant that we would rip out part of the application (over 50,000 lines of code) recompile the application then run it again. If we still had the error we would rip out more code until the problem did not occur any more. Then we knew that the last code ‘ripped-out’ contained the code causing the problem. It took three 10-hour days to localize the problem.

The Problem

Seems that if Exit For is used within a For/Next routine and a With statement is used to reference a user defined type (UDT) array, VB is not unlocking the array reference. Code similar to the following causes the problem:

'Declare in a Module
Type MyType
    iNumber As Integer
End Type
Public MyArray() as MyType

'Code in a form or module:
Dim i as Integer
For i = 1 to 10
'Put numbers in the array
    Redim Preserve MyArray(1 to i) as MyType
    MyArray.iNumber = i

'The Code that will lock the array
For i = 1 to 10
    With MyArray(i) '
Array is Locked
        If .iNumber = 4 Then
            Exit For
'Not Unlocked
        End If
    End With

'Some where else in another routine
Erase MyArray() 'This is where it fails.

Crash In the IDE as Well

Israel Burman (Microsoft Contractor) was able to cause the problem in the IDE as well. Erase the array in the same routine as the Exit For statement and VB will crash the application in the IDE.

A Workaround

Israel developed a ‘workaround’ by writing a function that ‘unlocks’ the array. It uses an undocumented VB function (VarPtrArray) so is not guaranteed to work in future releases. And the reference to msvbvm50.dll will change. The code:

' API function to move memory
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSrc As Any, ByVal Length As Long)
Public Declare Function VarPtrArray Lib "msvbvm50.dll" Alias "VarPtr" (Ptr() As Any) As Long

Sub UnlockArray(ByVal plArrayAddress As Long)

    Dim lArray(4) As Long ' a buffer to store first members of the SafeArray structure
    Dim lAddress As Long ' a variable to store the address of the SafeArray

    'dereferencing the pointer - plArrayAddress is a pointer to a pointer
    CopyMemory lAddress, ByVal plArrayAddress, 4

    ' exiting if null pointer
    If lAddress = 0 Then Exit Sub

    ' copying first 4 members to our lArray buffer
    ' members: cDims (WORD), fFeatures (WORD), cbElements (DWORD), _
    cLocks (DWORD) = 12 bytes
    ' we are interested in cLocks - it will be stored in the 3rd long = lArray(2)
    CopyMemory lArray(0), ByVal lAddress, 12

    ' exiting if cLocks <0 - this might indicate it is not a SafeArray
    If lArray(2) < 0 Then Exit Sub

    ' turning cLocks back to 0
    lArray(2) = 0

    ' copying the changed buffer back to the SafeArray structure
    CopyMemory ByVal lAddress, lArray(0), 12

End Sub

Sample Call to UnlockArray

' to fix the problem
UnlockArray VarPtrArray(MyArray())
Erase MyArray()

Download As Word 97 Document

Click here to download this page as a zipped Word 97 Document [8k].

Previous Tip    Tip Index    Next Tip


Updated 2000/10/29