WinForm Framework中用到的win32 API
窗体基本使用的API
CreateWindowExW
HWND CreateWindowExA( [in] DWORD dwExStyle, [in, optional] LPCSTR lpClassName, [in, optional] LPCSTR lpWindowName, [in] DWORD dwStyle, [in] int X, [in] int Y, [in] int nWidth, [in] int nHeight, [in, optional] HWND hWndParent, [in, optional] HMENU hMenu, [in, optional] HINSTANCE hInstance, [in, optional] LPVOID lpParam );
- dwExStyle
- 扩展的一些窗体样式,多个枚举值,可以添加到一起
- lpClassName
- 窗体的类名,窗体类必须已经注册完毕
- lpWindowName
- 窗体名称,有些窗体会把名称绘制出来。例如checkbox button会把名称绘制在自己的区域内
- dwStyle
- 窗体样式,通常包括显示边框、标题栏、调整最小化和最大化状态、是否禁用、是否可见、水平垂直滚动条、是否可以拉伸
- X
- Y
- nWidth
- nHeight
- 窗体位置、大小,定位时如果自己是顶级窗体根据屏幕左上角定位,如果是子窗体根据父窗体左上角定位
- hWndParent
- 父窗体的句柄
- hMenu
- hInstance
- lpParam
调用后,会发送WM_NCCREATE, WM_NCCALCSIZE, WM_CREATE 这几个消息。
调用成功返回新窗体的句柄,调用失败返回NULL
ShowWindow
BOOL ShowWindow( [in] HWND hWnd, [in] int nCmdShow );
FindWindowW
HWND FindWindowW( [in, optional] LPCWSTR lpClassName, [in, optional] LPCWSTR lpWindowName );
SetActiveWindow
激活某个窗体,z-order设置成最大
HWND SetActiveWindow( [in] HWND hWnd );
SetParent
SetFocus
SetScrollPos
SetCursor
SetCapture
ReleaseCapture
EnumWindows
EnableWindow
RegisterClassW
DestroyWindow
BOOL DestroyWindow( [in] HWND hWnd );
销毁窗体,发送WM_DESTROY, WM_NCDESTROY消息
BeginPaint
HDC BeginPaint( [in] HWND hWnd, [out] LPPAINTSTRUCT lpPaint );
EndPaint
BOOL EndPaint( [in] HWND hWnd, [in] const PAINTSTRUCT *lpPaint );
第一个参数是要绘制的窗体,第二个是绘制的信息。
注意调用BeginPaint,就必须调用EndPaint。
BeginPaint调用成功的话,返回display device context,用来绘制客户端区域。调用失败则返回NULL
EndPaint结束绘制请求,并且释放掉device context。
DrawTextW
FillRect
CopyImage
SetWindowTextW
InvalidateRect
具体案例
这是一个具体的例子,窗体处理过程处理了WM_PAINT消息,绘制文本
代码的具体逻辑是,首先创建一个窗体,然后显示窗体。显示窗体时,窗体会接收到WM_PAINT消息,在这个消息的处理函数中,调用API绘制了一段文本
注意到绘制文本的方法,需要BeginPaint返回的device context作为参数传递给TextOut函数
LRESULT APIENTRY WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_PAINT: hdc = BeginPaint(hwnd, &ps); TextOut(hdc, 0, 0, "Hello, Windows!", 15); EndPaint(hwnd, &ps); return 0L; // Process other messages. } } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hwnd; hwnd = CreateWindowEx( // parameters ); ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd); return msg.wParam; }
窗体消息API
CallWindowProcW
LRESULT CallWindowProcW( [in] WNDPROC lpPrevWndFunc, [in] HWND hWnd, [in] UINT Msg, [in] WPARAM wParam, [in] LPARAM lParam );
SetWindowLongPtrW
LONG SetWindowLongA( [in] HWND hWnd, [in] int nIndex, [in] LONG dwNewLong );
通常境况下,我们一般是写一个窗体过程的方法,然后在注册一个窗体类的时候,把窗体过程作为参数,传递进去。
这样根据这个窗体类所创建出来的窗体,自动关联到这个窗体过程方法,消息会传递到方法里
LRESULT CALLBACK MainWndProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam) // second message parameter { switch (uMsg) { case WM_CREATE: // Initialize the window. return 0; case WM_PAINT: // Paint the window's client area. return 0; case WM_SIZE: // Set the size and position of the window. return 0; case WM_DESTROY: // Clean up window-specific data objects. return 0; // // Process other messages. // default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; }
这里关联起来
int APIENTRY WinMain( HINSTANCE hinstance, // handle to current instance HINSTANCE hinstPrev, // handle to previous instance LPSTR lpCmdLine, // address of command-line string int nCmdShow) // show-window type { WNDCLASS wc; // Register the main window class. wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hinstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MainMenu"; wc.lpszClassName = "MainWindowClass"; if (!RegisterClass(&wc)) return FALSE; // // Process other messages. // }
但是可以创建subclass,将窗体过程拦截下来,重定向到另外一个窗体过程,处理完消息后,再发送给原始窗体过程函数。
特别注意到 SetWindowLong的返回值是原先的窗体过程,处理完之后是要把消息重新发给原先的处理过程的
WNDPROC wpOrigEditProc; LRESULT APIENTRY EditBoxProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HWND hwndEdit; switch(uMsg) { case WM_INITDIALOG: // Retrieve the handle to the edit control. hwndEdit = GetDlgItem(hwndDlg, ID_EDIT); // Subclass the edit control. wpOrigEditProc = (WNDPROC) SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG) EditSubclassProc); // // Continue the initialization procedure. // return TRUE; case WM_DESTROY: // Remove the subclass from the edit control. SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG) wpOrigEditProc); // // Continue the cleanup procedure. // break; } return FALSE; UNREFERENCED_PARAMETER(lParam); } // Subclass procedure LRESULT APIENTRY EditSubclassProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_GETDLGCODE) return DLGC_WANTALLKEYS; return CallWindowProc(wpOrigEditProc, hwnd, uMsg, wParam, lParam); }
消息处理方法里头的A版本就是用ANSI编码,一般指的是中文GB2312,日文Shift JIS 等不同语言编码。
U版本指的是Unicode版本编码。
- 从消息队列中取消息,Peek取不到符合条件消息立即返回,Get则会一直等待消息到来
- PeekMessageA
- PeekMessageW
- GetMessageA
- GetMessageW
- 转变消息,仅仅知道按下哪个键不够,还需要知道按键的字符是啥,需要用Translate
- TranslateMessage
- 发送消息
- SendMessageW,可以给操作系统中任何一个窗体发消息,发送完等待消息处理完毕
- DispatchMessageA/DispatchMessageW,把消息调度给具体窗体的窗体处理过程函数
- PostMessageW,给某个窗体发送消息,直接放置在消息队列的末端
- SendInput,合成一些键盘、鼠标消息,模拟用户行为
- 注册消息
- RegisterWindowMessageW
- 当一个窗体类内部发送消息时,可是使用 0x0400 ~ 0x7FFF 任意值,但是如果在不同application之间发送消息的时候
- 就需要先注册消息,注册成功后才能发送
- 消息钩子
- SetWindowsHookExW,安装应用程序消息钩子,可以在消息传递给特定窗体之前或者之后,通过钩子函数获取消息
- UnhookWindowsHookEx,删除挂钩函数
具体案例,一个简单的消息循环函数
MSG msg; BOOL bRet; while (( bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0) { if (bRet == -1); { // handle the error and possibly exit } else { if (TranslateAccelerator(hwndMain, haccl, &msg) == 0) { TranslateMessage(&msg); DispatchMessage(&msg); } } }