窗体上放一个TTimer,然后双击输入:

procedure TForm1.Timer1Timer(Sender: TObject);
var
cvs: TCanvas;
Rect: TRect;
Str: string;
begin
cvs := Canvas;
cvs.Font.Style := [fsBold, fsItalic];
cvs.Font.Size := ;
Randomize;
cvs.Font.Color := Random($FFFFFF);
Rect := Screen.DesktopRect;
Str := 'Delphi 绘图';
cvs.TextRect(Rect, , , Str);
end;

再添加一个新窗体和2个按钮:

procedure TForm1.Button1Click(Sender: TObject);
var
s : AnsiString;
begin
s:='dddd';
ShowMessage(s);
end; procedure TForm1.Button2Click(Sender: TObject);
begin
form2.ShowModal;
end;

执行Button1或者Button2之后,Timer1仍在主窗体上不停的绘制文字,这是为什么?

------------------------------------------------------------------------------------

查看ShowModal的内容:

function TCustomForm.ShowModal: Integer;
var
WindowList: Pointer;
SaveFocusCount: Integer;
SaveCursor: TCursor;
SaveCount: Integer;
ActiveWindow: HWnd;
begin
CancelDrag;
if Visible or not Enabled or (fsModal in FFormState) or
(FormStyle = fsMDIChild) then
raise EInvalidOperation.Create(SCannotShowModal);
if GetCapture <> then SendMessage(GetCapture, WM_CANCELMODE, , );
ReleaseCapture;
Application.ModalStarted;
try
Include(FFormState, fsModal);
ActiveWindow := GetActiveWindow;
SaveFocusCount := FocusCount;
Screen.FSaveFocusedList.Insert(, Screen.FFocusedForm);
Screen.FFocusedForm := Self;
SaveCursor := Screen.Cursor;
Screen.Cursor := crDefault;
SaveCount := Screen.FCursorCount;
WindowList := DisableTaskWindows();
try
Show;
try
SendMessage(Handle, CM_ACTIVATE, , );
ModalResult := ;
repeat
Application.HandleMessage; // 该怎么处理还是怎么处理,消息该发送给谁,还是发送给谁
if Application.FTerminate then ModalResult := mrCancel else
if ModalResult <> then CloseModal;
until ModalResult <> ;
Result := ModalResult;
SendMessage(Handle, CM_DEACTIVATE, , );
if GetActiveWindow <> Handle then ActiveWindow := ;
finally
Hide;
end;
finally
if Screen.FCursorCount = SaveCount then
Screen.Cursor := SaveCursor
else Screen.Cursor := crDefault;
EnableTaskWindows(WindowList);
if Screen.FSaveFocusedList.Count > then
begin
Screen.FFocusedForm := Screen.FSaveFocusedList.First;
Screen.FSaveFocusedList.Remove(Screen.FFocusedForm);
end else Screen.FFocusedForm := nil;
if ActiveWindow <> then SetActiveWindow(ActiveWindow);
FocusCount := SaveFocusCount;
Exclude(FFormState, fsModal);
end;
finally
Application.ModalFinished;
end;
end;

再看看Application.HandleMessage的源码:

procedure TApplication.HandleMessage;
var
Msg: TMsg;
begin
if not ProcessMessage(Msg) then Idle(Msg);
end; function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Handled: Boolean;
begin
Result := False;
if PeekMessage(Msg, , , , PM_REMOVE) then
begin
Result := True;
if Msg.Message <> WM_QUIT then
begin
Handled := False;
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
begin
TranslateMessage(Msg);
DispatchMessage(Msg); // 仍然正确发送消息
end;
end
else
FTerminate := True;
end;
end;

------------------------------------------------------------------------------------

顺便我还查了一下TTimer的源码,发现它的句柄是借来的,而不是控件所在窗体的句柄:

constructor TTimer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FEnabled := True;
FInterval := ;
FWindowHandle := Classes.AllocateHWnd(WndProc);
end; procedure TTimer.UpdateTimer;
begin
KillTimer(FWindowHandle, );
if (FInterval <> ) and FEnabled and Assigned(FOnTimer) then
if SetTimer(FWindowHandle, , FInterval, nil) = then
raise EOutOfResources.Create(SNoTimers);
end; function AllocateHWnd(Method: TWndMethod): HWND;
var
TempClass: TWndClass;
ClassRegistered: Boolean;
begin
UtilWindowClass.hInstance := HInstance;
ClassRegistered := GetClassInfo(HInstance, UtilWindowClass.lpszClassName,
TempClass);
if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then
begin
if ClassRegistered then
Windows.UnregisterClass(UtilWindowClass.lpszClassName, HInstance);
Windows.RegisterClass(UtilWindowClass);
end;
Result := CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
'', WS_POPUP {!0}, , , , , , , HInstance, nil);
if Assigned(Method) then
SetWindowLong(Result, GWL_WNDPROC, Longint(MakeObjectInstance(Method)));
end;

但是不管怎么说,TTimer的句柄所在的窗口也是也是另外一个窗口,所以不影响结论。

------------------------------------------------------------------------------------

总结:其实这个特性很重要。在许多程序中,模态窗口很重要,因为可以防止许多不必要的操作失误。然而它的运行,却不妨碍程序后台继续运行消息循环从而运行其它任务(如有必要的话),这样的设计,实在是绝美!

TForm.ShowModal只是接管消息循环,禁止外部键盘和鼠标输入到别的窗口,但并不封锁其它窗口继续获取消息(比如WM_TIMER消息仍可被发送到别的窗口上)的更多相关文章

  1. Android的消息循环机制 Looper Handler类分析

    Android的消息循环机制 Looper Handler类分析 Looper类说明   Looper 类用来为一个线程跑一个消息循环. 线程在默认情况下是没有消息循环与之关联的,Thread类在ru ...

  2. 【转】Android开发实践:自定义带消息循环(Looper)的工作线程

    http://ticktick.blog.51cto.com/823160/1565272 上一篇文章提到了Android系统的UI线程是一种带消息循环(Looper)机制的线程,同时Android也 ...

  3. 深入探讨MFC消息循环和消息泵

    首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情.在MFC ...

  4. 什么是消息循环,一个简单的win32程序如何运行?

    预备知识 1.什么是句柄? (HANDLE) 在win32编程中有各种句柄,那么什么是句柄呢? #define DECLARE_HANDLE(name) struct name##_ { int un ...

  5. Android应用程序线程消息循环模型分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6905587 我们知道,Android应用程序是 ...

  6. Chromium on Android: Android在系统Chromium为了实现主消息循环分析

    总结:刚开始接触一个Chromium on Android时间.很好奇Chromium主消息循环是如何整合Android应用. 为Android计划,一旦启动,主线程将具有Java消息层循环处理系统事 ...

  7. QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数

    QT源码解析(一) QT创建窗口程序.消息循环和WinMain函数 分类: QT2009-10-28 13:33 17695人阅读 评论(13) 收藏 举报 qtapplicationwindowse ...

  8. Win32 SDK 编程开始, 创建窗口, 消息的处理, 消息循环

    Windows SDK 编程的一般步骤为: 1. 注册窗口类, 使用到的结构 WNDCLASSEX, 函数 RegisterClassEx. 2. 创建窗口, 函数 CreateWindowEx. 3 ...

  9. win32编程中消息循环和WndProc()窗口过程函数

    原文地址:https://blog.csdn.net/zxxSsdsd/article/details/45504383 在win32程序的消息循环函数中  while (GetMessage (&a ...

随机推荐

  1. 使用CDN对动态网站内容加速有效果吗

      个资源文件,有利于减少原始服务器的压力.        缓存网页内容        对于动态网站而言,部分访问量大的网页内容可能改观不大,好比论坛的首页,置顶的帖子很少泛起大转变,因此这样的网页可 ...

  2. Mac中MacPorts安装和使用

    文章转载至http://www.zikercn.com/node/8 星期四, 06/07/2012 - 19:02 - 张慧敏 MacPorts简单介绍 MacPorts,以前叫做DarwinPor ...

  3. android开发1:安卓开发环境搭建(eclipse+jdk+sdk)

    计划折腾折腾安卓开发了,从0开始的确很痛苦,不过相信上手应该也不会太慢.哈哈 一.Android简介 Android 是基于Linux内核的软件平台和操作系统. Android构架主要由3部分组成,l ...

  4. 【C/C++多线程编程之四】终止pthread线程

    多线程编程之终止pthread线程       Pthread是 POSIX threads 的简称,是POSIX的线程标准.           终止线程似乎是多线程编程的最后一步,但绝不是本系列教 ...

  5. 如何灵活使用 ActionBar, Google 音乐ActionBar 隐藏和显示效果

    ActionBar 的历史这里就不介绍了,相信大家都清楚:在一个 app 中,如果 ActionBar 运用的好,那么将会省去大量的代码,而且整个 app 效果也相当不错,大家有兴趣可以下载 goog ...

  6. Opera浏览器测试移动端网站和模拟手机浏览器的方法

    链接地址:http://www.neirong.org/post-256.html?utm_source=tuicool Chrome浏览器请看:Chrome浏览器测试移动端网站和模拟手机浏览器的方法 ...

  7. ORACLE实例恢复过程详细分析--使用dump、BBED等多种工具结合分析

    ---友情提示,内容较多,可以从博文左上的+目录选择小节方便阅读.  实验思路:  --实验相关TRACE文件:http://download.csdn.net/detail/q947817003/6 ...

  8. PHP把数字ID转字母ID

    PHP把数字ID转字母ID ID是网站中经常出现的,它一般是数字,但是我们发现现在的网站很多ID都是字母了,比如YouTube的视频播放页它的URL类似/watch?v=yzNjIBEdyww. 下面 ...

  9. freemarker自己定义标签报错(三)

    freemarker自己定义标签 1.错误描写叙述 freemarker.core.ParseException: Encountered " " at line 14, colu ...

  10. js遍历对象的数组

    遍历数组: 1.js关键for遍历 2.jquery提供each功能 ----------------------------------- $.each(array, function(){     ...