Асинхронно контролировать значение, переданное byRef - PullRequest
0 голосов
/ 02 сентября 2018

Я создал класс в 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 голосов
/ 02 сентября 2018

Ну, как я уже упоминал, можно использовать функцию 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...