# Visual Basic > Office Development >  Class module callback method crashing in VBAx64

## fafalone

I'm using the following method to have a callback for TaskDialogIndirect... it works in VBA7 32bit, VB6 (32bit), twinBASIC 32bit, and twinBASIC 64bit, but crashes in VBA7 64bit (In all scenarios I'm testing the VBA7 method of calling it; I use a workaround that copies it into a UDT consisting of a byte array, to avoid the automatic padding that will break it otherwise. If I disable the callback, the API works, so it's definitely that). 



```
uTDC.pfCallback = tdFARPROC(AddressOf TaskDialogCallbackProc)
uTDC.lpCallbackData = ObjPtr(Me)
```

Then in a standard module,



```
Public Function TaskDialogCallbackProc(ByVal hWnd As LongPtr, ByVal uNotification As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr, ByVal lpRefData As cTaskDialog) As Long: TaskDialogCallbackProc = lpRefData.zz_ProcessCallback(hWnd, uNotification, wParam, lParam): End Function
```

I put a MsgBox in the first line of the callback; it never enters it. cTaskDialog is the name of the class. 

I'm at a loss to explain why this works in all scenarios except 64bit VBA. 

Attaching the full .cls/.bas I'm currently using.

If anybody has a clue what might be going on... or an alternative method for class module callbacks that does work in x64, I could really use some help here.

----------


## fafalone

Ok instead of resolving this specifically, does anyone have a class callback method that *does* work in VBA7 x64?

----------


## wqweto

Timers seem work in x64 VBA7



```
'--- Module1
Option Explicit

Public Sub TimerProc(ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal idEvent As LongPtr, ByVal dwTime As Long)
    Debug.Print Timer
End Sub
```



```
'--- UserForm1
Option Explicit

Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As Long
Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
    
Private m_TimerID As LongPtr

Private Sub CommandButton1_Click()
    If m_TimerID <> 0 Then
        Call KillTimer(0, m_TimerID)
        m_TimerID = 0
    End If
    m_TimerID = SetTimer(0, 0, 1000, AddressOf TimerProc)
    Debug.Print "m_TimerID=" & m_TimerID
End Sub

Private Sub CommandButton2_Click()
    If m_TimerID <> 0 Then
        Call KillTimer(0, m_TimerID)
        m_TimerID = 0
    End If
End Sub
```

cheers,
</wqw>

----------


## Dan_W

The Trick's Timer (in the Codebase) works with 64bit Office, and has the added (underappreciated) benefit of not crashing the host application.

----------


## fafalone

Unfortunately I don't know nearly enough about assembly to turn that into a general method to get the address of a class member... I was able to turn the thunk back into asm, but that's as far as I can get



```
0:  48 83 ec 38             sub    rsp,0x38
4:  48 89 4c 24 40          mov    QWORD PTR [rsp+0x40],rcx
9:  48 89 54 24 48          mov    QWORD PTR [rsp+0x48],rdx
e:  4c 89 44 24 50          mov    QWORD PTR [rsp+0x50],r8
13: 4c 89 4c 24 58          mov    QWORD PTR [rsp+0x58],r9
18: 48 b8 00 00 00 00 00    movabs rax,0x0
1f: 00 00 00
22: ff d0                   call   rax
24: 48 85 c0                test   rax,rax
27: 74 06                   je     0x2f
29: 3c 01                   cmp    al,0x1
2b: 74 23                   je     0x50
2d: eb 66                   jmp    0x95
2f: ff 0d c7 ff ff ff       dec    DWORD PTR [rip+0xffffffffffffffc7]        # 0xfffffffffffffffc
35: 48 31 c9                xor    rcx,rcx
38: 48 ba 00 00 00 00 00    movabs rdx,0x0
3f: 00 00 00
42: 48 b8 00 00 00 00 00    movabs rax,0x0
49: 00 00 00
4c: ff d0                   call   rax
4e: eb 45                   jmp    0x95
50: 48 b9 00 00 00 00 00    movabs rcx,0x0
57: 00 00 00
5a: 48 8b 54 24 40          mov    rdx,QWORD PTR [rsp+0x40]
5f: 4c 8b 44 24 48          mov    r8,QWORD PTR [rsp+0x48]
64: 4c 8b 4c 24 50          mov    r9,QWORD PTR [rsp+0x50]
69: 48 8b 44 24 58          mov    rax,QWORD PTR [rsp+0x58]
6e: 48 89 44 24 20          mov    QWORD PTR [rsp+0x20],rax
73: 48 8d 44 24 30          lea    rax,[rsp+0x30]
78: 48 c7 00 00 00 00 00    mov    QWORD PTR [rax],0x0
7f: 48 89 44 24 28          mov    QWORD PTR [rsp+0x28],rax
84: 48 b8 00 00 00 00 00    movabs rax,0x0
8b: 00 00 00
8e: ff d0                   call   rax
90: 48 8b 44 24 30          mov    rax,QWORD PTR [rsp+0x30]
95: 48 83 c4 38             add    rsp,0x38
99: c3                      ret
```

----------


## wqweto

The thunk is further patched with these

    CopyMemory ByVal pCode + &H1A, m_pEbMode, Len(m_pEbMode)
    CopyMemory ByVal pCode + &H44, pfnKillTimer, Len(pfnKillTimer)
    CopyMemory ByVal pCode + &H52, ObjPtr(Me), 8
    CopyMemory ByVal pCode + &H86, pfnTimerProc, Len(pfnTimerProc)

. . . and finally this

    CopyMemory ByVal m_pAsmThunk + &H3A, lIdEvent, Len(lIdEvent)

Its pfnTimerProc which is the method pfn obtained like this

    CopyMemory pVtbl, ByVal ObjPtr(Me), Len(pVtbl)
    CopyMemory pfnTimerProc, ByVal pVtbl + (TIMERPROC_INDEX + 7) * Len(pfnTimerProc), Len(pfnTimerProc)

So TIMERPROC_INDEX is 5 for 6-th method of the class (which is private none the less) plus 7 more methods from IDispatch

----------


## fafalone

right but isn't the code working with EbMode and KillTimer going to interfere with that?

I don't understand this at all... I mean I understand that the timer proc is a vtable entry in the class module vtable, but I don't understand what the asm code is doing at all, how it even winds up getting executed when it looks to me like it's just sitting in memory, and what it would take to generalize it to utilize a callback in other API calls like TaskDialogIndirect.

I've wanted to learn asm but there's just so many interesting things and so little time  :Frown:

----------

