UINT_PTR SetTimer(
HWND hWnd, // handle to window, 用来接收WM_TIMER消息的窗体句柄
UINT_PTR nIDEvent, // timer identifier, 定时器的ID
UINT uElapse, // time-out value, 定时器的超时值
TIMERPROC lpTimerFunc // timer procedure,定时器的处理函数
);
BOOL KillTimer(
HWND hWnd, // handle to window,窗体句柄
UINT_PTR uIDEvent // timer identifier,定时器的标示
);
但是,这种方式的定时并不能达到很精确的定时。因为当有到达了设置的时间时,windows仅仅是将一条WM_TIMER消息放入应用程序的消息队列,如果应用程序没有及时的处理,定时将可能更长。需要注意的是:WM_TIMER消息在应用程序的队列中不会同时出现两次,如果上次的WM_TIMER没有被处理,windows会将其合并成一条新的消息。
再看看Delphi中TTimer组件的继承关系:
TObject
|
|-- TPersist
|
|-- TComponent
|
|-- TTimer
进入TTimer的实现单元,我们可以看到,在TTimer中,有一个窗体句柄:FWindowHandle: HWND;
在TTimer的Create时:
FWindowHandle := Classes.AllocateHWnd(WndProc);
AllocateHWnd过程创建了一个类名为TPutilWindow的窗口:
UtilWindowClass: TWndClass = (
style: 0;
lpfnWndProc: @DefWindowProc;
cbClsExtra: 0;
cbWndExtra: 0;
hInstance: 0;
hIcon: 0;
hCursor: 0;
hbrBackground: 0;
lpszMenuName: nil;
lpszClassName: 'TPUtilWindow');
Result := CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
'', WS_POPUP {!0}, 0, 0, 0, 0, 0, 0, HInstance, nil);
然后设置窗体过程为WndProc.
使用Spy++察看,果然可以看到一个大小为0的窗体。不仅仅在TTimer中使用了该类窗体,而且,所有的弹出菜单(TPopupMenu)的管理窗体都是TPutilWindow。
再看看TTimer的窗口回调函数:WndProc中只处理了WM_TIMER:
procedure TTimer.WndProc(var Msg: TMessage);
begin
with Msg do
if Msg = WM_TIMER then
try
Timer;
except
Application.HandleException(Self);
end
else
Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam);
end;
Timer为动态方法(dynamic):
procedure TTimer.Timer;
begin
//如果用户自定义了FOnTimer事件,则调用该事件
if Assigned(FOnTimer) then FOnTimer(Self);
end;
在设置Interval的中,调用UpdateTimer:
该方法调用Windows API SetTimer设置一个定时器:
procedure TTimer.UpdateTimer;
begin
//如果已经存在,则销毁之前的timer
KillTimer(FWindowHandle, 1);
//重新设置Timer
if (FInterval <> 0) and FEnabled and Assigned(FOnTimer) then
if SetTimer(FWindowHandle, 1, FInterval, nil) = 0 then
raise EOutOfResources.Create(SNoTimers);
end;