Search code examples
apivbams-accesscopymemory

MS Access problems with API and CopyMemory


I have (once again) a problem with MS Access. Access has the somewhat peculiar habit to loose object references if errors in the vba code occur for example. I found a pretty neat solution adressing this problem which seems to work most of the time but sometimes causes Access to crash.

Public CurrentUser As CUser 'CUser is a class containing Userinformation

Private Const C_USER_STORAGENAME As String = "CURRENTUSER_HANDLEID"

Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (destination As Any, source As Any, ByVal length As LongPtr)

Private Const rbHandleProp = C_USER_STORAGENAME

Public Function InitUser() As Integer
    Dim lngUserPtr As LongPtr
    Set CurrentUser = New CUser                    'Cache a copy
    CurrentUser.InitializeUser
    lngUserPtr = ObjPtr(CurrentUser)                'HandleID
    WriteProjProperty rbHandleProp, lngUserPtr      'Write HandleID
End Function

Private Sub WriteProjProperty(key As String, Value As Variant)
    On Error Resume Next
    CurrentProject.Properties(key).Value = Value
    If err.Number = 2455 Then 'No Key!
        CurrentProject.Properties.Add key, Value
    End If
End Sub

Private Function GetCurrentUser(lngUserPtr As LongPtr) As Object
   Dim objUser As Object

   CopyMemory objUser, lngUserPtr, 4 'CRASHES HERE SOMETIMES
   Set GetCurrentUser = objUser
   Set objUser = Nothing
End Function

Public Function ReadProjProperty(key As String) As Variant
    On Error Resume Next
    ReadProjProperty = CurrentProject.Properties(key).Value
End Function

Property Get msCurrentUser() As CUser
    If CurrentUser Is Nothing Then
        Set CurrentUser = GetCurrentUser(CLng(ReadProjProperty(rbHandleProp)))
    End If
    Set msCurrentUser = CurrentUser
End Property

The User-Object is initialized once on startup and the handle is saved. The user-information can be aquired through the Property msCurrentUser. As I said this works most of the time but there seems to be certain cases in which CopyMemory fails. Any help would be appreciated.

Thanks in advance

Jon


Solution

  • Seriously, don't do that - it's a pain, but you'll just have to recreate the object as required. At present you're caching an object reference, i.e. a pointer to a specific location in memory (*). When an unhandled error causes the object to be destroyed, the operating system (or Access' internal memory manager) is unlikely to reuse the memory previously occupied by the object immediately (i.e., the memory pointed to by the reference), which is why your code appears to work sometimes. Even when it does 'work' however, in reality you're deferencing a stale pointer.

    (*) There's actually more than one level of indirection given an Access object reference is a COM/IUnknown pointer under the bonnet, but simplifying in terms of one is enough to make the point.