JBTALKS.CC

标题: 隨堂筆記 - C++ API 創建窗口 [打印本页]

作者: Super-Tomato    时间: 2008-12-16 09:06 AM
标题: 隨堂筆記 - C++ API 創建窗口
作者 : Super-Tomato


要用純 API 創建一個 GUI 其實一點都不難, 因為微軟已經定義好了這個結構, 只要根據結構填入自己想創建的方式即可. 聽起來好像很簡單, 但無須懷疑就是這麼簡單. 首先我們需要的必備工具是個 C++ 編輯器, 為了免費且快速的話, 當然就是選擇 Bloodshed Dev-C++Code::Blocks. 想跟著做又沒有 C++ 編輯器的朋友還在看甚麼就快去下載安裝吧. 再來所必備的就是一本 WINAPI 手冊, 沒有的話可以隨時到 MSDN 去查詢, 那麼接著就開始囉



在此我以 Code::Blocks 為例, 打開所安裝好的編輯器, 然後選擇 File -> New -> Empty Project, 之後都是下一步和填入該 Project 名稱即可. 預設的編譯器是 GCC, 如果電腦中還有其他符合的編譯器, 可以到 Settings -> Compiler & debugger 設定中去偵測.


首先我們先在編輯器建立一個 Empty File(Ctrl + Shift + N) 並定義檔名為 WinMain.cpp, 再來輸入內容

#include <windows.h> //載入必要的 windows 頭文件

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
}


WinMain 是 Windows 最主要的一個入口函數, 所有的編程也就從這裡開始, 而這個函數所自帶的 4 個參數請自行參考 WINAPI 手冊, 在這裡要提的是第二個參數, 這個參數主要是傳遞是否有相同的窗口已經被註冊了, 但在 NT 下已經沒再使用了, 所以所接收到的 HINSTANCE 值都是 NULL, 所以也不用去理會. 接下來我們可以先測試看看編譯器是否成功通過編譯, 以下是個消息測試

#include <windows.h> //載入必要的 windows 頭文件

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MessageBox(NULL, "Hello World!", "WinAPI Lesson1", MB_OK);
}


接下來我們按 F9 進行編譯與執行測試, 成功通過的話會看到一個消息框出現, 但怎麼會有個 DOS 的窗口呢?? 原來 Empty Project 中預設是以 Console 模式編譯, 所以我們到選單的 Project -> Properties -> Build Targets 把 Type 從 Console Applications 改為 GUI Application 就可以了... 再次編譯還是看到 DOS 窗口是怎麼回事呢?? 那是因為我們還沒有註冊一個 Windows 窗口, 所以我們接下來就開始做這個步驟

* WinMain (http://msdn.microsoft.com/en-us/library/ms633559.aspx)
* MessageBox (http://msdn.microsoft.com/en-us/library/ms645505.aspx]http://msdn.microsoft.com/en-us/library/ms645505.aspx[/url])




Windows 窗口在註冊的時候需要填入哪些資料呢?? 在 WINAPI 中已經有清楚列出結構, 所以我們只要根據 WNDCLASSEX 的結構填入相關資料.

#include <windows.h> //載入必要的 windows 頭文件

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wincl; //定義一個 WNDCLASSEX

    wincl.hInstance = hInstance;                    //填入WinMain第一個參數
    wincl.lpszClassName = "FirstLesson";              //給窗口一個 class name
    wincl.lpfnWndProc = WinProc;                    //指定消息循環函數
    wincl.style = CS_DBLCLKS;                       //窗口類型, 其他的可以參考手冊
    wincl.cbSize = sizeof(WNDCLASSEX);              //定義一個結構大小

    wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);  //因為沒有特定的Icon所以使用系統所擁有的 Icon
    wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);//小Icon同上
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);    //鼠標類型同樣使用Windows自帶的
    wincl.lpszMenuName = NULL;                      //因為目前沒製作選單, 所以這裡設定為 NULL
    wincl.cbClsExtra = 0;                           //無任何附加的byte
    wincl.cbWndExtra = 0;                           //無任何其他窗體和結構
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;//設定背景顏色

}


看過以上的結構, 還不是時候進行編譯, 這只是第一步, 接下來我們就要為這個結構向 Windows 系統進行註冊
* WNDCLASSEX (http://msdn.microsoft.com/en-us/library/ms633577(VS.85).aspx)

#include <windows.h> //載入必要的 windows 頭文件

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wincl; //定義一個 WNDCLASSEX

    wincl.hInstance = hInstance;                    //填入WinMain第一個參數
    wincl.lpszClassName = "FirstLesson";              //給窗口一個 class name
    wincl.lpfnWndProc = WinProc;                    //指定消息循環函數
    wincl.style = CS_DBLCLKS;                       //窗口類型, 其他的可以參考手冊
    wincl.cbSize = sizeof(WNDCLASSEX);              //定義一個結構大小

    wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);  //因為沒有特定的Icon所以使用系統所擁有的 Icon
    wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);//小Icon同上
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);    //鼠標類型同樣使用Windows自帶的
    wincl.lpszMenuName = NULL;                      //因為目前沒製作選單, 所以這裡設定為 NULL
    wincl.cbClsExtra = 0;                           //無任何附加的byte
    wincl.cbWndExtra = 0;                           //無任何其他窗體和結構
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;//設定背景顏色

    //以上資料填寫好之後就開始註冊
    if (!RegisterClassEx(&wincl))
        return 0;

}


通過 RegisterClassEx 即可向 Windows 系統要求註冊這個窗口, 而 RegisterClassEx 如果註冊不成功會返回 false, 所以我們在這個時候就回傳給 WinMain 一個 0 以結束掉這個程式. 然而下一個步驟是甚麼呢?? 當然就是開始創建了, 怎麼上面那麼多並沒有創建到窗口嗎?? 所謂程式就像人類的生活結構, 妳沒向建屋居提交構畫好的所要建造房子的藍圖並受到批准的話, 建屋居怎麼會批准你建呢?? 那麼開始建立我們的窗口吧

#include <windows.h> //載入必要的 windows 頭文件

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wincl; //定義一個 WNDCLASSEX

    wincl.hInstance = hInstance;                    //填入WinMain第一個參數
    wincl.lpszClassName = "FirstLesson";            //給窗口一個 class name
    wincl.lpfnWndProc = WinProc;                    //指定消息循環函數
    wincl.style = CS_DBLCLKS;                       //窗口類型, 其他的可以參考手冊
    wincl.cbSize = sizeof(WNDCLASSEX);              //定義一個結構大小

    wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);  //因為沒有特定的Icon所以使用系統所擁有的 Icon
    wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);//小Icon同上
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);    //鼠標類型同樣使用Windows自帶的
    wincl.lpszMenuName = NULL;                      //因為目前沒製作選單, 所以這裡設定為 NULL
    wincl.cbClsExtra = 0;                           //無任何附加的byte
    wincl.cbWndExtra = 0;                           //無任何其他窗體和結構
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;//設定背景顏色

    //以上資料填寫好之後就開始註冊
    if (!RegisterClassEx(&wincl))
        return 0;
        

    HWND hwnd; //定義一個窗口柄句
   
    hwnd = CreateWindowEx(
           0,                                       //窗口延伸類型
           "FirstLesson",                           //窗口的 class name, 需同結構一至
           "WinMain GUI Application",               //窗口標題
           WS_OVERLAPPEDWINDOW,                     //窗口樣式
           100,                                     //螢幕上的X座標點
           100,                                     //螢幕上的Y座標點
           300,                                     //寬度
           200,                                     //高度
           HWND_DESKTOP,                            //定義窗口為Desktop的子窗口
           NULL,                                    //同結構一至無選單
           hInstance,                               //WinMain第一個參數
           NULL                                     //無任何附加資料
           );

    //如果創建不成功返回 WinMain 0
    if(hwnd == NULL)
        return 0;

    ShowWindow(hwnd, nCmdShow);

}


這裡我們創建好之後, 連同顯示一併寫入, CreateWindowEx 在創建成功後會返回該窗口的柄句, 否則會回傳 NULL. 而 ShowWindow 這個函數主要就是顯示哪個柄句, 而第二個參數則是以哪種型態顯示, 這方面可參考 API

* CreateWindowEx (http://msdn.microsoft.com/en-us/library/ms632680.aspx)
* ShowWindow (http://msdn.microsoft.com/en-us/library/ms633548.aspx)

[ 本帖最后由 Super-Tomato 于 2008-12-16 09:08 AM 编辑 ]
作者: Super-Tomato    时间: 2008-12-16 09:06 AM
最後當然不可忘記一個 GUI 程式都一定會有不同的事件(Events)需要做處理, 如放大, 縮小, 關閉等等... 所以我們必須也定義一個事件處理函數. 之前我們就已經在結構中定義好了這個函數名稱  wincl.lpfnWndProc = WinProc, 所以我們就要開始寫這個函數的處理事件

#include <windows.h> //載入必要的 windows 頭文件

//定義 WinProc 事件處理函數
LRESULT CALLBACK WinProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wincl; //定義一個 WNDCLASSEX

    wincl.hInstance = hInstance;                    //填入WinMain第一個參數
    wincl.lpszClassName = "FirstLesson";            //給窗口一個 class name
    wincl.lpfnWndProc = WinProc;                    //指定消息循環函數
    wincl.style = CS_DBLCLKS;                       //窗口類型, 其他的可以參考手冊
    wincl.cbSize = sizeof(WNDCLASSEX);              //定義一個結構大小

    wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);  //因為沒有特定的Icon所以使用系統所擁有的 Icon
    wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);//小Icon同上
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);    //鼠標類型同樣使用Windows自帶的
    wincl.lpszMenuName = NULL;                      //因為目前沒製作選單, 所以這裡設定為 NULL
    wincl.cbClsExtra = 0;                           //無任何附加的byte
    wincl.cbWndExtra = 0;                           //無任何其他窗體和結構
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;//設定背景顏色

    //以上資料填寫好之後就開始註冊
    if (!RegisterClassEx(&wincl))
        return 0;


    HWND hwnd; //定義一個窗口柄句

    hwnd = CreateWindowEx(
           0,                                       //窗口延伸類型
           "FirstLesson",                           //窗口的 class name, 需同結構一至
           "WinMain GUI Application",               //窗口標題
           WS_OVERLAPPEDWINDOW,                     //窗口樣式
           100,                                     //螢幕上的X座標點
           100,                                     //螢幕上的Y座標點
           300,                                     //寬度
           200,                                     //高度
           HWND_DESKTOP,                            //定義窗口為Desktop的子窗口
           NULL,                                    //同結構一至無選單
           hInstance,                               //WinMain第一個參數
           NULL                                     //無任何附加資料
           );

    //如果創建不成功返回 WinMain 0
    if(hwnd == NULL)
        return 0;

    ShowWindow(hwnd, nCmdShow);

    //定義一個 MSG 以接收消息
    MSG messages;

    //使用 while 循環取得使用者的處理消息
    while (GetMessage (&messages, NULL, 0, 0))
    {
        //轉換處理消息
        TranslateMessage(&messages);
        //把轉換好的消息傳給 WinProc 函數
        DispatchMessage(&messages);
    }

    //回傳 0 結束程式
    return 0;

}


LRESULT CALLBACK WinProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    //使用 switch case 處理不同事件
    switch (message)
    {
        //當使用者按下關閉按鈕就會觸發 WM_CLOSE, 然而會呼叫 WM_DESTROY
        case WM_DESTROY:
            PostQuitMessage(0); //使用 PostQuitMessage 設定 message 的 wParam 為 0 退出程式
            break;
        default:
            return DefWindowProc (hwnd, message, wParam, lParam); //呼叫 DefWindowProc 不進行任何處理
    }

    return 0;
}


只要在 WinMain 函數之後編寫的函數都必須在 WinMain 之前定義一個函數地址
在這當中比較不清楚的應該就是 while 這個循環在甚麼時候會停止, while 會停止主要是當 PostQuitMessage 執行的時候, message 的 wParam 為 0, while 就即刻結束循環.

* WindowProcess (http://msdn.microsoft.com/en-us/library/ms633573(VS.85).aspx)
* DefWindowProc (http://msdn.microsoft.com/en-us/library/ms633572(VS.85).aspx)




1. 定義結構
2. 註冊結構
3. 建立窗口
4. 顯示窗口
5. 寫入事件

只要這 5 個步驟完成, 那麼也就是代表可以按下 F9 進行編譯與執行了, 是不是都成功了呢?? 了解了以上的說明後, 只要根據 WINAPI 對參數的詳解進行更改就可以做出自己喜歡的介面顏色, 大小, 邊框等.



在了解了 WinMain 架構后, 以後的編程只要選擇 File -> New -> Win GUI 就會自動幫你完成今天的教程




欢迎光临 JBTALKS.CC (https://www.jbtalks.cc/) Powered by Discuz! X2.5