日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TApplication与主消息循环

發(fā)布時間:2025/3/13 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TApplication与主消息循环 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Windows應用程序的每一個窗口都有一個大的消息循環(huán)以及一個窗口函數(WndProc)用以分發(fā)和處理消息。VCL作為一個Framework,當然會將這些東西隱藏起來,而重新提供一種易用的、易理解的虛擬機制給程序員。
那么VCL是如何做到的呢?
本節(jié)就來解答這個問題。
只要代碼單元中包含了Forms.pas,就會得到一個對象——Application。利用它可以幫助我們完成許多工作。例如要退出應用程序,可以使用
Application.Terminate();
Application對象是VCL提供的,在Forms.pas中可以看到如下這個定義:
var
Application: TApplication;
當創(chuàng)建一個默認的應用程序時,會自動得到以下幾行代碼:
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
這幾行代碼很簡潔地展示了TApplication的功能、初始化、創(chuàng)建必要的窗體、運行……
但是,這幾行代碼具體做了什么幕后操作呢?Application.Run之后,程序流程走向了哪里?
1.脫離VCL的Windows程序
??? 在此,給出一個用純Pascal所編寫的十分簡單的Windows應用程序,以演示標準Windows程序是如何被建立及運行的。
program?WindowDemo;
uses?Windows,?Messages;
//?窗口函數,窗口接到消息時被Windows所調用
function?WindowProc(hwnd?:?HWND;?uMsg?:?Cardinal;?wParam?:?WPARAM;
lParam?:?LPARAM)?:?LResult;?stdcall;
begin
Result?:
=?0;
case?uMsg?of
//?關閉窗口消息,當用戶關閉窗口后,通知主消息循環(huán)結束程序
WM_CLOSE?:?PostMessage(hwnd,?WM_QUIT,?
0,?0);
//?鼠標左鍵按下消息
WM_LBUTTONDOWN?:?MessageBox(hwnd,?
'Hello!',?'和您打個招呼',
MB_ICONINFORMATION);
else
//?其他消息做默認處理
Result?:
=?DefWindowProc(hWnd,?uMsg,?wParam,?lParam);
end;
end;
var
wndcls?:?WNDCLASS;?
//?窗口類的記錄(結構)類型
hWnd?:?THandle;
Msg?:?tagMSG;?
//?消息類型
begin
wndcls.style?:
=?CS_DBLCLKS;?//?允許窗口接受鼠標雙擊
wndcls.lpfnWndProc?:
=?@WindowProc;?//?為窗口類指定窗口函數
wndcls.cbClsExtra?:
=?0;
wndcls.cbWndExtra?:
=?0;
wndcls.hInstance?:
=?hInstance;
wndcls.hIcon?:
=?0;
wndcls.hCursor?:
=?LoadCursor(hInstance,?'IDC_ARROW');
wndcls.hbrBackground?:=?COLOR_WINDOWFRAME;
wndcls.lpszMenuName?:
=?nil;
wndcls.lpszClassName?:
=?'WindowClassDemo';?//?窗口類名稱
//?注冊窗口類
if?RegisterClass(wndcls)?=?0?then
Exit;
//?創(chuàng)建窗口
hWnd?:
=?CreateWindow(
'WindowClassDemo',?//?窗口類名稱
'
WindowDemo',?//?窗口名稱
WS_BORDER?or?WS_CAPTION?or?WS_SYSMENU,?//?窗口類型
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
0,
0,
hInstance,
nil
);
if?hWnd?=?0?then
Exit;
//?顯示窗口
ShowWindow(hWnd,?SW_SHOWNORMAL);
UpdateWindow(hWnd);
//?創(chuàng)建主消息循環(huán),處理消息隊列中的消息并分發(fā)
//?直至收到WM_QUIT消息,退出主消息循環(huán),并結束程序
//?WM_QUIT消息由PostMessage()函數發(fā)送
while?GetMessage(Msg,?hWnd,?0,?0)?do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end.
該程序沒有使用VCL,它所做的事情就是顯示一個窗口。當在窗口上單擊鼠標右鍵時,會彈出一個友好的對話框向您問好。如果從來不曾了解過這些,那么建議您實際運行一下光盤上的這個程序,對其多一些感性認識。
就是這樣一個簡單的程序,演示了標準Windows程序的流程:
(1)從入口函數WinMain開始。
(2)注冊窗口類及窗口函數(Window Procedure)。
(3)創(chuàng)建并顯示窗口。
(4)進入主消息循環(huán),從消息隊列中獲取并分發(fā)消息。
(5)消息被分發(fā)后,由Windows操作系統調用窗口函數,由窗口函數對消息進行 處理。
在Object Pascal中看不到所謂的“WinMain”函數。不過,其實整個program的begin處就是Windows程序的入口。
注冊窗口類通過系統API函數RegisterClass()來完成,它向Windows系統注冊一個窗口的類型。
注冊窗口類型完成后,就可以創(chuàng)建這個類型的窗口實例。創(chuàng)建出一個真正的窗口可通過API函數CreateWindow()來實現。
創(chuàng)建出的窗口實例通過API函數ShowWindow()來使得它顯示在屏幕上。
當這一切都完成后,窗口開始進入一個while循環(huán)以處理各種消息,直至API函數GetMessage()返回0才退出程序。循環(huán)中,程序需要從主線程的消息隊列中取出各種消息,并將它分發(fā)給系統,然后由Windows系統調用窗口的窗口函數(WndProc),以完成窗口對消息的響應處理。
???????? TApplication除了定義一個應用程序的特性及行為外,另一個重要的使命就是封裝以上的那些令人討厭的、繁瑣的步驟。

2.Application對象的本質
注意:Application是一個0*0大小的不可見窗口!并且這個窗口是windows應用程序的主窗口,delphi應用程序的主窗體是這個窗口的子窗口,因此會以一個消息循環(huán)接受窗口消息并且加以分派和處理。TApplication類封裝了創(chuàng)建秘密窗口和消息循環(huán)的程序代碼。所有的事情發(fā)生在全局對象Application對象被創(chuàng)建之時。
?? TApplication的構造函數中:
constructor?TApplication.Create(AOwner:?TComponent)
var
??
??
if?not?IsLibrary?then?CreateHandle;
??
end;
? 構造函數會調用CreateHandle方法(非常重要的函數)。查看該方法源代碼可知,該方法的任務正是注冊窗口類,并創(chuàng)建一個窗口實例。
procedure?TApplication.CreateHandle;
var
??TempClass:?TWndClass;
??SysMenu:?HMenu;
begin
??
if?not?FHandleCreated?and?not?IsConsole?then
??begin
????FObjectInstance?:
=?Classes.MakeObjectInstance(WndProc);
????
//?如果窗口類不存在,則注冊窗口類
????
if?not?GetClassInfo(HInstance,?WindowClass.lpszClassName,?TempClass)?then
????begin
??????WindowClass.hInstance?:
=?HInstance;
??????
if?Windows.RegisterClass(WindowClass)?=?0?then
????????raise?EOutOfResources.Create(SWindowClass);
????
end;
???
//?創(chuàng)建窗口,長度和寬度都是0,位置在屏幕中央,返回的句柄FHandle
???
//?也就是Tapplication.Handle的值
????FHandle?:
=?CreateWindow(WindowClass.lpszClassName,?PChar(FTitle),
??????WS_POPUP?
or?WS_CAPTION?or?WS_CLIPSIBLINGS?or?WS_SYSMENU
??????
or?WS_MINIMIZEBOX,
??????GetSystemMetrics(SM_CXSCREEN)?div?
2,
??????GetSystemMetrics(SM_CYSCREEN)?div?
2,
??????
0,?0,?0,?0,?HInstance,?nil);
????FTitle?:
=?'';
????FHandleCreated?:=?True;
???
//?調用SetWindowLong設置窗口的窗口函數(WndProc)
????SetWindowLong(FHandle,?GWL_WNDPROC,?Longint(FObjectInstance));
????
if?NewStyleControls?then
????begin
??????SendMessage(FHandle,?WM_SETICON,?
1,?GetIconHandle);
??????SetClassLong(FHandle,?GCL_HICON,?GetIconHandle);
????
end;
????SysMenu?:
=?GetSystemMenu(FHandle,?False);
????DeleteMenu(SysMenu,?SC_MAXIMIZE,?MF_BYCOMMAND);
????DeleteMenu(SysMenu,?SC_SIZE,?MF_BYCOMMAND);
????
if?NewStyleControls?then?DeleteMenu(SysMenu,?SC_MOVE,?MF_BYCOMMAND);
??
end;
end;
對照一下此前使用純API編寫的窗口程序,就會發(fā)現一些它們的相似之處。在CreateHandle()中,可以看到熟悉的RegisterClass()、CreateWindow()等API函數的調用。比較特別的是,CreateHandle()中通過API函數SetWindowLong()來設置窗口的窗口函數:
SetWindowLong(FHandle, GWL_WNDPROC, Longint(FObjectInstance));
此時,SetWindowLong()的第3個參數為窗口函數實例的地址,其中FObjectInstance是由CreateHandle()的第1行代碼
FObjectInstance := Classes.MakeObjectInstance(WndProc);
所創(chuàng)建的實例的指針,而WndProc()則成了真正的窗口函數。
????????? TApplication本身有一個private成員FMainForm,它指向程序員所定義的主窗體,并在TApplication.CreateForm方法中判斷并賦值:
procedure?TApplication.CreateForm(InstanceClass:?TComponentClass;?var?Reference);
var
??Instance:?TComponent;
begin
??Instance?:
=?TComponent(InstanceClass.NewInstance);
??TComponent(Reference)?:
=?Instance;
??try
????Instance.Create(Self);
??except
????TComponent(Reference)?:
=?nil;
????raise;
??
end;
???
//?第一個創(chuàng)建的窗體實例就是MainForm
??
if?(FMainForm?=?nil)?and?(Instance?is?TForm)?then
??begin
????TForm(Instance).HandleNeeded;
????FMainForm?:
=?TForm(Instance);
??
end;
end;
因此,Delphi為每個應用程序自動生成的代碼中就有對CreateForm的調用,如:
Application.CreateForm(TForm1, Form1);
值得注意的是,如果有一系列的多個CreateForm的調用,則第一個調用CreateForm被創(chuàng)建的窗體,就是整個Application的MainForm。

3.TApplication創(chuàng)建主消息循環(huán)
????? 在TApplication的CreateHandle方法中可以看到,SetWindowLong()的調用將TApplication.WndProc設置成了那個0×0大小窗口的窗口函數。
也就是說,在TApplication的構造函數中主要完成了兩件事情:注冊窗口類及窗口函數,創(chuàng)建Application窗口實例。TApplication類的Run方法中:
procedure?TApplication.Run;
begin
??FRunning?:
=?True;
??try
????AddExitProc(DoneApplication);
????
if?FMainForm?<>?nil?then
????begin
??????
case?CmdShow?of
????????SW_SHOWMINNOACTIVE:?FMainForm.FWindowState?:
=?wsMinimized;
????????SW_SHOWMAXIMIZED:?MainForm.WindowState?:
=?wsMaximized;
??????
end;
??????
if?FShowMainForm?then
????????
if?FMainForm.FWindowState?=?wsMinimized?then
??????????Minimize?
else
??????????FMainForm.Visible?:
=?True;
??????repeat
????????try
??????????HandleMessage;
????????except
??????????HandleException(Self);
????????
end;
??????until?Terminated;
????
end;
??finally
????FRunning?:
=?False;
??
end;
end;

是的,這就是主消息循環(huán)。看上去似乎沒有取消息、分發(fā)消息的過程,其實它們都被包含在HandleMessage()方法中了。HandleMessage()方法其實是對ProcessMessage()方法的調用,而在ProcessMessage()中就可以看到取消息、分發(fā)消息的動作了。
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,?0,?0,?0,?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
????????
//?熟悉的分發(fā)消息過程
????????TranslateMessage(Msg);
????????DispatchMessage(Msg);
??????
end;
????
end
????
else
??????
//?如果取到的消息為WM_QUIT,則將Fterminate設為真
???????
//?以通知主消息循環(huán)退出
???????
//?這和WindowDemo程序中判斷GetMessage()函數返回值是否為0等效
???????
//?因為GetMessage()函數取出的消息如果是WM_QUIT,它的返回值為0
??????FTerminate?:
=?True;
??
end;
end;

4.窗口函數(WndProc)處理消息
窗口函數是一個回調函數,它被Windows系統所調用,其參數會被給出消息編號、消息參數等信息,以便進行處理。
典型的窗口函數中會包含一個大的case分支,以處理不同的消息。TApplication.CreateHandle()的代碼時提到過,CreateHandle()將Application窗口的窗口函數設置為WndProc()。那么,現在就來看一下這個WndProc:
procedure?TApplication.WndProc(var?Message:?TMessage);
type?
//?函數內嵌定義的類型,只限函數內部使用
??TInitTestLibrary?
=?function(Size:?DWord;?PAutoClassInfo:?Pointer):?Boolean;?stdcall;

var
??I:?
Integer;
??SaveFocus,?TopWindow:?HWnd;
??InitTestLibrary:?TInitTestLibrary;
??
//?內嵌函數,默認的消息處理
???
//?調用Windows的API函數DefWindowProc
??procedure?Default;
??begin
????
with?Message?do
??????Result?:
=?DefWindowProc(FHandle,?Msg,?WParam,?LParam);
??
end;

??procedure?DrawAppIcon;
??var
????DC:?HDC;
????PS:?TPaintStruct;
??begin
????
with?Message?do
????begin
??????DC?:
=?BeginPaint(FHandle,?PS);
??????DrawIcon(DC,?
0,?0,?GetIconHandle);
??????EndPaint(FHandle,?PS);
????
end;
??
end;

begin
??try
????Message.Result?:
=?0;
????
for?I?:=?0?to?FWindowHooks.Count?-?1?do
??????
if?TWindowHook(FWindowHooks[I]^)(Message)?then?Exit;
????CheckIniChange(Message);
????
with?Message?do
??????
//?開始龐大的case分支,對不同的消息做出不同的處理
??????
case?Msg?of
????????WM_SYSCOMMAND:
??????????
case?WParam?and?$FFF0?of
????????????SC_MINIMIZE:?Minimize;
????????????SC_RESTORE:?Restore;
??????????
else
????????????Default;
??????????
end;
????????WM_CLOSE:
??????????
if?MainForm?<>?nil?then?MainForm.Close;
????????WM_PAINT:
??????????
if?IsIconic(FHandle)?then?DrawAppIcon?else?Default;
????????WM_ERASEBKGND:
??????????begin
????????????Message.Msg?:
=?WM_ICONERASEBKGND;
????????????Default;
??????????
end;
????????WM_QUERYDRAGICON:
??????????Result?:
=?GetIconHandle;
????????WM_SETFOCUS:
??????????begin
????????????PostMessage(FHandle,?CM_ENTER,?
0,?0);
????????????Default;
??????????
end;
????????WM_ACTIVATEAPP:
??????????begin
????????????Default;
????????????FActive?:
=?TWMActivateApp(Message).Active;
????????????
if?TWMActivateApp(Message).Active?then
????????????begin
??????????????RestoreTopMosts;
??????????????PostMessage(FHandle,?CM_ACTIVATE,?
0,?0)
????????????
end
????????????
else
????????????begin
??????????????NormalizeTopMosts;
??????????????PostMessage(FHandle,?CM_DEACTIVATE,?
0,?0);
????????????
end;
??????????
end;
????????WM_ENABLE:
??????????
if?TWMEnable(Message).Enabled?then
??????????begin
????????????RestoreTopMosts;
????????????
if?FWindowList?<>?nil?then
????????????begin
??????????????EnableTaskWindows(FWindowList);
??????????????FWindowList?:
=?nil;
????????????
end;
????????????Default;
??????????
end?else
??????????begin
????????????Default;
????????????
if?FWindowList?=?nil?then
??????????????FWindowList?:
=?DisableTaskWindows(Handle);
????????????NormalizeAllTopMosts;
??????????
end;
????????WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:
??????????Result?:
=?SendMessage(LParam,?CN_BASE?+?Msg,?WParam,?LParam);
????????WM_ENDSESSION:?
if?TWMEndSession(Message).EndSession?then?FTerminate?:=?True;
????????WM_COPYDATA:
??????????
if?(PCopyDataStruct(Message.lParam)^.dwData?=?DWORD($DE534454))?and
????????????(FAllowTesting)?
then
????????????
if?FTestLib?=?0?then
????????????begin
??????????????FTestLib?:
=?SafeLoadLibrary('vcltest3.dll');
??????????????if?FTestLib?<>?0?then
??????????????begin
????????????????Result?:
=?0;
????????????????@InitTestLibrary?:
=?GetProcAddress(FTestLib,?'RegisterAutomation');
????????????????if?@InitTestLibrary?<>?nil?then
??????????????????InitTestLibrary(PCopyDataStruct(Message.lParam)
^.cbData,
????????????????????PCopyDataStruct(Message.lParam)
^.lpData);
??????????????
end
??????????????
else
??????????????begin
????????????????Result?:
=?GetLastError;
????????????????FTestLib?:
=?0;
??????????????
end;
????????????
end
????????????
else
??????????????Result?:
=?0;
????????CM_ACTIONEXECUTE,?CM_ACTIONUPDATE:
??????????Message.Result?:
=?Ord(DispatchAction(Message.Msg,?TBasicAction(Message.LParam)));
????????CM_APPKEYDOWN:
??????????
if?IsShortCut(TWMKey(Message))?then?Result?:=?1;
????????CM_APPSYSCOMMAND:
??????????
if?MainForm?<>?nil?then
????????????
with?MainForm?do
??????????????
if?(Handle?<>?0)?and?IsWindowEnabled(Handle)?and
????????????????IsWindowVisible(Handle)?
then
??????????????begin
????????????????FocusMessages?:
=?False;
????????????????SaveFocus?:
=?GetFocus;
????????????????Windows.SetFocus(Handle);
????????????????Perform(WM_SYSCOMMAND,?WParam,?LParam);
????????????????Windows.SetFocus(SaveFocus);
????????????????FocusMessages?:
=?True;
????????????????Result?:
=?1;
??????????????
end;
????????CM_ACTIVATE:
??????????
if?Assigned(FOnActivate)?then?FOnActivate(Self);
????????CM_DEACTIVATE:
??????????
if?Assigned(FOnDeactivate)?then?FOnDeactivate(Self);
????????CM_ENTER:
??????????
if?not?IsIconic(FHandle)?and?(GetFocus?=?FHandle)?then
??????????begin
????????????TopWindow?:
=?FindTopMostWindow(0);
????????????
if?TopWindow?<>?0?then?Windows.SetFocus(TopWindow);
??????????
end;
????????WM_HELP,???
//?MessageBox(?MB_HELP)
????????CM_INVOKEHELP:?InvokeHelp(WParam,?LParam);
????????CM_WINDOWHOOK:
??????????
if?wParam?=?0?then
????????????HookMainWindow(TWindowHook(Pointer(LParam)
^))?else
????????????UnhookMainWindow(TWindowHook(Pointer(LParam)
^));
????????CM_DIALOGHANDLE:
??????????
if?wParam?=?1?then
????????????Result?:
=?FDialogHandle
??????????
else
????????????FDialogHandle?:
=?lParam;
????????WM_SETTINGCHANGE:
??????????begin
????????????Mouse.SettingChanged(wParam);
????????????SettingChange(TWMSettingChange(Message));
????????????Default;
??????????
end;
????????WM_FONTCHANGE:
??????????begin
????????????Screen.ResetFonts;
????????????Default;
??????????
end;
????????WM_NULL:
??????????CheckSynchronize;??
??????
else
????????Default;
??????
end;
??except
????HandleException(Self);
??
end;
end;
整個WndProc()方法,基本上只包含了一個龐大的case分支,其中給出了每個消息的處理代碼,“WM_”打頭的為Windows定義的窗口消息,“CM_”打頭的為VCL庫自定義的消息。
需要注意的是,這里給出WndProc是屬于TApplication的,也就是那個0×0大小的Application窗口的窗口函數,而每個Form另外都有自己的窗口函數。

轉載于:https://www.cnblogs.com/sideandside/archive/2007/05/09/740309.html

總結

以上是生活随笔為你收集整理的TApplication与主消息循环的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。