powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Параметры API SetTimer
9 сообщений из 9, страница 1 из 1
Параметры API SetTimer
    #38019740
Snowblast
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
В процедуре используется установка таймера API функцией SetTimer.

В первом варианте процедура ActivateTimer отрабатывает без ошибок, функция SetTimer использует фактическое имя процедуры, запускаемой таймером (TimerProc).
Код: vbnet
1.
2.
3.
Sub ActivateTimer(ByVal hwnd As Long, ByVal TimerInt As Long)
   SetTimer hwnd, 0, TimerInt, AddressOf TimerProc     'TimerProc - имя процедуры, запускаемой таймером.
End Sub


Во втором варианте при попытке передать в процедуру ActivateTimer в качестве параметра ProcName имя процедуры, запускаемой таймером (TimerProc), выдается ошибка : "Expected Sub, Function, or Property."
Код: vbnet
1.
2.
3.
Sub ActivateTimer(ByVal hwnd As Long, ByVal TimerInt As Long, ByVal ProcName as String)
   SetTimer hwnd, 0, TimerInt, AddressOf ProcName     'ProcName - переменная, содержащая имя процедуры, запускаемой таймером.
End Sub


Каким образом реализовать второй вариант?
...
Рейтинг: 0 / 0
Параметры API SetTimer
    #38019796
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Каким образом реализовать второй вариант?Реализовать первый вариант, а разблюдовку по ProcName делать внутри TimerProc.
...
Рейтинг: 0 / 0
Параметры API SetTimer
    #38023107
Snowblast
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Antonariy,

Если я верно понимаю, нужно в TimerProc передавать параметром ProcName имя запускаемой таймером процедуры и внутри TimerProc осуществлять вызов через Case?
Как правильно передать параметр ProcName или его нужно объявлять как Public?

Подойдет ли предложенное Вами решение, если делать класс для работы с таймером, а на его основе класс собственной коллекции таймеров?
...
Рейтинг: 0 / 0
Параметры API SetTimer
    #38023217
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Параметром таймера можно делать лишь Long-число. Через него можно передать указатель на объект, у которого вызвать ProcName. Можно такие объекты сделать наследниками одного интерфейса, чтобы не было ошибок вызова. Никаких case'ов, это вредно для архитектуры.
SnowblastПодойдет ли предложенное Вами решение, если делать класс для работы с таймером, а на его основе класс собственной коллекции таймеров?Только оно для этого и подойдет.
...
Рейтинг: 0 / 0
Параметры API SetTimer
    #38023262
Snowblast,

в общем-то, Antonariy методику рассказал. Слегка корявая реализация (для VBA) ниже.
Класс с незатейливым названием Timer (в VB можно переименовать, чтобы избежать путаницы)
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
Option Explicit

Private Enum BOOL
   FALSE_BOOL
   TRUE_BOOL
End Enum

Private Declare Function SetTimer Lib "user32" ( _
   ByVal hWnd As Long, ByVal nIDEvent As Timer, _
   ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" ( _
   ByVal hWnd As Long, ByVal nIDEvent As Timer) As BOOL

Private m_hWnd As Long
Private m_nInterval As Long
Private m_fEnabled As Boolean
Private m_fRunning As Boolean

Public Event Timer()

Public Property Get hWnd() As Long
 hWnd = m_hWnd
End Property

Public Property Let hWnd(ByVal hwndOwner As Long)
 Dim lResult As Long
 If m_fRunning Then
    lResult = KillTimer(m_hWnd, Me)
    Debug.Assert lResult = FALSE_BOOL
    lResult = SetTimer(hwndOwner, Me, m_nInterval, AddressOf TimerProc)
    m_fRunning = lResult = 0&
    Debug.Assert m_fRunning
 End If
 m_hWnd = hwndOwner
End Property


Public Property Get Enabled() As Boolean
 Enabled = m_fEnabled
End Property

Public Property Let Enabled(ByVal fEnabled As Boolean)
 Dim lResult As Long
 If m_fEnabled = fEnabled Then Exit Property
 If fEnabled Then
    If m_nInterval > 0 Then
       lResult = SetTimer(m_hWnd, Me, m_nInterval, AddressOf TimerProc)
       m_fRunning = lResult <> 0&
       Debug.Assert m_fRunning
    End If
 Else
    If m_fRunning Then
       lResult = KillTimer(m_hWnd, Me)
       Debug.Assert lResult <> FALSE_BOOL
       m_fRunning = False
    Else
       Debug.Assert False
    End If
 End If
 m_fEnabled = fEnabled
End Property


Public Property Get Interval() As Long
 Interval = m_nInterval
End Property

Public Property Let Interval(ByVal nInterval As Long)
 Dim lResult As Long
 Select Case nInterval
 Case Is > 0:
    m_nInterval = nInterval
    If m_fEnabled Then
       lResult = SetTimer(m_hWnd, Me, nInterval, AddressOf TimerProc)
       m_fRunning = lResult <> 0&
       Debug.Assert m_fRunning
    End If
 Case 0:
    If m_fRunning Then
       lResult = KillTimer(m_hWnd, Me)
       Debug.Assert lResult <> FALSE_BOOL
       m_fRunning = False
    End If
    m_nInterval = 0
 Case Else:
    Err.Raise 5
 End Select
End Property


Friend Sub WM_TIMER_Notify()
 Debug.Print VBA.Timer(), "WM_TIMER_Notify"
 RaiseEvent Timer
End Sub


Private Sub Class_Initialize()
 m_hWnd = hWndAccessApp 'Это для MS Access. Для других VBA-хостов hWnd главного окна, либо же просто 0
End Sub

Private Sub Class_Terminate()
 Enabled = False
End Sub

модуль
Код: vbnet
1.
2.
3.
4.
5.
6.
Option Explicit

Public Sub TimerProc(ByVal hWnd As Long, ByVal uMsg As Long, _
                     ByVal idEvent As Timer, ByVal dwTime As Long)
 idEvent.WM_TIMER_Notify
End Sub

Поленился делать с интерфейсным классом, использовал Friend-метод.
Использование на месте, как у стандартного VB-шного таймера:
Код: vbnet
1.
2.
3.
4.
5.
Dim WithEvents Timer1 As Timer
...
Private Sub Timer1_Timer
...
End Sub

Насчёт коллекции - можно и коллекцию, по образу 8197167 , но надо ли?
...
Рейтинг: 0 / 0
Параметры API SetTimer
    #38028063
Snowblast
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
13-й квартал,

Спасибо за пример. Адаптировал, все работает. Тем не менее прошу Вашего комментария.
В объявлении функций SetTimer, KillTimer и процедуры ProcName параметр idEvent as Long заменен на idEvent as Timer - это и есть передача указателя на объект?

Какие преимущества при реализации через Friend-метод, как в Вашем примере, или через интерфейсный класс?
Ниже рабочий вариант на базе Вашего примера, реализованный через CallByName.

В классе Timer из Вашего примера:
- объявляется свойство ProcName as String, содержащее имя процедуры, запускаемой таймером (TimerSub1, TimerSub2)
- использование Event и WM_TIMER_Notify не требуется.

В классе TimerAction размещаются процедуры, запускаемые таймером:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
Public Sub TimerSub1
...
End Sub

Public Sub TimerSub2
...
End Sub


В модуле:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
Option Explicit

Dim TimerActionProc as TimerAction
Set TimerActionProc = New TimerAction

Public Sub TimerProc(ByVal hWnd As Long, ByVal uMsg As Long, _
                     ByVal idEvent As Timer, ByVal dwTime As Long)
   CallByName TimerActionProc, idEvent.ProcName, vbMethod
End Sub



P.S. В Вашем примере при объявлении процедуры KillTimer, ее тип, видимо, подразумевался как Long, а не BOOL.
...
Рейтинг: 0 / 0
Параметры API SetTimer
    #38029554
SnowblastВ объявлении функций SetTimer, KillTimer и процедуры ProcName параметр idEvent as Long заменен на idEvent as Timer - это и есть передача указателя на объект?Да.
SnowblastКакие преимущества при реализации через Friend-метод, как в Вашем примере, или через интерфейсный класс?Плюсы реализации через Friend-метод:
не создаётся лишней сущности (интерфейсного класса) на уровне проекта;

такая реализация хороша, если VBA-проект является библиотечным (например, создаётся ActiveX DLL, которая будет подключаться в дальнейших проектах). Поскольку Friend-методы не видны за пределами VBA-проекта, из библиотеки ничего лишнего (зачем клиенту знать, что у класса есть метод-"потайная дверь", который вызывается только из одного, не контролируемого притом клиентом, места?) не "торчит".
Плюсы реализации через интерфейсный класс:

из класса ничего не "торчит" уже на уровне проекта, только Public-методы/свойства. Случайно написать код, входящий в "потайную дверь", маловероятно, нужно знать, что класс реализует интерфейс, да ещё получить ссылку на этот интерфейс;

больший универсализм - годится для любого вида проекта.

SnowblastНиже рабочий вариант на базе Вашего примера, реализованный через CallByName.

В классе Timer из Вашего примера:
- объявляется свойство ProcName as String, содержащее имя процедуры, запускаемой таймером (TimerSub1, TimerSub2)
- использование Event и WM_TIMER_Notify не требуется.

В классе TimerAction размещаются процедуры, запускаемые таймером:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
Public Sub TimerSub1
...
End Sub

Public Sub TimerSub2
...
End Sub


В модуле:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
Option Explicit

Dim TimerActionProc as TimerAction
Set TimerActionProc = New TimerAction

Public Sub TimerProc(ByVal hWnd As Long, ByVal uMsg As Long, _
                     ByVal idEvent As Timer, ByVal dwTime As Long)
   CallByName TimerActionProc, idEvent.ProcName, vbMethod
End Sub

Вроде всё и гладко, но не нравится по нескольким причинам сразу. Кроме того, выполняемая при каждом вызове диспетчеризация через CallByName содержит в себе скрытый Select Case по именам методов, время выполнения которого зависит от количества методов, и скрытые циклы в операции сравнения имён методов со строковыми константами, время выполнения которых зависит от длин строк. Думаю, что возможна диспетчеризация за O(1), т. е. за константное время, если опишите задачу подробнее (и согласитесь на некоторый отход от имеющегося видения решения, скорее всего).
SnowblastP.S. В Вашем примере при объявлении процедуры KillTimer, ее тип, видимо, подразумевался как Long, а не BOOL.Я предпочитаю следовать первоисточнику . Это непринципиально, поскольку перечислимые типы в VB приводятся к Long, но и автоматом получать "плюшки" от IDE приятно, и код читабельнее.
...
Рейтинг: 0 / 0
Параметры API SetTimer
    #38031763
Snowblast
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем спасибо за участие. Разобрался.
...
Рейтинг: 0 / 0
Параметры API SetTimer
    #38034369
Фотография Андрей159
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Идея Set Timer сама по себе хороша, но для того чтоб не балела даром голова, нужно отлаживать на форме.таймер и если будет обеспечен обход каких-либо ошибок и не будет доработок в этом месте, можно переписать код на Set Timer (event). Если сразу работать с event Timer заявленым, то програма останавливается при ошибке не на паузу а вылетает без сохраниения
...
Рейтинг: 0 / 0
9 сообщений из 9, страница 1 из 1
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Параметры API SetTimer
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]