首页 > 解决方案 > 在 VBA 中使用 Waitable Timer 对象 - 无效句柄错误

问题描述

我正在尝试在 VBA 中使用 Waitable Timer 对象,因为我想以低于 1 秒的延迟(所以没有Application.OnTime)和参数(所以没有SetTimerAPI)异步调用某些东西

我还没有发现有人在其他任何地方尝试过这个,所以我不得不从头开始,但我认为这应该是可行的。以下是 API 声明:

Public Declare Function CreateWaitableTimer Lib "kernel32" Alias "CreateWaitableTimerA" ( _
                        ByVal lpTimerAttributes As Long, _
                        ByVal manualReset As Boolean, _
                        ByVal lpTimerName As Long) As Long
'The A meaning Ansi not Unicode https://jeffpar.github.io/kbarchive/kb/145/Q145727/

Public Declare Function SetWaitableTimer Lib "kernel32" ( _
                        timerHandle As Long, _
                        lpDueTime As fileTime, _
                        lPeriod As Long, _
                        pfnCompletionRoutine As Long, _
                        lpArgToCompletionRoutine As Long, _
                        fResume As Boolean) As Boolean

其中引用了一个文件时间(结构)

'see https://social.msdn.microsoft.com/Forums/sqlserver/en-US/a28a32c6-df4e-41b9-94ce-6260812dd92f/problem-trying-to-run-32-bit-vba-program-on-a-64-bit-machine?forum=exceldev
Public Type fileTime
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type

我这样调用 API:

'[...]

args = 1234 'public args As Long so it doesn't go out of scope while the timer is waiting

Dim timerHandle As Long
timerHandle = CreateWaitableTimer(0, False, 0)

Debug.Print GetSystemErrorMessageText(Err.LastDllError)

If Not SetWaitableTimer(timerHandle, absoluteDueTime, 0, AddressOf TimerCallbacks.pointerProc, VarPtr(args), False) Then
    Debug.Print "Error: "; GetSystemErrorMessageText(Err.LastDllError)
End If

GetSystemErrorMessageText来自 Chip Pearson。absoluteDueTime是一个变量,在过程中提前1 秒fileTime设置为Now + 1 秒。我进入即时窗口:

0 - 操作成功完成。

错误:6 - 句柄无效。

这意味着CreateWaitableTimer似乎有效但SetWaitableTimer无效。

FWIWTimerCallbacks.pointerProc看起来像:

Public Sub pointerProc(ByVal argPtr As Long, ByVal timerLowValue As Long, ByVal timerHighValue As Long)
    Debug.Print "pointerProc called"; Time
End Sub

(但我认为这不是错误所在......)

标签: excelvbawinapitimer

解决方案


哦,问题出在所有内容的隐式 byRef 上:

Public Declare Function SetWaitableTimer Lib "kernel32" ( _
                        timerHandle As Long, _
                        lpDueTime As fileTime, _
                        lPeriod As Long, _
                        pfnCompletionRoutine As Long, _
                        lpArgToCompletionRoutine As Long, _
                        fResume As Boolean) As Boolean

指针必须通过 byVal 还是立即被尊重?这个对吗:

Public Declare Function SetWaitableTimer Lib "kernel32" ( _
                        byVal timerHandle As Long, _
                        byRef lpDueTime As fileTime, _
                        byRef lPeriod As Long, _
                        byVal pfnCompletionRoutine As Long, _
                        byVal lpArgToCompletionRoutine As Long, _
                        byRef fResume As Boolean) As Boolean

不确定是否所有的byRefs 都是必要的


推荐阅读