GWLP_USERDATA

昨日せっかくstdcall_thunkを作ったのですが、今回は使わないことにしました。
というのも、ウィンドウクラスは「クラス」であるはずなのに、サンクでインスタンスと結び付けてしまうと、「クラス」でなく「インスタンス」になってしまう気がしたからです。
ウィンドウクラスが「クラス」であることを守ろうとすると、ウィンドウのユーザーデータにインスタンス情報を格納するのが素直な実装だと思います。
この方法にもいろいろ制約があるのですが、今回のように単純な例なら特に問題ないでしょう。


以下、何のひねりもない書きかけコードです。

#include <cstring>
#include <stdexcept>

#include <windows.h>
#include <commctrl.h>

class main_window
{
public:
    explicit main_window(::HWND handle) : handle_(handle)
    {
    }

    ~main_window()
    {
    }

private:
    ::HWND handle_;
};

::LRESULT CALLBACK WindowProc(
    ::HWND hwnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam)
{
    try
    {
        main_window* pimpl =
            reinterpret_cast<main_window*>(
                GetWindowLongPtr(hwnd, GWLP_USERDATA)
            );

        if (uMsg == WM_CREATE)
        {
            pimpl = new main_window(hwnd);
            SetWindowLongPtr(
                hwnd, GWLP_USERDATA, reinterpret_cast< ::LONG_PTR>(pimpl));
        }
        else if (uMsg == WM_DESTROY)
        {
            SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
            delete pimpl;
            ::PostQuitMessage(0);
        }
    }
    catch (const std::exception& e)
    {
        ::MessageBoxA(0, e.what(), "Sound Player", MB_OK);
    }
    return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(::HINSTANCE hInstance, ::HINSTANCE, ::LPSTR, int nCmdShow)
{
    try
    {
        {
            ::INITCOMMONCONTROLSEX data;
            std::memset(&data, 0, sizeof(data));
            data.dwSize = sizeof(data);
            data.dwICC = ICC_BAR_CLASSES;
            if (::InitCommonControlsEx(&data) == FALSE)
                throw std::runtime_error("InitCommonControlsEx() failed");
        }

        ::WNDCLASSEXA wc;
        std::memset(&wc, 0, sizeof(wc));
        wc.cbSize = sizeof(wc);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = &WindowProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = sizeof(::LONG_PTR);
        wc.hInstance = hInstance;
        wc.hIcon = ::LoadIcon(0, IDI_APPLICATION);
        wc.hCursor = ::LoadCursor(0, IDC_ARROW);
        wc.hbrBackground =
            reinterpret_cast< ::HBRUSH>(::GetStockObject(WHITE_BRUSH));
        wc.lpszMenuName = 0;
        wc.lpszClassName = "MainWindow";
        wc.hIconSm = 0;

        ::ATOM cls = ::RegisterClassExA(&wc);
        if (cls == 0)
            throw std::runtime_error("RegisterClassExA() failed");

        ::HWND hwnd = ::CreateWindowEx(
            WS_EX_CLIENTEDGE, MAKEINTATOM(cls), "Sound Player",
            WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
            CW_USEDEFAULT, CW_USEDEFAULT, 280, 100, 0, 0, hInstance, 0
        );
        if (hwnd == 0)
            throw std::runtime_error("CreateWindowEx() failed");

        ::ShowWindow(hwnd, nCmdShow);
        ::UpdateWindow(hwnd);

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

        ::UnregisterClass(MAKEINTATOM(cls), hInstance);
        return (msg.wParam);
    }
    catch (const std::exception& e)
    {
        ::MessageBoxA(0, e.what(), "Sound Player", MB_OK);
    }
    return 0;
}

中途半端ですが、さっきまで熱出してひーひー言ってた割には進んだかなぁと思います。