将 GetWindowLongPtr() 强制转换为模板化类型失败

Casting GetWindowLongPtr() to a templated type failed

本文关键字:类型 失败 转换 GetWindowLongPtr      更新时间:2023-10-16

我开始做一个免费项目,在OpenGL中创建自己的渲染引擎我已经在 DirectX 中创建了一个,并希望探索新事物。我的 DirectX 引擎在这里和那里有一些缺陷,所以我想在我的 OpenGl 引擎中纠正它们。

目前,我也在尝试创建一个新窗口来渲染我的几何体。我希望我的窗口类是可重用的,所以我做了一个模板类:

    #ifndef _IWINDOW_H
    #define _IWINDOW_H
    #ifndef _WINDOWS_
        #include <windows.h>
    #endif
    #ifndef _STRING_H
        #include "string.h"
    #endif
    template<class T>
    class IWindow
    {
    public:
        IWindow();
        virtual ~IWindow();
        static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
        virtual LRESULT handleEvent(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) = 0;
        virtual BYTE getWindowBitsPerPixel() const = 0;
        virtual long getWindowWidth() const = 0;
        virtual long getWindowHeight() const = 0;
        virtual const std::tstring getWindowTitle() const = 0;
        virtual const std::tstring getWindowClassName() const = 0;
        HWND getWindowHandle();
        HINSTANCE getWindowInstance();
    protected:
        bool createWindow();
        bool destroyWindow();
        void setExtendedStyle(DWORD extendedStyle);
        void setStyle(DWORD style);
        DWORD getExtentedStyle() const;
        DWORD getStyle() const;
        RECT getWindowRect() const;
    private:
        bool setupWindow();
        bool errorHandling();
        WNDCLASS createWindowClass();
        RECT createWindowRect();
        PIXELFORMATDESCRIPTOR createWindowPixelFormatDescription();
        HGLRC           handle_resourcecontext;
        HDC             handle_devicecontext;
        HWND            handle_window;
        HINSTANCE       handle_instance;
        RECT            window_rect;
        DWORD           extended_style;
        DWORD           style;
        unsigned int    pixel_format;
    };
    template<class T>
    IWindow<T>::IWindow()
        : handle_resourcecontext(0)
        , handle_devicecontext(0)
        , handle_window(0)
        , handle_instance(GetModuleHandle(NULL))
        , window_rect()
        , extended_style(0)
        , style(0)
        , pixel_format(0)
    {}
    template<class T>
    IWindow<T>::~IWindow()
    {}
    template<class T>
    LRESULT CALLBACK IWindow<T>::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_CREATE)
        {
            // if the message is WM_CREATE, the lParam contains a pointer to a CREATESTRUCT
            // the CREATESTRUCT contains the "this" pointer from the CreateWindow method
            // the "this" pointer of our app is stored in the createstruct pcs->lpCreateParams
            CREATESTRUCT *pCS = (CREATESTRUCT*)lParam;
            SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG)pCS->lpCreateParams);
        }
        else
        {
            //retrieve the stored "this" pointer
            LONG value = GetWindowLongPtr(hWnd, GWLP_USERDATA);
            T* window = (T*)(GetWindowLongPtr(hWnd, GWLP_USERDATA));
            if (window != nullptr)
                return window->handleEvent(hWnd, uMsg, wParam, lParam);
        }
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    template<class T>
    bool IWindow<T>::createWindow()
    {
        this->window_rect = createWindowRect();
        if (!RegisterClass(&createWindowClass()))
        {
            MessageBox(NULL, _T("Failed To Register The Window Class."), _T("ERROR"), MB_OK | MB_ICONEXCLAMATION);
            return FALSE;
        }
        if (!setupWindow())
            return FALSE;
        if (!errorHandling())
            return FALSE;
        ShowWindow(this->handle_window, SW_SHOW);
        SetForegroundWindow(this->handle_window);
        SetFocus(this->handle_window);
        return TRUE;
    }
    template<class T>
    bool IWindow<T>::destroyWindow()
    {
        LPCTSTR window_classname = getWindowClassName().c_str();
        if (this->handle_resourcecontext)
        {
            if (!wglMakeCurrent(NULL, NULL)) MessageBox(NULL, _T("Release Of DC And RC Failed."), _T("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
            if (!wglDeleteContext(this->handle_resourcecontext)) MessageBox(NULL, _T("Release Rendering Context Failed."), _T("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
            this->handle_resourcecontext = NULL;
        }
        if (this->handle_devicecontext && !ReleaseDC(this->handle_window, this->handle_devicecontext))
        {
            MessageBox(NULL, _T("Release Device Context Failed."), _T("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
            this->handle_devicecontext = NULL;
        }
        if (this->handle_window && !DestroyWindow(this->handle_window))
        {
            MessageBox(NULL, _T("Could Not Release hWnd."), _T("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
            this->handle_window = NULL;
        }
        if (!UnregisterClass(_T("classname"), this->handle_instance))
        {
            MessageBox(NULL, _T("Could Not Unregister Class."), _T("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION);
            this->handle_instance = NULL;
        }
        return TRUE;
    }
    template<class T>
    void IWindow<T>::setExtendedStyle(DWORD extendedStyle)
    {
        this->extended_style = extendedStyle;
    }
    template<class T>
    void IWindow<T>::setStyle(DWORD style)
    {
        this->style = style;
    }
    template<class T>
    DWORD IWindow<T>::getExtentedStyle() const
    {
        return this->extended_style;
    }
    template<class T>
    DWORD IWindow<T>::getStyle() const
    {
        return this->style;
    }
    template<class T>
    RECT IWindow<T>::getWindowRect() const
    {
        return this->window_rect;
    }
    template<class T>
    bool IWindow<T>::setupWindow()
    {
        LPCTSTR window_classname = getWindowClassName().c_str();
        LPCTSTR window_title = getWindowTitle().c_str();
        this->handle_window = CreateWindowEx(this->extended_style,
            _T("classname"),
            window_title,
            this->style |
            WS_CLIPSIBLINGS |
            WS_CLIPCHILDREN,
            0, 0,
            this->window_rect.right - this->window_rect.left,
            this->window_rect.bottom - this->window_rect.top,
            NULL,
            NULL,
            this->handle_instance,
            this);
        int error = GetLastError();
        if (!this->handle_window)
        {
            MessageBox(NULL, _T("Window Creation Error."), _T("ERROR"), MB_OK | MB_ICONEXCLAMATION);
            return FALSE;
        }
        return TRUE;
    }
    template<class T>
    bool IWindow<T>::errorHandling()
    {
        //Check if we have a device context
        if (!(this->handle_devicecontext = GetDC(this->handle_window)))
        {
            MessageBox(NULL, _T("Can't Create A GL Device Context."), _T("ERROR"), MB_OK | MB_ICONEXCLAMATION);
            return FALSE;
        }
        //Check the pixel format
        PIXELFORMATDESCRIPTOR pfd = createWindowPixelFormatDescription();
        this->pixel_format = ChoosePixelFormat(this->handle_devicecontext, &pfd);
        if (!this->pixel_format)
        {
            MessageBox(NULL, _T("Can't Find A Suitable PixelFormat."), _T("ERROR"), MB_OK | MB_ICONEXCLAMATION);
            return FALSE;
        }
        if (!SetPixelFormat(this->handle_devicecontext, this->pixel_format, &pfd))
        {
            MessageBox(NULL, _T("Can't Set The PixelFormat."), _T("ERROR"), MB_OK | MB_ICONEXCLAMATION);
            return FALSE;
        }
        //Check if the open gl context is succesfully created
        this->handle_resourcecontext = wglCreateContext(this->handle_devicecontext);
        if (!this->handle_resourcecontext)
        {
            MessageBox(NULL, _T("Can't Create A GL Rendering Context."), _T("ERROR"), MB_OK | MB_ICONEXCLAMATION);
            return FALSE;
        }
        if (!wglMakeCurrent(this->handle_devicecontext, this->handle_resourcecontext))
        {
            MessageBox(NULL, _T("Can't Activate The GL Rendering Context."), _T("ERROR"), MB_OK | MB_ICONEXCLAMATION);
            return FALSE;
        }
        return TRUE;
    }
    template<class T>
    WNDCLASS IWindow<T>::createWindowClass()
    {
        LPCTSTR window_classname = getWindowClassName().c_str();
        WNDCLASS wc;
        wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
        wc.lpfnWndProc = (WNDPROC)WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = sizeof(LONG_PTR);
        wc.hInstance = this->handle_instance;
        //TODO: load the cursor and application icon!
        wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = NULL;
        wc.lpszMenuName = NULL;
        wc.lpszClassName = _T("classname");
        return wc;
    }
    template<class T>
    RECT IWindow<T>::createWindowRect()
    {
        return RECT
        {
            (long)0,
            (long)0,
            (long)getWindowWidth(),
            (long)getWindowHeight()
        };
    }
    template<class T>
    PIXELFORMATDESCRIPTOR IWindow<T>::createWindowPixelFormatDescription()
    {
        return PIXELFORMATDESCRIPTOR
        {
            sizeof(PIXELFORMATDESCRIPTOR),
            1,
            PFD_DRAW_TO_WINDOW |
            PFD_SUPPORT_OPENGL |
            PFD_DOUBLEBUFFER,
            PFD_TYPE_RGBA,
            getWindowBitsPerPixel(),
            0, 0, 0, 0, 0, 0,
            0,
            0,
            0,
            0, 0, 0, 0,
            16,
            0,
            0,
            PFD_MAIN_PLANE,
            0,
            0, 0, 0
        };
    }
    template<class T>
    HWND IWindow<T>::getWindowHandle()
    {
        return this->handle_window;
    }
    template<class T>
    HINSTANCE IWindow<T>::getWindowInstance()
    {
        return this->handle_instance;
    }
    #endif //_IWINDOW_H

如果我创建此类的实例,它将如下所示:

#ifndef _WINDOW_H
#define _WINDOW_H
#include "System.h"
#include "IWindow.h"
class MainWindow : public System, public IWindow<MainWindow>
{
public:
    MainWindow();
    virtual ~MainWindow();
    virtual bool initialize();
    virtual void update();
    virtual bool shutDown();
    bool setFullscreen(bool fullscreen);
    bool getFullscreen() const;
    virtual LRESULT handleEvent(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
    virtual BYTE getWindowBitsPerPixel() const;
    virtual long getWindowWidth() const;
    virtual long getWindowHeight() const;
    virtual const std::tstring getWindowTitle() const;
    virtual const std::tstring getWindowClassName() const;
private:
};
#endif //_WINDOW_H

使用相应的源文件:

#include "MainWindow.h"
#include <glgl.h>
#include <glglu.h>
#ifndef _STRING_H
    #include "string.h"
#endif
#include "WorldSettings.h"
#include "ApplicationSettings.h"
#include "SystemType.h"
#include "Singleton.h"
MainWindow::MainWindow()
    :System(SystemType::WINDOW_SYSTEM)
{
}
MainWindow::~MainWindow()
{
}
bool MainWindow::initialize()
{
    if (!createWindow())
    {
        shutDown();
        return FALSE;
    }
    return true;
}
void MainWindow::update()
{
}
bool MainWindow::shutDown()
{
    ApplicationSettings settings = Singleton<WorldSettings>::getInstance().getApplicationSettings();
    if (settings.getFullscreen())
    {
        ChangeDisplaySettings(NULL, 0);                 
        ShowCursor(TRUE);                               
    }
    destroyWindow();
    return TRUE;
}
bool MainWindow::setFullscreen(bool fullscreen)
{
    ApplicationSettings settings = Singleton<WorldSettings>::getInstance().getApplicationSettings();
    if (settings.getFullscreen() == fullscreen)
        return true;
    if (fullscreen)
    {
        DEVMODE dmScreenSettings;
        memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
        dmScreenSettings.dmSize = sizeof(dmScreenSettings);
        dmScreenSettings.dmPelsWidth = settings.getWindowWidth();
        dmScreenSettings.dmPelsHeight = settings.getWindowHeight();
        dmScreenSettings.dmBitsPerPel = settings.getBitsPerPixel();
        dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
        if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
        {
            if (MessageBox(NULL, _T("The Requested Fullscreen Mode Is Not Supported BynYour Video Card. Use Windowed Mode Instead?"), _T("NeHe GL"), MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
            {
                settings.setFullscreen(FALSE);
            }
            else
            {
                MessageBox(NULL, _T("Program Will Now Close."), _T("ERROR"), MB_OK | MB_ICONSTOP);
                return FALSE;
            }
        }
    }
    if (settings.getFullscreen())
    {
        setExtendedStyle(WS_EX_APPWINDOW);
        setStyle(WS_POPUP);
        ShowCursor(FALSE);
    }
    else
    {
        setExtendedStyle(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
        setStyle(WS_OVERLAPPEDWINDOW);
        ShowCursor(TRUE);
    }
    AdjustWindowRectEx(&getWindowRect(), getStyle(), FALSE, getExtentedStyle());
    //If the settings have been changed apply them to the worldsettings
    Singleton<WorldSettings>::getInstance().setApplicationSettings(settings);
    return TRUE;
}
bool MainWindow::getFullscreen() const
{
    return Singleton<WorldSettings>::getInstance().getApplicationSettings().getFullscreen();
}
LRESULT MainWindow::handleEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_ACTIVATE:
    {
        if (!HIWORD(wParam))
        {
            activate();
        }
        else
        {
            deactivate();
        }
        return 0;
    }
    case WM_SYSCOMMAND:
    {
        switch (wParam)
        {
        case SC_SCREENSAVE:
        case SC_MONITORPOWER:
            return 0;
        }
        break;
    }
    case WM_CLOSE:
    {
        PostQuitMessage(0);
        return 0;
    }
    //case WM_SIZE:                             
    //{
    //  ReSizeGLScene(LOWORD(lParam), HIWORD(lParam));
    //  return 0;                               
    //}
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
BYTE MainWindow::getWindowBitsPerPixel() const
{
    return Singleton<WorldSettings>::getInstance().getApplicationSettings().getBitsPerPixel();
}
long MainWindow::getWindowWidth() const
{
    return Singleton<WorldSettings>::getInstance().getApplicationSettings().getWindowWidth();
}
long MainWindow::getWindowHeight() const
{
    return Singleton<WorldSettings>::getInstance().getApplicationSettings().getWindowHeight();
}
const std::tstring MainWindow::getWindowTitle() const
{
    return Singleton<WorldSettings>::getInstance().getApplicationSettings().getWindowTitle();
}
const std::tstring MainWindow::getWindowClassName() const
{
    return Singleton<WorldSettings>::getInstance().getApplicationSettings().getWindowTitle();
}

问题是,每当我使用 CreateWindowEx(...) 创建主窗口时,WndProc 函数中的强制转换总是失败。我已经将"this"指针作为CreateWindowEx(...)函数的最后一个参数,以便从WndProc函数内的GetWindowLongPtr(...)方法中检索它。这样,我可以调用类本身的自定义事件处理方法,以使用更"面向对象"的设计。

如果有人能看到我在这里做错了什么,我很想听听。提前感谢您阅读这篇长文。

亲切问候。迪罗尼克斯。

我找到了解决问题的方法。与其强制转换为模板类,我可以将 void 指针强制转换为基类的类型,因为每个类都会从它继承正确的方法,无论如何都会调用正确的方法

LRESULT CALLBACK IWindow<T>::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (uMsg == WM_CREATE)
    {
        // if the message is WM_CREATE, the lParam contains a pointer to a CREATESTRUCT
        // the CREATESTRUCT contains the "this" pointer from the CreateWindow method
        // the "this" pointer of our app is stored in the createstruct pcs->lpCreateParams
        CREATESTRUCT *pCS = (CREATESTRUCT*)lParam;
        SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG)pCS->lpCreateParams);
    }
    else
    {
        //retrieve the stored "this" pointer
        LONG value = GetWindowLongPtr(hWnd, GWLP_USERDATA);
        IWindow* window = (IWindow*)(GetWindowLongPtr(hWnd, GWLP_USERDATA));
        if (window != nullptr)
            return window->handleEvent(hWnd, uMsg, wParam, lParam);
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

所以实际上我不是 100% 确定,但模板类不是必需的,可以删除。我首先认为我需要为每个新类创建一个不同的实例才能使 WndProc 工作,但我认为您只需要一个 WndProc/应用程序,它将捕获来自所有活动窗口的所有窗口消息。

无论如何,如果是这种情况,您可以删除模板类,如果不是,我可以保留它。 该解决方案适用于这两种方法。