win32程序之窗口程序,以及消息機制


        win32程序值窗口程序,以及消息機制

一丶簡介

  通過上一講.我們了解了窗口其實是繪制出來的.而且是不斷繪制的過程. 所以窗口的本質是繪制. 但是我們現在看到的窗口程序.都可以點擊關閉按鈕. 使用鼠標點擊會有反應.

而我們要怎么實現那. 

  其實鼠標點擊是產生了一個消息.  window把這個消息封裝成了消息結構體. 發送給了我們的窗口程序.  那么windows怎么知道你點擊的那個窗口那?

是這樣的. 當我們點擊的時候. 會記錄點擊坐標.消息.等等. windows系統會接受到. 然后遍歷內核中的WINOBJ結構. 而這個結構中存儲着窗口對象. 窗口對象對應着消息線程.

所以windows一層一層的遍歷.則找到了對應的窗口以及窗口對應的線程.然后發送給我們的應用程序. 

上面說的我們需要了解. 要知道消息怎么產生的. 怎么傳遞的.那么下面編程就明白了.

例如下圖:

 

每個應用程序都有一個線程對象. 而這個線程對象如果創建窗口.那么內核中就有這個窗口對象.

如果我們有鼠標點擊的消息.鍵盤消息等等.操作系統都會遍歷窗口對象. 而窗口對象也會保存着創建這個窗口對象對應的線程對象. 而這個線程對象中則有消息隊列.

這樣的話操作系統則會封裝消息發送給我們窗口對象.

二丶Wind窗口類結構.創建窗口程序.

1.進行窗口編程需要注意的問題

  在Windows中進行窗口編程.入口點已經改成WinMain了. 有四個參數.

如以下代碼所示

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,               //窗口的實例句柄 hinstance代表模塊意思 HWND代表窗口意思. HANDLE代表內核對象. HDC 設備上下文.
                     _In_opt_ HINSTANCE hPrevInstance,        //父窗口句柄.不需要.
                     _In_ LPWSTR    lpCmdLine,                //命令行參數
                     _In_ int       nCmdShow)                 //命令. 最大化命令.還是最小化命令.
{
  
    return 0;
}
wWinMain  因為有UNICODE跟ASCII區別. 所以我是UNICODE使用wWinMain. A版本就是用WinMain

2.進行Windows編程的調試手法

  在Windows中我們調試程序不能簡單的使用printf進行調試.或者打印輸出了. 我們可以使用兩個API進行操作.

1.Sprintf() 格式化字符串. 

2.OutPutDebugString() 輸出調試字符串.

具體兩個API. 不再累贅.百度搜索即可.

因為OutPutDebugString() 只能打印固定字符串.所以使用sprintf進行格式化字符串.如下面代碼.

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
  
    TCHAR str[30] = { NULL };
    wsprintf(str,TEXT( "%s"),TEXT("HelloWin32"));      //因為是Unicode所以使用W版本.
    OutputDebugStringW(str);
    return 0;
}

我們編譯出程序之后.可以使用DebugView這款工具查看.

 

3.窗口編程的步驟

1.創建窗口類. windows提供的窗口樣式.我們需要給定.

2.注冊窗口類.創建了窗口我們需要注冊到windows系統中.

3.創建窗口.如果注冊窗口成功.那么我們需要創建出來這個窗口.並且顯示跟更新.

4.消息處理

4.窗口編程需要的主要結構

  窗口的創建Windows已經為我們提供了. 這個結構就是WNDCLASSEXW 結構

看下這個結構中的內容吧

typedef struct _WNDCLASSEX { 
    UINT       cbSize;                   擴展的大小. 自己WndClass本身大小.
    UINT       style;                    風格
    WNDPROC    lpfnWndProc;              窗口回調.消息都要進入這個回調
    int        cbClsExtra;              
    int        cbWndExtra;  
    HINSTANCE  hInstance;                實例句柄
    HICON      hIcon;                    圖標
    HCURSOR    hCursor;                  光標
    HBRUSH     hbrBackground;            背景
    LPCTSTR    lpszMenuName;             菜單名稱
    LPCTSTR    lpszClassName;            類名稱
    HICON      hIconSm;                  最小化圖標
} WNDCLASSEX, *PWNDCLASSEX; 

這個結構就是說.你的窗口是什么樣式. 大小.是否有圖標. 消息處理函數在哪里等等.需要我們給指定.

 5.完整代碼.

// WindoS.cpp : 定義應用程序的入口點。
//

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include "WindoS.h"

#define MAX_LOADSTRING 100

// 全局變量: 
HINSTANCE hInst;                                // 當前實例
WCHAR szTitle[MAX_LOADSTRING]  = TEXT("第一個我的窗口");                  // 標題欄文本
WCHAR szWindowClass[MAX_LOADSTRING] = TEXT("MyWindow");            // 主窗口類名

// 此代碼模塊中包含的函數的前向聲明: 

LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);


int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    //1.自定義窗口樣式
    WNDCLASS wcex;


    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;                          //設置回調
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;                         //設置模塊實例句柄
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOS));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);    //設置背景顏色
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOS);
    wcex.lpszClassName = szWindowClass;                //設置類名
    
  //上面主要就是4個參數有用. 回調函數 背景顏色 模塊句柄. 類名
//2.注冊窗口類 BOOL bRet = RegisterClass(&wcex); //A RegisterClass U RegisterClassW 擴展 RegisterClassExA /ExW if (bRet == FALSE) { return 0; } //3.創建窗口 並且顯示跟更新窗口 HWND hWnd = CreateWindowW( szWindowClass, //我們的類名 szTitle, //我們自定義的窗口名稱 WS_OVERLAPPEDWINDOW, //窗口的創建樣式 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, //實例句柄 nullptr); //上面重要的也就是4個參數.其余參數查詢下MSDN. if (!hWnd) { return FALSE; } ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); //4.消息循環. MSG msg; /* 1參數是消息結構體.操作系統會往里面填寫消息. 2 參數窗口句柄 因為每個線程可以有多個窗口.表示我要取那個窗口的消息 3.4 參數表示我要取這個窗口的那個消息. 后面三個參數屬於過濾條件 */ while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { if (bRet == -1) { // handle the error and possibly exit } else { TranslateMessage(&msg); //鍵盤消息轉換為小寫. DispatchMessage(&msg); //分發消息.將我們的消息傳遞給我們的回調函數處理. 重要函數.此消息會將Windows的消息.發送給我們 定義窗口類的時候給的回調函數.這樣我們就可以根據消息執行我們代碼了. } } return 0; } // // 函數: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: 處理主窗口的消息。 // // WM_COMMAND - 處理應用程序菜單 // WM_PAINT - 繪制主窗口 // WM_DESTROY - 發送退出消息並返回 // // 我們的窗口回調. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: //菜單消息類型 { int wmId = LOWORD(wParam); //取低兩位為菜單ID.根據菜單ID可以進行操作我們的窗口 // 分析菜單選擇: switch (wmId) { case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); //如果不處理.則必須調用這個函數教給默認的窗口回調處理 } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: 在此處添加使用 hdc 的任何繪圖代碼... LineTo(hdc, 0, 100); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }

創建完畢的結果

三丶消息類型

我們回調中有我們的消息類型.我們可以判斷消息類型進行我們不同的操作.

比如菜單消息.

WM_COMMAND.  如果是這個消息.那么回調函數的 wparam等附加信息就是WM_COMMAND的附加消息了. 我們可以取低位得出操作的菜單ID.進而進行消息處理.

 

WM_PAINT  這個消息是繪制的消息.我們知道.窗口是不斷繪制的.所以繪制消息會一直來.

WM_DESTROY  窗口關閉消息. 如果接受到這個消息.則調用API往消息隊列中(MSG)中傳遞退出消息. 此時外層主線程就會結束.

具體API:

  postQuitMessage(0);

當前具體的消息還要查詢MSDN. 因為消息種類很多.

windows消息都是WM開頭的.

比如查詢WM_COMMAND消息

可以清楚的看到.她會告訴你如果是WM_COMMAND消息來了.那么回調函數的參數.分別代表的是什么意思.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM