I have created a basic class that creates a win32 application. I only want windows to handle events I specify in WindowProc(). My goal is to place an openGL window/renderer inside that (win32) window, that also has it's own event system. I have managed to get that running but it crashes, so I have removed the code for now. I only want a single instance of the win32 window class, but I am allowed to create one as you can see. Is there a way other than Singleton approach?

Can I please ask an experienced programmer if s/he would look over my code and see if I have made any mistakes. I have placed comments OakMain.cpp and OakTree.cpp and hope you can advise.

OakMain.cpp
Code:
#include "OakTree.hpp"


int main()
{
    oak::OakTree OakApp;
    oak::OakTree OakApp1;  // *** This works, see comment below... ***


    while (OakApp.IsRunning()) {
        OakApp.ProcessWinMessages();
        // OakApp1.ProcessWinMessages();  // *** How is this allowed? Don't see window tho? ***
    }


    return 0;
}
OakTree.hpp
Code:
#ifndef OAKTREE_OAKTREE_HPP
#define OAKTREE_OAKTREE_HPP


#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
constexpr int OAK_PLATFORM_WINDOWS = 1;
#else
#error "Windows OS only supported at this time."
#endif


#include <Windows.h>


namespace oak
{
    class OakTree
    {
    public:
        OakTree();
        ~OakTree();


        void ProcessWinMessages();
        bool IsRunning() const { return m_IsRunning; }
        HWND GetWindowHandle() const { return m_window; }


    private:
        WNDCLASSEX m_wc{ 0 };
        HWND m_window{ nullptr };
        bool m_IsRunning{ false };
    };


}  // namespace oak


#endif  // OAKTREE_OAKTREE_HPP
OakTree.cpp
Code:
#include "OakTree.hpp"
#include <iostream>


static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);


oak::OakTree::OakTree()
{
    std::cout << "In OakTree() C'tor.\n";


    m_wc.cbSize = sizeof(WNDCLASSEX);
    m_wc.style = CS_HREDRAW | CS_VREDRAW;
    m_wc.lpfnWndProc = WindowProc;
    m_wc.hInstance = GetModuleHandleW(nullptr);
    m_wc.lpszClassName = L"OakTree";
    m_wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    m_wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
    m_wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
    m_wc.hIconSm = LoadIconW(NULL, IDI_APPLICATION);


    if (!RegisterClassExW(&m_wc)) { return; }


    m_window = CreateWindowExW(
        0, m_wc.lpszClassName, L"OakTree",
        WS_OVERLAPPEDWINDOW | WS_MAXIMIZE | WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        nullptr, nullptr, GetModuleHandleW(nullptr), nullptr
    );


    if (m_window == nullptr) { return; }


    ShowWindow(m_window, SW_SHOWMAXIMIZED);
    //UpdateWindow(m_window);  // *** Do I need this? Works ok without? ***


    m_IsRunning = true;
}


oak::OakTree::~OakTree()
{
    std::cout << "In OakTree() D'tor.\n";
}


void oak::OakTree::ProcessWinMessages()
{
    MSG msg = { 0 };
    while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
        if (msg.message == WM_QUIT) {
            m_IsRunning = false;
            break;
        }
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
}


LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
    case WM_DESTROY: {
        PostQuitMessage(0);
        return 0;
    }


    /*
    case WM_PAINT: {     // *** Not sure if this is needed? ***
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);


        // All painting occurs here, between BeginPaint and EndPaint.


        FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
        EndPaint(hwnd, &ps);
        return 0;
    }
    */
    }


    return DefWindowProcW(hwnd, msg, wParam, lParam);
}