windows编程(窗口与消息)
窗口是屏幕上的矩形区域,消息窗口功能有限,因为我们不能添加四个以上的按钮以及菜单等,而且添加的按钮必须是windows提供的按钮,不能自定义。所以我们有必要自己创建一个多功能可自定义的窗口。
自己的窗口
创建窗口最重要的函数是CreateWindow,它可以创建重叠式窗口,弹出式窗口,子窗口等。而且可以自定义各种功能。
HWND CreateWindow( LPCTSTR lpClassName, //窗口类 LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); |
从函数原型可以看出创建窗口的各种属性,一种窗口含有显示程序名称的标题列、菜单甚至可能还有工具列和滚动条,另一种窗口是对话框,在上面可以放置各种控件。而这些控件也是窗口,称为子窗口。
窗口消息处理程序
窗口处理函数是理解消息机制的关键,windows处理将消息循环中取得的各种消息放入这个函数中进行处理,该函数会比对各个匹配项直到找到对应的处理项,如果没有找到就传给一个默认的窗口函数,是程序正常结束。该函数可以在程序中也可以在dll中。使用窗口类使多个窗口能够属于同一个窗口类,并使用同一个窗口消息处理函数。
HELLOWIN程序
//=========================== // (c)狗尾草 2008.1.19 //=========================== #include<windows.h> LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow) { static TCHAR szAppName[]="HelloWin"; HWND hWnd; MSG msg; WNDCLASS wndclass; wndclass.cbClsExtra=0; wndclass.cbWndExtra=0; wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); wndclass.hInstance=hInstance; wndclass.lpfnWndProc=WndProc; wndclass.lpszClassName=szAppName; wndclass.lpszMenuName=NULL; wndclass.style=CS_HREDRAW|CS_VREDRAW; if(!RegisterClass(&wndclass)) { MessageBox(NULL,"This program requires windows nt!",szAppName,MB_ICONERROR); return 0; } hWnd=CreateWindow(szAppName, "The Hello Program!", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hWnd,iCmdShow); UpdateWindow(hWnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; switch(message) { case WM_CREATE: return 0; case WM_PAINT: hdc=BeginPaint(hWnd,&ps); GetClientRect(hWnd,&rect); DrawText(hdc,"Hello,WindowsXP!", -1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER); EndPaint(hWnd,&ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd,message,wParam,lParam); } |
调用的函数和功能如下,绝大多数在winuser.h:
LoadIcon |
加载图标供程序使用 |
LoadCursor |
加载鼠标光标供程序使用 |
GetStockObject |
取得一个图形对象 |
RegisterClass |
为程序窗口注册窗口类别 |
MessageBox |
显示消息框 |
CreateWindow |
根据窗口类别建立一个窗口 |
ShowWindow |
在屏幕上显示窗口 |
UpdateWindow |
指示窗口自我更新 |
GetMessage |
从消息队列中取得消息 |
TranslateMessage |
转译某些键盘消息 |
DispatchMessage |
将消息发送给窗口消息处理程序 |
PlaySound |
播放一个声音文件 |
BeginPaint |
开始绘制窗口 |
GetClientRect |
取得窗口显示区域的大小 |
DrawText |
显示字符串 |
EndPaint |
结束绘制窗口 |
PostQuitMessage |
在消息队列中插入一个「退出程序」消息 |
DefWindowProc |
执行内定的消息处理 |
关于一些常量定义的约定如下:
前缀 |
类别 |
CS |
窗口类别样式 |
CW |
建立窗口 |
DT |
绘制文字 |
IDI |
图示ID |
IDC |
游标ID |
MB |
消息框 |
SND |
声音 |
WM |
窗口消息 |
WS |
窗口样式 |
常数值是没有必要记忆的,记住常量的一些大写标识符就可以了。
各种句柄
标识符 |
含义 |
HINSTANCE |
执行实体(程序自身)句柄 |
HWND |
窗口句柄 |
HDC |
设备内容句柄 |
句柄通常是调用函数取得的,起到一个标识身份的目的。其实实质是一个32位整数。
数据结构注解
窗口类结构
typedef struct { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX, *PWNDCLASSEX; |
窗口是根据窗口类建立的,窗口类相当于一个模板。他定义了各种属性,定义后打造出来的窗口就是他定义的那个样子了。如果窗口类没有定义完全,注册的时候就会失败,我在写的过程中漏写了几个属性导致这样的错误
该消息是在注册的时候发生的。
Msg数据结构
ttypedef struct tagMSG { HWND hwnd ; UINT message ; WPARAM wParam ; LPARAM lParam ; DWORD time ; POINT pt ; } MSG, * PMSG ; |
从消息队列中取得的消息来填充各个属性。
Point数据结构
typedef struct tagPOINT { LONG x ; LONG y ; } POINT, * PPOINT |
存放该消息时的鼠标坐标。
建立窗口
HWND CreateWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); |
其中的大部分参数都可以猜出意思,最后一个参数有必要解释一下,是用来传递额外的数据的,这里并没有用到就设为NULL了。该函数返回一个窗口句柄,用来唯一表示一个窗口的。这样其他函数才知道作用在哪个窗口上。
建立窗口后调用ShowWindow (hwnd, iCmdShow)来显示窗口,第二个参数是初始的显示方式。UpdateWindow (hwnd)是用来重画窗口的。
消息循环
while(GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } |
消息循环功能是将事件转换为消息并给处理函数处理。TranslateMessage用来解释消息,DispatchMessage用来投递给消息处理函数,非常神奇的一个函数。
WM_PAINT消息
显示区域无效时才产生WM_PAINT消息,遮盖后重新要求显示,此时为无效。当然改变窗口大小也为无效。
关于程序的结束
当WM_DESTROY消息收到后执行PostQuiteMessage产生WM_QUITE消息,这样该消息GetMessage()时返回的是0,导致消息循环结束。WinMain函数继续执行,这个例子中是返回并结束程序。从这里我可以想象到,windows程序主要是消息循环和消息处理函数的交互。除此之外的都是一些设置和初始化等事务性的例程。
汇编版本
;========================== ; (c)狗尾草 2008.1.19 ;========================== .386 .Model Flat, StdCall Option Casemap :None Include windows.inc Include user32.inc Include kernel32.inc Include gdi32.inc includelib gdi32.lib IncludeLib user32.lib IncludeLib kernel32.lib include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.DATA szClassName db "HELLOWIN",0
.DATA? hInstance dd ?
.CODE START: invoke GetModuleHandle,NULL ;取得当前进程实例 mov hInstance,eax invoke WinMain,hInstance,NULL,NULL,SW_SHOWDEFAULT invoke ExitProcess,0 WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD LOCAL wc :WNDCLASSEX LOCAL msg :MSG local hWnd :HWND
mov wc.cbSize,sizeof WNDCLASSEX mov wc.style,CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW mov wc.lpfnWndProc,offset WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst ;存储器变量用push和pop完成赋值 pop wc.hInstance mov wc.hbrBackground,COLOR_BTNFACE+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,offset szClassName invoke LoadIcon,hInst,100 mov wc.hIcon,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax mov wc.hIconSm,0 invoke RegisterClassEx, ADDR wc invoke CreateWindowEx,NULL,ADDR szClassName,CTXT("henry's hellowin program"),WS_OVERLAPPEDWINDOW,200,200,400,200,NULL,NULL,hInst,NULL mov hWnd,eax invoke ShowWindow,hWnd,SW_SHOWNORMAL invoke UpdateWindow,hWnd
StartLoop: invoke GetMessage,ADDR msg,NULL,0,0 cmp eax, 0 je ExitLoop invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg jmp StartLoop ExitLoop:
mov eax,msg.wParam ret WinMain endp WndProc proc hWin:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD LOCAL hdc:HDC LOCAL ps:PAINTSTRUCT LOCAL rect:RECT .if uMsg==WM_CREATE
.elseif uMsg == WM_DESTROY invoke PostQuitMessage,NULL .elseif uMsg == WM_PAINT invoke BeginPaint,hWin,ADDR ps mov hdc,eax invoke GetClientRect,hWin,ADDR rect invoke DrawText,hdc,CTXT("HELLO,WINDOWS XP!"),-1,ADDR rect,DT_SINGLELINE or DT_CENTER or DT_VCENTER invoke EndPaint,hWin,ADDR ps .else invoke DefWindowProc,hWin,uMsg,wParam,lParam .endif ret WndProc endp END START |
汇编版本基本结构同C版,主要更注重细节方面。程序实例句柄由一个特殊的函数取得GetModuleHandle。函数符合先声明后使用的特点。这点与严格的c语言是相同的。各种高级的伪指令的使用,可以说和C语言已经是哥哥弟弟了。程序运行的结果和C版没任何区别