我在VBA中创建了一个类,用于监视变量,直到它发生变化。该类以异步方式运行; Windows Timer API每隔一秒左右调用一次Tick
方法 - 比如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
,以便可以监控每个滴答的变化。同时,异步运行的其他代码(例如工作表上的按钮)可以根据需要编辑变量的值。
我可以想到一些解决方法;
waitUntil
作为类的公共变量,以便调用者代码可以直接写入它然而,这两个都需要呼叫方的额外步骤,这是我不想要的。
我想知道我是否可以用VarPtr
做一些诡计 - 如果我理解正确,这将返回传递给byRef
的变量的内存地址。因此,通过在我的课程中保存该地址的副本,我可以在需要时在该位置查找变量。但是我不知道如何做到这一点,不能简单地用短语来搜索这个问题!
好吧,正如我所提到的,一种方法是使用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
检查变量的值