天天看點

DirectX之一——Windows程式設計基礎架構

DirectX——Windows程式設計基礎架構

前言

  DirectX是由微軟公司建立的一系列專為多媒體以及遊戲開發的應用程式接口,内部元件按照性質可分為:顯示部分、聲音部分、輸入部分和網絡部分。其中Direct3D便屬于顯示部分中的3D圖形顯示子集,Direct3D按照前後版本的釋出可分為Direct3D9、Direct3D10、Direct3D11、Direct3D12;本系列文章介紹D3D的版本會在标題的括号中說明,首先介紹的會是Direct3D9系列的文章。關于DirectX的詳細介紹可點選這裡,由于DirectX是一套調用接口,是以我們必須編寫應用程式,在程式中進行調用。這裡對windows程式設計進行簡單(詳細)介紹,隻介紹使用DirectX需要的那一部分。

Windows應用程式介紹

  了解Windows程式設計的人都知道,windows程式遵循事件驅動模型,通常Windows程式啟動後會等到事件的發生,隻有等事件發生後Windows程式才進行處理,比如:滑鼠點選,鍵盤點選,視窗大小變化等等;這裡對一些概念進行一下梳理:

Windows————Windows作業系統

Windows應用程式————我們編寫的應用程式

應用視窗————一個Windows應用程式所建立的視窗(可能多個)

當我們點選螢幕時,接受到該指令的是Windows,然後Windows會向Windows應用程式進行發送該消息。具體的實作是每個Windows應用程式都有一個消息隊列,Windows向Windows應用程式發送消息其實就是将消息添加到Windows應用程式的消息隊列裡,Windows應用程式會在一個消息循環中不斷檢查消息隊列,當檢查到消息時便将消息派送給特定的視窗(一個應用程式可能有多個視窗)。具體流程下面這張圖挺清楚的

DirectX之一——Windows程式設計基礎架構

Windows應用程式簡單實作

  知道了Windows應用程式的運作原理,我們就可以來進行程式的編寫了,這裡實作的是一個空白視窗,按esc鍵可退出,滑鼠左鍵點選會彈出“hello world”視窗;

具體代碼如下(注釋中有各子產品的解釋):

/*windows 開發所需頭檔案
包含Windows開發所需要的宏、類、函數、結構體等結構的定義*/
#include <windows.h>

HWND MainWindowHandle = ;                                                              //主視窗句柄,相當于視窗的ID
bool InitWindowsApp(HINSTANCE instanceHandle, int show);            //初始化視窗函數
int Run();                                                                                                          //消息循環函數
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg,               //消息處理函數
                         WPARAM wParam, LPARAM lParam);

/*WinMain函數,相當于C++中的main函數,是程式運作的入口*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nShowCmd)
{
    //初始化失敗則彈出消息視窗提示
    if (!InitWindowsApp(hInstance, nShowCmd))
    {
        ::MessageBox(, "Init - Failed", "Error", MB_OK);
        return ;
    }
    return Run();
}

//初始化視窗函數實作
bool InitWindowsApp(HINSTANCE instanceHandle, int show)
{
    //windows視窗類型的自定義
    WNDCLASS wc;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = ;
    wc.cbWndExtra = ;
    wc.hInstance = instanceHandle;
    wc.hIcon = ::LoadIcon(, IDI_APPLICATION);
    wc.hCursor = ::LoadCursor(, IDC_ARROW);
    wc.hbrBackground = static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH));
    wc.lpszMenuName = ;
    wc.lpszClassName = "Hello";

    //向Windows系統注冊視窗
    if (!::RegisterClass(&wc))
    {
        ::MessageBox(, "RegisterClass - Failed", , );
        return false;
    }

    //根據自定義的視窗進行建立
    MainWindowHandle = ::CreateWindow(
        "Hello",
        "Hello",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        ,
        ,
        instanceHandle,
        );

    //建立失敗則彈出消息視窗提示
    if (MainWindowHandle == )
    {
        ::MessageBox(, "CreateWindow - Failed", , );
        return false;
    }

    ::ShowWindow(MainWindowHandle, show);           //顯示視窗
    ::UpdateWindow(MainWindowHandle);                   //進行視窗的重新整理

    return true;
}

//消息循環函數定義
int Run()
{
    MSG msg;
    ::ZeroMemory(&msg, sizeof(MSG));

    while (::GetMessage(&msg, , , ))
    {
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
    }

    return msg.wParam;
}

//消息處理函數定義
LRESULT CALLBACK WndProc(HWND windowHandle, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_LBUTTONDOWN:
        ::MessageBox(, "Hello, Word", "Hello", MB_OK);
        return ;
    case WM_KEYDOWN:
        if (wParam == VK_ESCAPE)
        {
            ::DestroyWindow(MainWindowHandle);
        }
        return ;
    case WM_DESTROY:
        ::PostQuitMessage();
        return ;
    }
    return ::DefWindowProc(windowHandle, msg, wParam, lParam);
}
           

Windows應用程式詳細介紹

  1. WinMain函數

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nShowCmd)

    WinMain函數是Windows程式運作的入口,就跟c++中的main函數一樣,各參數定義如下:

    WINAPI:

    #define WINAPI __stdcall;

    該語句指定了一個呼叫約定,包括如何生産機械碼以在堆棧中放置函數呼叫的參數。許多Windows函數呼叫聲明為WINAPI;

    HINSTANCE:

    typedef HINSTANCE_ *HINSTANCE;

    可以看出HINSTANCE實際是一個指針類型;

    PSTR:

    typedef CHAR *PSTR;

    可以看到PSTR代表一個字元串指針;

    hInstance:目前windows應用程式執行個體(程式自身)的句柄,由系統配置設定。當同時運作同一程式多次時,便建立了該程式的多個執行執行個體;

    hPresInstance:通過檢查hPrevInstance參數能夠确定自身的其它執行執行個體是否正在運作。在32位Windows版本中,該概念已被抛棄,傳給WinMain的第二個參數總是NULL(定義為0);

    pCmdLine:用于運作程式的指令行字元串;

    nShowCmd:指定應用程式視窗的顯示方式;SW_SHOWNORMAL:視窗按一般大小顯示;SW_SHOWMAXIMIZED視窗最大化顯示;SW_SHOWMINNOACTIVE視窗最小化顯示。

    msdn有WinMain的詳細介紹

  2. InitWindowsApp函數

    bool InitWindowsApp(HINSTANCE instanceHandle, int show)

    該函數首先進行視窗類型的自定義,然後注冊視窗、建立視窗、顯示視窗。這裡大部分都是調用系統函數,是以很多東西隻貼一個查找連接配接,以便查閱;

    WNDCLASS:

    typedef struct tagWNDCLASSA { ... } WNDCLASSA typedef WNDCLASSA WNDCLASS;

    可以看出WNDCLASS是一個結構體類型,裡面各成員的取值都有參考範圍,具體範圍比較長,這裡貼一個MSDN連接配接以供查閱

    ::LoadIcon:這裡的作用域符号”::”需要注意,這裡代表的是全局作用域,即調用的是全局LoadIcon函數,關于作用域符号”::”的三種用法,這裡再貼一個連結。另外關于LoadIcon函數的用法,這裡貼一個查找連接配接;

    ::LoadCursor:與::LoadIcon類似,不過設定的是滑鼠指針的樣式,查找連接配接

    ::GetStockObject(WHITE_BRUSH):傳回一個筆刷或者字型或者調色闆等的句柄,具體參數參考查找連接配接

    static_cast<>():static_cast用于編譯時進行類型轉換,與之相關的還有reinterpret_cast、dynamic_cast。dynamic_cast用于運作時類型轉換、reinterpret_cast是完全的按照二進制存儲重新解釋。

    ::RegisterClass向系統注冊視窗,參考文檔

    ::MessageBox:消息視窗函數,參考文檔

    ::CreateWindow:建立視窗函數,參考文檔

    ::ShowWindow:顯示視窗,控制視窗的顯示/隐藏,參考文檔

    ::UpdateWindow:更新視窗,控制視窗繪制内容的更新,參考文檔

  3. Run函數

    消息循環,用來處理Windows系統向程式發動的消息。

    MSG:

    typedef struct tagMSG { ... } MSG, *PMSG, *LPMSG;

    可以看出,MSG為一個結構體,裡面包含一些跟消息相關的成員資訊。具體各成員意義參考這裡

    ::ZeroMemory:初始化記憶體為0,參考文檔

    ::GetMessage:從消息隊列中擷取一個消息,該函數會阻塞程序,直到擷取到消息。與之相對應的是PeekMessage函數,該函數不會阻塞程式。參考文檔

    ::TranslateMessage:負責将消息中的虛拟鍵轉換為字元資訊,參考文檔

    ::DispatchMessage:将消息派送到視窗消息處理函數,參考文檔

  4. WndProc函數

    LRESULT CALLBACK WndProc(HWND windowHandle, UINT msg, WPARAM wParam, LPARAM lParam)

    負責接受并處理消息,具體可參考

    LRESULT:實際是一個long類型;

    CALLBACK:

    #define CALLBACK __stdcall

    ;同WINAPI一樣,表明該函數是一個回調函數,表明Windows可以對該函數進行外部調用;

    msg:表示消息的數值。

    WPARAM、LPARAM:實際上是usigned int,表示與消息相關的參數

    WM_KEYDOWN、WM_LBUTTONDOWN、WM_DESTROY:表示消息的類型,Windows定義額很多消息類型,具體可參考

    ::DestroyWindow:手動銷毀視窗,并向Windows發送WM_DESTROY消息,詳細可參考這裡

    ::PostQuitMessage:向程式的消息隊列中插入一個WM_QUIT消息。前面提到過,GetMessage對于除了WM_QUIT之外的從消息隊列中取出的所有消息都傳回非0值。而當GetMessage得到一個WM_QUIT消息時,它傳回0。這将導緻WinMain退出消息循環,并終止程式。參考文檔

    ::DefWindowProc:預設的消息處理函數,該函數針對不同的消息實作了不同的預設處理方法。對于不需要我們處理的消息,隻需調用該函數即可。參考文檔

  到這裡整個程式差不多就能了解了,以後在可以在此程式基礎上學習DirectX接口的使用。