One of the most powerful and yet one of the least understood new features introduced in Visual basic version 5 was the AddressOf keyword. What this keyword does is return the loaded memory address of a function which can then be passed to any API call which requires a pointer to a function as it's parameter call.
In itself this isn't very exciting, but it does allow the Visual Basic
programmer to use one of the most powerful features in Win32 programming - the
callback.
The following example introduces this and shows one of the most simple
implementations: a timer without using the Visual basic timer control. The API
calls used in this example are:
Public Declare Function SetTimer Lib "user32" (ByVal hwnd As Long,
ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As
Long
Public Declare Function KillTimer Lib "user32" (ByVal hwnd As Long,
ByVal nIDEvent As Long) As Long
The last parameter to SetTimer is defined in the API documentation as
"pointer to a TIMER PROC". This means that you need to use AddressOf
to pass the pointer and the procedure argument that this takes must be
compatible with a TIMERPROC. The C++ declaration of this is:
typedef VOID (CALLBACK* TIMERPROC)(HWND, UINT, UINT, DWORD);
Where hWnd is a LONG which uniquely identifies the window with which the timer
proc is associated. You will notice that the other parameters are defined as
UINT and DWORD - niether of which exist in Visual Basic. This is not a problem,
however, as they are the same size as the Visual Basic LONG data type.Public Sub VB_TIMERPROC(ByVal hwnd As Long, ByVal uint1 As Long, ByVal nEventId
As Long, ByVal dwParam As Long)
Because the return from this procedure is defined as a VOID, it is implemented
in visual basic as a subroutine. A word of warning - this procedure must return
to the calling program even if an error occurs. For this reason, the first line
of code is:
On Error Resume Next
I would not recommend this for any procedure which is not used in a callback,
however.
In order to start the timer running, you need to call the SetTimer API call:Dim lRet As Long
lret = SetTimer(Me.hwnd, 1, 100, AddressOf VB_TIMERPROC)
Typically I would do this in the Form_Load() code. The event id (1) uniquely
identifies this timer, and the elapse (100) is the timer interval in
milliseconds.
Before the form is unloaded, you need to unset the timer. This is done with the
KillTimer call.lret = KillTimer(Me.hwnd, 1)
And there you have it - a timer without using the timer control and a gentle
introduction to callbacks.
Comments