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); 
        } 
    } 
}

 

posted @ 2022-09-25 10:53  内心澎湃的水晶侠  阅读(199)  评论(0编辑  收藏  举报