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

时间:2016-07-08 10:59:59

标签: c++ templates winapi

我开始研究一个免费项目,在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 <gl\gl.h>
#include <gl\glu.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 By\nYour 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(...)方法中检索它。这样我就可以调用类本身的自定义事件处理方法,以使用更“面向对象”的设计。

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

亲切的问候。 Dyronix。

1 个答案:

答案 0 :(得分:0)

我找到了解决问题的方法。 我可以将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 / Application并且它将捕获来自所有活动窗口的所有窗口消息。 / p>

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