首页 > 解决方案 > 异步监听由 Ref 传递的值

问题描述

我在 VBA 中创建了一个类,该类旨在监视变量直到它发生变化。该类异步运行;TickWindows Timer API每隔一秒左右调用一次该方法 - 比如Application.OnTime

Option Explicit

Public Event Tick()
Public Event Complete()

Private Type tTimer
    tickFrequency As Double                      'in seconds
    conditionMet As Boolean
End Type

Private this As tTimer

Public Sub await(ByRef waitUntil As Boolean, Optional ByVal tickFrequency As Double = 1)
    this.conditionMet = waitUntil 'only creates a copy, doesn't point to the same variable
    startTicking tickFrequency, Me
End Sub

Public Sub Tick()
    If this.conditionMet Then  'If initially False then will never be updated to True
        stopTicking
        RaiseEvent Complete
    Else
        RaiseEvent Tick
    End If
End Sub

Private Sub Class_Terminate()
    stopTicking
End Sub

称为

Dim someCondition As Boolean
'evaluate condition
await someCondition, 0.5 'check back every half a second
'continue other processes which may alter the value of someCondition

这个想法是通过条件byRef,以便可以监控每个滴答声的变化。同时,其他异步运行的代码(例如工作表上的按钮)可以根据需要编辑变量的值。

我可以想到一些解决方法;

然而,这两个都需要我不想要的调用方的额外步骤。

我想知道我是否可以做一些诡计VarPtr- 如果我理解正确,这将返回传递的变量内存中的地址byRef。因此,通过在我的班级中保存该地址的副本,我可以在需要时在该位置查找变量。但是我不知道该怎么做,也不能简洁地表达这个问题来搜索!

标签: vbaexcelpointersasynchronous

解决方案


正如我所提到的,一种方法是使用该VarPtr函数来查找值。CopyMemory Api 方法将查找地址中的值移动到新变量中。

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As _
    Any, source As Any, ByVal bytes As Long)

' read a value of any type from memory

Function Peek(ByVal address As Long, ByVal ValueType As VbVarType) As Variant
    Select Case ValueType
        Case vbByte
            Dim valueB As Byte
            CopyMemory valueB, ByVal address, 1
            Peek = valueB
        Case vbInteger
            Dim valueI As Integer
            CopyMemory valueI, ByVal address, 2
            Peek = valueI
        Case vbBoolean
            Dim valueBool As Boolean
            CopyMemory valueBool, ByVal address, 2
            Peek = valueBool
        Case vbLong
            Dim valueL As Long
            CopyMemory valueL, ByVal address, 4
            Peek = valueL
        Case vbSingle
            Dim valueS As Single
            CopyMemory valueS, ByVal address, 4
            Peek = valueS
        Case vbDouble
            Dim valueD As Double
            CopyMemory valueD, ByVal address, 8
            Peek = valueD
        Case vbCurrency
            Dim valueC As Currency
            CopyMemory valueC, ByVal address, 8
            Peek = valueC
        Case vbDate
            Dim valueDate As Date
            CopyMemory valueDate, ByVal address, 8
            Peek = valueDate
        Case vbVariant
            ' in this case we don't need an intermediate variable
            CopyMemory Peek, ByVal address, 16
        Case Else
            Err.Raise 1001, , "Unsupported data type"
    End Select

End Function

然后可以像这样使用

Public Sub await(ByRef waitUntil As Boolean, Optional ByVal tickFrequency As Double = 1)
    this.conditionAddress= VarPtr(waitUntil) 'only creates a copy, doesn't point to the same variable
    startTicking tickFrequency, Me
End Sub


Public Sub Tick()
    If Peek(this.conditionAddress, vbBoolean) Then  'If initially False then will never be updated to True
        stopTicking
        RaiseEvent Complete
    Else
        RaiseEvent Tick
    End If
End Sub

每次检查变量的值Tick


推荐阅读