簡體   English   中英

CreateWindowEx 不使用 class 模板的實例創建 HWND

[英]CreateWindowEx does not create HWND with the instance of a class template

我正在關注本教程,如果我將所有定義保存在一個 header 文件中,一切都很好並且可以工作(我的意思是,如果我直接復制此代碼)。 但是如果我嘗試將定義移動到單獨的文件中,它不會創建 HWND。

在調用CreateWindowEx時,它會轉到BaseWindow<DERIVED_TYPE>::WindowProc並按順序一個接一個地發送以下消息:

  • WM_GETMINMAXINFO
  • WM_NCCREATE
  • WM_NCDESTROY

退出后,會導致未創建 HWND。 因此,不會出現 window。 HWND 為 NULL

項目結構:

  • 主文件
  • 視窗/
    • BaseWindow.h
    • BaseWindow.cpp
    • 主窗口.h
    • 主窗口.cpp

這就是我的代碼的外觀。

主文件

#ifndef UNICODE
#define UNICODE
#endif // !UNICODE

#include <windows.h>
#include <new>

#include "Windows/MainWindow.h"

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    MainWindow win;

    if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW))
    {
        return 0;
    }

    ShowWindow(win.Window(), nCmdShow);

    // Run the message loop.

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

    return 0;
}

Windows/BaseWindow.h

#pragma once

#include <windows.h>

template <typename DERIVED_TYPE>
class BaseWindow
{
public:
    BaseWindow() : m_hwnd(NULL) { }
    
    static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    BOOL Create(
        PCWSTR lpWindowName,
        DWORD dwStyle,
        DWORD dwExStyle = 0,
        int x = CW_USEDEFAULT,
        int y = CW_USEDEFAULT,
        int nWidth = CW_USEDEFAULT,
        int nHeight = CW_USEDEFAULT,
        HWND hWndParent = 0,
        HMENU hMenu = 0
    );

    HWND Window() const { return m_hwnd; }

protected:

    virtual PCWSTR ClassName() const = 0;
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;

    HWND m_hwnd;
};

Windows/BaseWindow.cpp

#include "BaseWindow.h"
#include "MainWindow.h"

template <typename DERIVED_TYPE>
LRESULT CALLBACK BaseWindow<DERIVED_TYPE>::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    DERIVED_TYPE* pThis = NULL;

    if (uMsg == WM_NCCREATE)
    {
        CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
        pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
    }
    else
    {
        pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    }

    if (pThis)
    {
        return pThis->HandleMessage(uMsg, wParam, lParam);
    }
    else
    {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

template <typename DERIVED_TYPE>
BOOL BaseWindow<DERIVED_TYPE>::Create(
    PCWSTR lpWindowName,
    DWORD dwStyle,
    DWORD dwExStyle,
    int x,
    int y,
    int nWidth,
    int nHeight,
    HWND hWndParent,
    HMENU hMenu
)
{
    WNDCLASS wc = { 0 };

    wc.lpfnWndProc = DERIVED_TYPE::WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = ClassName();

    RegisterClass(&wc);

    m_hwnd = CreateWindowEx(
        dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
        nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
    );

    return (m_hwnd ? TRUE : FALSE);
}

// !The definition of the class template
template class BaseWindow<MainWindow>;

Windows/MainWindow.h

#pragma once

#include "BaseWindow.h"

class MainWindow : public BaseWindow<MainWindow>
{
public:
    PCWSTR  ClassName() const { return L"Sample Window Class"; }
    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};

Windows/MainWindow.cpp

#include "MainWindow.h"

LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(m_hwnd, &ps);
        FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
        EndPaint(m_hwnd, &ps);
    }
    return 0;

    default:
        return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
    }
    return TRUE;
}

我試圖在沒有模板的情況下僅在類上做它並且它有效。

我還嘗試在CreateWindowEx的參數列表末尾刪除,它也有效,我的意思是,如果它看起來像這樣:它將有效:

m_hwnd = CreateWindowEx(
    dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
    nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), NULL
);

所以也許我在某種程度上濫用模板。

CreateWindowEx返回之前會抽取幾條消息。 除了一個 ( WM_NCCREATE ) 之外的所有都將通過 HandleMessage 進行路由,它期望m_hwnd是一個有效的句柄。 他們沒有得到它,因為您永遠不會保存它,直到CreateWindowEx的最終結果通過返回值獲得。 到那時為時已晚。

WM_NCCREATE是頂層 windows(例如你的)接收到的第一個 window 消息,並且應該是用於(a)將 hwnd 參數保存到 m_hwnd,以及(b)將實例指針存儲在GWLP_USERDATA空間中的消息。 你在做后者,但你沒有做前者。

CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
pThis->m_hwnd = hwnd; // <===== ADD THIS =====
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);

HandleMessage()使用未初始化的m_hwnd 這就是CreateWindowEx()返回 NULL 的原因。

1. CreateWindowEx
2. sends WM_CREATE
3. WM_CREATE is processed by WindowProc
4. WindowProc calls HandleMessage, which uses m_hwnd,
   which is not initialized, so HandleMessage returns
   failure
5. Failure is returned as the result for WM_CREATE,
   cancelling window creation
6. CreateWindowEx fails and returns NULL <--- and right now
   m_hwnd is assigned

簡而言之:在CreateWindowEx()返回之前,您不能使用m_hwnd

WM_CREATE 參考

如果應用程序處理此消息,它應該返回零以繼續創建 window。 如果應用程序返回 –1,則 window 被銷毀,並且 CreateWindowEx 或 CreateWindow function 返回 NULL 句柄。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM