1. 程式人生 > >Windows程式和訊息機制(一):視窗程式的建立

Windows程式和訊息機制(一):視窗程式的建立

Windows視窗程式的實現

上面介紹了Windows下的訊息機制,系統傳送訊息到程式,程式接收到訊息後的處理統稱為視窗過程。
要實現視窗過程當然需要先建立一個視窗程式了。視窗程式的建立很簡單,主要分為以下幾個步驟:

  1. 註冊視窗類
  2. 建立視窗及顯示視窗
  3. 建立訊息迴圈
  4. 編寫視窗過程函式
    接下來詳細說下細節。

註冊視窗類

註冊視窗要做的事情主要是設定視窗的屬性和特徵,通過呼叫RegisterClass(&wndclass)完成註冊,wndclass是一個叫作WNDCLASS的結構體,它可以設定視窗的很多屬性,在註冊之前有若干個欄位是必須要賦值的,它的結構體如下:

style:指定視窗的樣式

  • CS_HREDRAW:當視窗的水平寬度發生變化時視窗進行重繪
  • CS_VREDRAW:代表什麼不用說了吧
  • CS_NOCLOSE:沒有關閉按鈕
  • CS_DBLCLKS:可以傳送雙擊訊息
    去掉某個樣式:style = style &~CS_XXXX

lpfnWndProc:視窗過程函式,Windows系統中每個視窗都可以有一個視窗過程函式,它的建立過程如下:

  1. 將視窗過程函式賦值給lpfnWndProc
  2. 註冊視窗類,呼叫RegisterClass函式進行註冊
  3. 主迴圈中呼叫DispatchMessage進行訊息派發
    視窗過程函式的宣告如下:
typedef LRESULT (CALLBACK *WNDPROC)(HWND,UINT,WPARAM,WPARAM);

LRESULT實際上是long型別,CALLBACK實際上是__stdcall,在VC++開發環境中,預設的編譯選項是__cdecl,這種呼叫約定適用引數可變的函式,因為它是在函式外進行棧楨平衡。如果需要使用__stdcall需要指定,Windows API都遵循__stdcall。在Windows NT4.0以後程式中要使用回撥函式必須遵循__stdcall。
LoadIcon:這個欄位用來指定圖示,它的第二個引數用來指定一個資源名,Windows SDK的資源命名規範一般是ID+型別,比如說按鈕就是IDB_XXX。
hbrBackground:當視窗發生重繪時,系統使用這個欄位指定的背景色作為重繪的顏色
貼上一段參考程式碼:

WNDCLASSEXW wcex;

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.cbSize = sizeof(WNDCLASSEXW);
    wcex.lpfnWndProc = (WNDPROC)WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInst;
    wcex.hCursor = LoadCursor(nullptr, MAKEINTRESOURCE(IDI_ZZPEANALYZER));
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ZZPEANALYZER));
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    wcex.lpszMenuName = MAKEINTRESOURCE(IDC_ZZPEANALYZER);
    RegisterClassExW(&wcex);

建立視窗及顯示視窗

建立視窗沒有什麼特別需要講解的地方,下面將要貼出的程式碼說明了一切,不過還是有需要注意的地方。

  1. 當呼叫CreateWindows函式時,傳遞引數hWndParent時,如果指定為WS_CHILD,說明建立的是子視窗,子視窗會被父視窗所影響,具體影響如下:
  2. 呼叫UpdateWindow函式會發送一個WM_PAINT訊息來重新整理視窗
HWND hWnd = CreateWindowEx(WS_EX_ACCEPTFILES,szWindowClass,szTitle,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,700,500,NULL,NULL,hInstance,NULL);
    if (!hWnd)
    {
        return FALSE;
    }
    ShowWindow(hWnd, nShowCmd);
    UpdateWindow(hWnd);

建立訊息迴圈

每個程式都有一個訊息佇列,可以通過GetMessage函式獲取佇列中的訊息。
GetMessage原型如下:

BOOL GetMessage(LPMSG lpMsg,//訊息結構體
                HWND hWnd,//接收指定視窗的訊息
                UINT wMsgFilterMin,//獲取的訊息最小值
                UINT wMsgFilterMax);//獲取的訊息最大值

GetMessage只有在接收到WM_QUIT時才會返回0,如果出現錯誤會返回-1,主迴圈一般如下:

MSG msg;
while (GetMessage(&msg,nullptr,0,0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

GetMessage獲取到訊息佇列中的訊息以後,會執行TranslateMessage(),這個函式是用來將虛擬按鍵轉換成字元的,比如說使用者按下某個按鍵會產生WM_KEYDOWN和WM_KEYUP兩個虛擬鍵程式碼,TranslateMessage()會將這兩個虛擬鍵碼轉為WM_CHAR,並將這個message放到訊息佇列中,當下次呼叫GetMessage時這個訊息會被呼叫,TranslateMessage()並不會修改訊息,只是會新增一個訊息。
DispatchMessage()用來將訊息回傳給系統,系統會呼叫視窗過程的回撥函式。

PeekMessage
和GetMessage()類似的函式還有PeekMessage,這個函式跟GetMessage()唯一的不同是有一個wRemoveMsg欄位,當它是PM_REMOVE的時候在獲取到訊息後會將訊息從訊息佇列中刪除,跟GetMessage一致,當它是PM_NOREMOVE時不會將訊息從訊息佇列中移除。

編寫視窗過程函式

還記得在註冊視窗時繫結的回撥函式嗎?它就是視窗過程函式的入口。

wcex.lpfnWndProc = (WNDPROC)WndProc;

這個回撥函式的宣告如下:

引數在上面的註冊視窗部分講解過,就不贅述了。這個回撥函式內部的實現主要是通過switch的方式來分類不同的訊息處理函式,下面的圖可供參考:

WM_CHAR:當用戶按下鍵盤上的一個字元鍵,這個分支會被呼叫
WM_PAINT:當視窗的一部分或全部變為無效時就會呼叫這個分支,具體有以下幾種情況觸發:

  • 視窗剛建立時
  • 呼叫UpdateWindow時
  • 視窗大小變化時(前提需要註冊視窗時設定了CS_HREDRAW和CS_VREDRAW標誌)
  • 視窗被遮蓋再顯示時
    注意,只有在WM_PAINT分支內部才可以使用BeginPaint(對應EndPaint),在外部只能通過GetDC函式來獲取DC(對應ReleaseDC)。

DC全稱Device Context,它包含了顯示器、圖形裝置驅動器的一些資訊,要在視窗上顯示文字或者顯示圖形都需要用到DC,如果沒有DC,我們就需要了解圖形裝置和它的驅動程式,通過呼叫驅動程式的介面來完成圖形的顯示,而圖形裝置有很多種,每一種都是不一樣的,如果真要去了解這些驅動程式再作畫,那工作量也太大了,因此微軟提供了DC,由它去跟圖形裝置驅動程式打交道,我們只要使用它就可以直接畫圖了

WM_CLOSE:當用戶單擊視窗上的關閉按鈕時,系統會發送一條WM_CLOSE訊息。
DestroyWindow函式會向視窗過程傳送WM_DESTROY訊息,DestroyWindow函式執行完視窗就已經被銷燬了,但是程式沒還沒有退出,因此需要在WM_DESTROY分支裡面進行最後的處理。
如果程式沒有響應WM_CLOSE訊息,系統就會呼叫DefWindowProc函式,這個函式會呼叫DestroyWindow函式來響應這條WM_CLOSE訊息。

WM_DESTROY:在這個分支裡呼叫了PostQuitMessage,它會向訊息佇列中傳送一條WM_QUIT訊息,之前我們說過GetMessage在收到WM_QUIT會返回0,當它返回0時,主迴圈就停止了。傳遞給PostQuitMessage的引數會作為WM_QUIT訊息的wParam引數,這個值通常作為WinMain函式的返回值。

相關推薦

Windows程式訊息機制視窗程式建立

Windows視窗程式的實現 上面介紹了Windows下的訊息機制,系統傳送訊息到程式,程式接收到訊息後的處理統稱為視窗過程。 要實現視窗過程當然需要先建立一個視窗程式了。視窗程式的建立很簡單,主要分為以下幾個步驟: 註冊視窗類建立視窗及顯示視窗建立訊

Windows程式訊息機制訊息與程序間通訊

自定義訊息與程序間通訊 視窗程式可以接收自定義的訊息型別,前提是通訊的程序聲明瞭這種訊息型別,宣告的方法很簡單,WM_USER加一個值就可以了,一般加的值從0x400開始,其他的值已經被系統使用了。 實現一個完整的自定義訊息需要進行以下步驟:

Windows程式訊息機制訊息有關的函式

不同視窗程式可以通過訊息進行互動,主要用到的函式如下: FindWindow 獲取一個視窗的控制代碼。 HWND FindWindow( LPCTSTR lpClassName,// 類名 LPCTSTR lpWindowName//

WPF的訊息機制- 讓應用程式動起來

原文: WPF的訊息機制(一)- 讓應用程式動起來 前言 談起“訊息機制”這個詞,我們都會想到Windows的訊息機制,系統將鍵盤滑鼠的行為包裝成一個Windows Message,然後系統主動將這些Windows Message派發給特定的視窗,實際上訊息是被Post到特定視窗所線上程

WIN32學習——Windows訊息機制

1、Win32視窗程式採用的是事件驅動方式執行,也就是訊息機制,當系統通知視窗工作時,就是採用訊息的方式派發給視窗,通過呼叫視窗處理函式進行對訊息對處理。 2、訊息MessageBox結構體: int MessageBox( HWND hWnd, //父視窗

深入理解android訊息機制——handler Looper原始碼

android 重要核心知識點,怎麼深刻理解都不為過,本篇部落格從常用api ,Looper Hanldery以及HanlderTread原始碼角度解讀 一 常用api,主執行緒接收處理訊息 private Handler handler = ne

Android訊息機制之 ThreadLocal

前言   今天重溫了Android開發藝術探索上的訊息機制,花了一些時間,書上寫很好,但是可能文章一些先後順序問題,不是特別好理解,這篇文章博主用了自己的理解,看原始碼,結合書上的知識,希望大家能更容易理解。(可能會寫的不太好。。。不正確的地方歡迎指出)  

JVM——深入解析原理執行機制類載入過程

       隔了好久終於把這篇文章補上了,最近在看《深入理解Java虛擬機器》,一本很不錯的書,必須值得一看。        由於本人對Java類的載入過程一直是一知半解,所以優先看了一下

JVM——深入解析原理執行機制類載入器

      上次我們說了一下jvm中類載入的過程,大概有載入,連線(驗證,準備,解析),初始化這麼幾個步驟,當然要實現這些功能就需要有載入器,今天我們就來說說jvm中的類載入器。 一、分類

Spring整合Struts2Hibernate+Maven之Maven專案建立

趁著畢設的功夫,寫一些東西。也算是記錄個人平時畢設完成的過程。 建立專案 工具:intellij idea+JDK1.6+Maven 第一步:New Project ->點選左側maven ->勾選Create fromarchetype 後選擇maven-archet

【caffe】在windows平臺中安裝caffe基礎安裝及簡單測試

基礎配置 本文中的配置:win10 + vs2015 + python2.5 + cmake3.12 + git2.15 + CUDA8.0 + cuDNN-8.0-5 在進行windows下的caffe安裝前,一定要把以上的這些軟體安裝好,並加入系統路徑中。

Java架構之訊息佇列 訊息佇列的概述

訊息佇列系列分享大綱:  一、訊息佇列的概述 二、訊息佇列之RabbitMQ的使用 三、訊息佇列之Kafka的使用 四、訊息佇列之RabbitMQ的原理詳解 五、訊息佇列之Kafka的原理詳解 六、訊息佇列之面試集錦 1.訊息佇列的概述 訊息佇列(Me

.Net——快取機制利用Dictionary模擬快取

      在計算機的硬體設計中,有一個被反覆使用的思想——快取。同樣,在軟體設計中,這個思想也可以用來解決資料讀取非常耗時帶來的效能問題(當然,在時間和空間上,我們要尋找一個平衡點)。    

Rxjava2原始碼分析Flowable的建立基本使用過程分析

本文用於記錄一下自己學習Rxjava2原始碼的過程。首先是最簡單的一個使用方法(未做執行緒切換),用來學習Flowable的建立和使用。Flowable .create(new FlowableOnSubscribe<Object>() {

如何有效的區分小公司創業公司小公司的特點

為什麼要寫這篇文章 現在有很多的“小公司”假扮成“創業公司”。因為大家一直有一種錯誤的認知“創業公司就是小公司”,所以,很容易被這樣的偽裝公司欺騙。等到了這樣的小公司以後才發現,這TM哪是創業公司啊,分明就是小公司。本文的主要目的是跟大家分享一些我對於小公司和

FFmpegSDL教程製作螢幕截圖

概觀電影檔案有幾個基本元件。首先,檔案本身被稱為容器,容器的型別決定了檔案中資訊的位置。容器的例子是AVI和Quicktime。接下來,你有一堆流;例如,你通常有一個音訊流和一個視訊流。 (“流”只是“隨著時間的推移可用的一系列資料元素”的流行詞)。流中的資料元素稱為幀。每個

Java多執行緒記憶體模型程序執行緒基礎

Java多執行緒和記憶體模型(一) 由於java是執行在 JVM上 的,所以需要涉及到 JVM 的記憶體模型概念,需要理解記憶體模型,就需要多執行緒的基礎; 而執行緒是基於載體執行緒裡的,所以我們藉由作業系統的程序來講一講。 程序 什麼是程序?

微信小程式基礎入門程式介面介紹&創造自己的第一個小程式

承接小程式配置之後 ,先對小程式開發軟體的介面進行介紹: 然後進入第一個小程式的構造:獲取使用者登陸資訊 通過左上角的 “+”新增頁面 然後建立第一個功能頁面 然後頁面構造完成:各部分程式碼: app.

java虛擬機器記憶體管理機制JVM記憶體管理總結【分享】

近期看了看Java記憶體洩露的一些案例,跟原來的幾個哥們討論了一下,深入研究發現JVM裡面還是有不少以前不知道的細節,這裡稍微剖析一下。先看一看JVM的內部結構——如圖所示,JVM主要包括兩個子系統和兩個元件。兩個子系統分別是Class loader子系統和Execution

springboot用eclipse建立簡單的springboot專案

1.安裝sts外掛。    2.安裝成功之後便可以NEW 一個springboot專案。 3.點選Finish便生成如下專案: 這裡,SpringbootApplicatio是啟動類,static存放靜態資源,templates存放模版,appli