1 了解窗口
2 窗口程序原理
事件驱动模式
窗口程序的运行过程
DispatchMessage会在内部调用消息处理的回调函数
应用程序之间也可互相发送消息
PostMessage 将一个消息放到其他程序的消息队列中
SendMessage 越过消息队列直接调用目标程序的窗口过程
RegisterClassEx 用来注册窗口过程 即用来注册处理窗口消息的回调函数
2 分析窗口程序
2.1 模块与句柄
exe dll装入内存后称为模块,每个模块都有一个唯一标识的句柄,在win32中,模块句柄在数值上等于内存中装入的起始地址,用来标识各种资源
使用GetModuleHandle来获取模块句柄
例子
当参数为NULL,会获取调用者本模块的句柄
在win32中实例句柄就是模块句柄
hInstance hModule
在C语言编程中 hInstance通过WinMain由系统传入
在win32汇编中,hInstance需要自己获取
windows中几乎所有的东西都是用句柄标识的,如文件句柄、窗口句柄、线程句柄、模块句柄等
2.2 创建窗口
窗口类中定义窗口的属性
初始化
hIcon 窗口图标句柄 预定义 可通过LoadIcon指定
hCursor 光标句柄 预定义(IDC_ARROW...) 可通过LoadCursor指定
lpszMenuName 菜单字符串
hInstance 指定窗口类属于哪个模块 GetModuleHandle
cbSize 指定结构的长度
style 窗口风格 CS_HREDRAW CS_VREDRAW CS_DBLCLKS
hbrBackground 刷子句柄 指定窗口客户区的背景色 如BLACK_BRUSH、WHITE_BRUSH 使用 GetStockObject来获取刷子句柄 也可以指定颜色值 如 COLOR_BACKGROUND,COLOR_HIGHLIGHT,COLOR_MENU,COLOR_WINDOW等,需要加1
lpszClassName 指定类的名称
cbWndExtra cbClsExtra 预留的空间 用来存放自定义数据
lpfnWndProc 回调函数
使用CreateWindowEx来建立窗口
HWND CreateWindowEx(
DWORD dwExStyle, // extended window style WS_EX_* 可参考MSDN
LPCTSTR lpClassName, // pointer to registered class name
LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle, // window style 可参考MSDN WS_*
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window 上级窗口,上级窗口销毁时,下级窗口也会被销毁
HMENU hMenu, // handle to menu, or child-window identifier 菜单句柄 可替换窗口类中的菜单 当dwStyle为WS_CHILD时,表示子窗口的ID
HINSTANCE hInstance, // handle to application instance 模块句柄
LPVOID lpParam // pointer to window-creation data 传给窗口的参数 这个参数在WM_CREATE消息中可以被获取
);
常见的窗口样式
ShowWindow用来显示窗口
BOOL ShowWindow(
HWND hWnd, // handle of window
int nCmdShow // show state of window
);
UpdateWindow绘制客户区 向窗口发送了一条WM_PAINT消息
BOOL UpdateWindow(
HWND hWnd // handle of window
);
CreateWindowEx也可以创建子窗口 如Button Edit
创建一个button
注意 hMenu在这里表示子窗口的ID
2.3 消息循环
消息循环的一般形式
消息格式
lpMsg为指向MSG结构体的指针,接收返回消息
若获取的消息WM_QUIT eax中的返回值为0
TranslateMessage进行一些键盘消息的转换,将键盘消息的扫描码转换成ASC码在消息队列中插入WM_CHAR WM_SYSCHAR消息 遇到非键盘消息不处理
DispathMessage将消息发送到窗口处理过程,窗口过程返回后DispathMessage才返回
其它形式的消息循环
PeekMessage
BOOL PeekMessage(
LPMSG lpMsg, // pointer to structure for message
HWND hWnd, // handle to window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax, // last message
UINT wRemoveMsg // removal flags PM_NOREMOVE 取一条消息后在消息队列中删除该消息 PM_REMOVE 取一条消息不删除
);
无论应用程序消息队列是否有消息,PeekMessage函数都立即返回,程序得以继续执行后面的语句(无消息则执行其它指令,有消息时一般要将消息派发出去,再执行其它指令)。
GetMessage函数只有在消息队列中有消息时返回,队列中无消息就会一直等,直至下一个消息出现时才返回。在等的这段时间,应用程序不能执行任何指令。
2.4 窗口过程
窗口过程的一般结构
注意 proc后面的uses伪操作在子程序进入和退出时自动插上push和pop寄存器指令,来保护这些寄存器的值
uMsg的windows标准窗口预定义的值的范围为0-03ffh
用户自定义的消息可以从04ooh开始 一般为WM_USER+1,WM_USER+2,...
wParam和lParam为消息附带的参数,不同消息不一样
当窗口决定关闭时,需要程序调用DestoryWindow来摧毁窗口,并用PostQuitMessage向消息循环发送WM_QUIT消息来退出消息循环。
DefWindowProc来处理用户不管的消息,采取默认方式来处理窗口消息
附录 B实验
3 窗口间的通信
3.1 窗口间消息的互发
不同的应用程序之间可以使用SendMessage和PostMessage互相发送消息
LRESULT SendMessage(
HWND hWnd, // handle of destination window
UINT Msg, // message to send
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
BOOL PostMessage(
HWND hWnd, // handle of destination window
UINT Msg, // message to post
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
对于不同的msg, wParam和lParam的含义是不同的
wsprintf win32 api中用来格式化字符串输出的函数
windows在处理SendMessage时 传递字符串时使用了共享内容
注意 在用户自定义的消息中,不要在消息参数中传递指针 会引发非法访问内存
3.2 在窗口间传递数据
WM_COPYDATA消息用于在不同应用间传递数据
WM_COPYDATA
wParam = (WPARAM) (HWND) hwnd; // handle of sending window
lParam = (LPARAM) (PCOPYDATASTRUCT) pcds; // pointer to structure with data
数据包含在COPYDATASTRUCT结构体中
typedef struct tagCOPYDATASTRUCT { // cds
DWORD dwData; //Specifies up to 32 bits of data to be passed to the receiving application
DWORD cbData; //Specifies the size, in bytes, of the data pointed to by the lpData member.
PVOID lpData; //Points to data to be passed to the receiving application. This member can be NULL.
} COPYDATASTRUCT;
3.3 SendMessage和PostMessage的区别
SendMessage相当于直接调用其他窗口的窗口过程来处理某个消息,并等待窗口过程处理完成后返回
PostMessage则将某个消息放入目标窗口的消息队列中并直接返回,不能用于任何参数中用到指针的消息