异步监视Ref传递的值

时间:2018-09-02 16:54:40

标签: excel vba excel-vba pointers asynchronous

我在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变量的内存中返回一个地址。因此,通过在我的班级中保存该地址的副本,我可以在需要时在该位置查找变量。但是我不知道该怎么做,也不能简洁地表达这个问题以进行搜索!

1 个答案:

答案 0 :(得分:0)

一种方法,如我所提到的,是使用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检查一次变量的值