Cannot create a window

Asked by At

I am trying to create a simple window but it does not work for some reason. I have actually tried this for the past few months. Even used some old code and it does not run at all for whatever reason.

Actually, the registerclass function does not return anything ever but this happened in the past too but I was still able to create a window. Nothing works this time

I tried:

  • Providing a valid value for all of the members of WNDCLASS
  • Running Debug/Release
  • Running as administrator
  • Using GetLastError (always returns 0)
  • Providing different class names, because I know some don't work for some reason, and also window names

This is my code:

WNDCLASSEXW lpClass = WNDCLASSEXW{ 0x00 };
lpClass.cbSize = sizeof(decltype(lpClass));
lpClass.style = (CS_HREDRAW | CS_VREDRAW);
lpClass.lpfnWndProc = ScreenProcess;
lpClass.hInstance = GetModuleHandleW(nullptr);
lpClass.lpszClassName = L"__TEST__";
lpClass.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));

RegisterClassExW(&lpClass);

if (HWND hwnd = CreateWindowExW(WS_EX_APPWINDOW, lpClass.lpszClassName, L"abc", WS_POPUP,
    CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, nullptr, nullptr, lpClass.hInstance, nullptr))
{
    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);
}

printf("%d", GetLastError());

for (;;) {};

3 Answers

2
Arush Agarampur On

Did you define your window procedure as something like this?

LRESULT CALLBACK ScreenProcess(
    HWND hWnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
)
{
    switch (msg)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }

    return 0;
}

And instead of the infinite loop, place this:

MSG msg{0};

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

Also, the cbSize of the WNDCLASSEXW should be sizeof(WNDCLASSEXW). Also change the CreateSolidBrush(RGB(0, 0, 0)); to GetStockObject(BLACK_BRUSH);

Hope this helps!

0
selbie On

Here's a modified, single source file version of the default Win32 project template that Visual Studio generates for you. I've just stripped out the stuff related to resources, accelerators, menus, and the About box thing.

#include <windows.h>

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[] = L"Test App";                  // The title bar text
WCHAR szWindowClass[] = L"__TestAppWindowClass__";            // the main window class name

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex = { 0 };

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszClassName = szWindowClass;
    return RegisterClassExW(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // Store instance handle in our global variable

    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // TODO: Add any drawing code that uses hdc here...
        EndPaint(hWnd, &ps);
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
1
Ted Lyngmo On

Here's a version of your code that's working in VS2017 (using C++17). I've added debug prints so that you can see ScreenProcess() receiving mouse movements etc. I've also added a class (WindowsClassRegistrator) for handling one of the resources you allocate to show how you can extend the existing C structs to handle releasing of resources automatically.

I added a mapping between common Windows messages and their macro names to make it easier to follow what you actually get into your WndProc. Unknown Windows messages are collected and displayed when you click close on the app (on the task bar) so you can extend the messages you'd like to handle/display as you go.

I also added assertion function templates for throwing exceptions with proper errors messages that you can use around all WinAPI functions that have an easy way of checking if they've succeeded or not.

#include "pch.h" // if you use precompiled headers
#include <Windows.h>
#include <Windowsx.h> // GET_X_LPARAM, GET_Y_LPARAM
#include <Olectl.h> // OCM_BASE
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <stdexcept>
#include <map>
#include <unordered_set>

// --- bug hunting support functions start ---
std::string GetLastErrorString() {
    DWORD le = GetLastError();
    LPSTR  lpBuffer = nullptr;

    if (FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        0, le, 0,
        reinterpret_cast<LPSTR>(&lpBuffer),
        0, NULL
    ))
    {
        std::string rv(lpBuffer);
        LocalFree(lpBuffer);
        return rv;
    }
    else return std::to_string(le);
}

struct win_error : public std::runtime_error {
    win_error(const std::string& prefix) :
        std::runtime_error(prefix + ": " + GetLastErrorString())
    {}
};

// assert that a function does NOT return a specific value
template<typename T>
inline T AssertNEQ(T value, const char* funcname, T got_value) {
    if (value == got_value) throw win_error(funcname);
    return got_value;
}

// assert that a function DOES return a specific value
template<typename T>
inline T AssertEQ(T value, const char* funcname, T got_value) {
    if (value != got_value) throw win_error(funcname);
    return got_value;
}
// --- bug hunting support functions end ---

class WindowsClassRegistrator : public WNDCLASSEXW {
    ATOM wca;

public:
    WindowsClassRegistrator(WNDPROC lpfnWndProc) :
        WNDCLASSEXW{ 0 }, wca{}
    {
        this->cbSize = sizeof(WNDCLASSEXW);
        this->style = CS_SAVEBITS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
        this->lpfnWndProc = lpfnWndProc;
        this->hInstance =
            AssertNEQ<HMODULE>(NULL, "GetModuleHandleW", GetModuleHandleW(nullptr));
        this->lpszClassName = L"__TEST__";
        this->hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
        this->wca = 
            AssertNEQ<ATOM>(NULL, "RegisterClassExW", RegisterClassExW(this));
    }
    WindowsClassRegistrator(const WindowsClassRegistrator&) = delete;
    WindowsClassRegistrator(WindowsClassRegistrator&&) = delete;
    WindowsClassRegistrator& operator=(const WindowsClassRegistrator&) = delete;
    WindowsClassRegistrator& operator=(WindowsClassRegistrator&&) = delete;
    ~WindowsClassRegistrator() {
        AssertNEQ<BOOL>(FALSE,
            "UnregisterClassW", UnregisterClassW(GetAtomAsStr(), this->hInstance));
    }

    inline LPCWSTR GetAtomAsStr() const noexcept {
        return reinterpret_cast<LPCWSTR>(this->wca);
    }
    inline HINSTANCE GetInstance() const noexcept {
        return this->hInstance;
    }
    inline LPCWSTR GetClassName() const noexcept {
        return this->lpszClassName;
    }
};

std::multimap<UINT, std::string> messages = {
    {WM_NULL, "WM_NULL"},
    {WM_CREATE, "WM_CREATE"},
    {WM_DESTROY, "WM_DESTROY"},
    {WM_MOVE, "WM_MOVE"},
    {WM_SIZE, "WM_SIZE"},
    {WM_ACTIVATE, "WM_ACTIVATE"},
    {WM_SETFOCUS, "WM_SETFOCUS"},
    {WM_KILLFOCUS, "WM_KILLFOCUS"},
    {WM_PAINT, "WM_PAINT"},
    {WM_CLOSE, "WM_CLOSE"},
    {WM_QUIT, "WM_QUIT"},
    {WM_ERASEBKGND, "WM_ERASEBKGND"},
    {WM_SHOWWINDOW, "WM_SHOWWINDOW"},
    {WM_ACTIVATEAPP, "WM_ACTIVATEAPP"},
    {WM_CANCELMODE, "WM_CANCELMODE"},
    {WM_SETCURSOR, "WM_SETCURSOR"},
    {WM_MOUSEACTIVATE, "WM_MOUSEACTIVATE"},
    {WM_VKEYTOITEM, "WM_VKEYTOITEM"},
    {WM_CHARTOITEM, "WM_CHARTOITEM"},
    {WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING"},
    {WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED"},
    {SPI_SETDRAGHEIGHT, "SPI_SETDRAGHEIGHT"},
    {WM_HELP, "WM_HELP"},
    {WM_CONTEXTMENU, "WM_CONTEXTMENU"},
    {WM_GETICON, "WM_GETICON"},
    {WM_NCCREATE, "WM_NCCREATE"},
    {WM_NCDESTROY, "WM_NCDESTROY"},
    {WM_NCCALCSIZE, "WM_NCCALCSIZE"},
    {WM_NCHITTEST, "WM_NCHITTEST"},
    {WM_NCPAINT, "WM_NCPAINT"},
    {WM_NCACTIVATE, "WM_NCACTIVATE"},
    {SPI_GETDOCKMOVING, "SPI_GETDOCKMOVING"},
    {WM_KEYDOWN, "WM_KEYDOWN"},
    {WM_KEYUP, "WM_KEYUP"},
    {WM_CHAR, "WM_CHAR"},
    {WM_SYSKEYDOWN, "WM_SYSKEYDOWN"},
    {WM_SYSKEYUP, "WM_SYSKEYUP"},
    {WM_SYSCHAR, "WM_SYSCHAR"},
    {WM_SYSCOMMAND, "WM_SYSCOMMAND"},
    {WM_MOUSEMOVE, "WM_MOUSEMOVE"},
    {WM_LBUTTONDOWN, "WM_LBUTTONDOWN"},
    {WM_LBUTTONUP, "WM_LBUTTONUP"},
    {WM_LBUTTONDBLCLK, "WM_LBUTTONDBLCLK"},
    {WM_RBUTTONDOWN, "WM_RBUTTONDOWN"},
    {WM_RBUTTONUP, "WM_RBUTTONUP"},
    {WM_RBUTTONDBLCLK, "WM_RBUTTONDBLCLK"},
    {WM_MBUTTONDOWN, "WM_MBUTTONDOWN"},
    {WM_MBUTTONUP, "WM_MBUTTONUP"},
    {WM_MBUTTONDBLCLK, "WM_MBUTTONDBLCLK"},
    {WM_MOUSEWHEEL, "WM_MOUSEWHEEL"},
    {WM_XBUTTONDOWN, "WM_XBUTTONDOWN"},
    {WM_XBUTTONUP, "WM_XBUTTONUP"},
    {WM_IME_SETCONTEXT, "WM_IME_SETCONTEXT"},
    {WM_IME_NOTIFY, "WM_IME_NOTIFY"},
    {WM_HOTKEY, "WM_HOTKEY"},
    {0x0313, ": https://stackoverflow.com/questions/10430377/winapi-undocumented-windows-message-0x0313-stable"},
    {WM_PRINT, "WM_PRINT"},
    {WM_APPCOMMAND, "WM_APPCOMMAND"},
};

std::unordered_set<UINT> unmapped_messages;

std::map<WPARAM, std::string> mk_down = {
    {MK_CONTROL, "MK_CONTROL"},
    {MK_LBUTTON,"MK_LBUTTON"},
    {MK_MBUTTON,"MK_MBUTTON"},
    {MK_RBUTTON,"MK_RBUTTON"},
    {MK_SHIFT,"MK_SHIFT"},
    {MK_XBUTTON1,"MK_XBUTTON1"},
    {MK_XBUTTON2,"MK_XBUTTON2"}
};

constexpr int colw = 40;
std::string message_maker(const char* macro, UINT uMsg, UINT offset) {
    std::stringstream ss;
    ss << macro << " + " << std::hex << (uMsg - offset) << " (" << uMsg << ")";
    return ss.str();
}

inline void DisplayMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) {
    std::string message;

    if (uMsg < WM_USER) {
        // there may be duplicate macros for some messages, so show all of them
        auto[rangefirst, rangelast] = messages.equal_range(uMsg);

        if (rangefirst == rangelast) {
            // unmapped message found, store it
            unmapped_messages.emplace(uMsg);
            rangefirst = messages.emplace(uMsg, ": " + std::to_string(uMsg) + " -- UNMAPPED MESSAGE");
            rangelast = rangefirst;
            ++rangelast;
        }
        message = rangefirst->second;
        while (++rangefirst != rangelast) message += " " + rangefirst->second;
    }
    else {
        // https://docs.microsoft.com/en-us/windows/desktop/winmsg/ocm--base
#define REGISTERED_WINDOWS_MESSAGE_BASE (0xC000)
#define SYSRESERVED_BASE                (0x10000)

        if (uMsg < OCM__BASE)
            message = message_maker("WM_USER", uMsg, WM_USER);
        else if (uMsg < WM_APP)
            message = message_maker("(WM_USER) OCM__BASE", uMsg, OCM__BASE);
        else if (uMsg < REGISTERED_WINDOWS_MESSAGE_BASE)
            message = message_maker("WM_APP", uMsg, WM_APP);
        else if (uMsg < SYSRESERVED_BASE)
            message = message_maker("Registered Window Message", uMsg, REGISTERED_WINDOWS_MESSAGE_BASE);
        else
            message = message_maker("Reserved by the system", uMsg, SYSRESERVED_BASE);
    }
    std::cout << std::setw(colw) << std::hex << message << std::setw(18) << wParam
        << std::setw(12) << lParam << "\n";
}

LRESULT CALLBACK ScreenProcess(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    static std::string old_mouse_message;

    switch (uMsg) {
    case WM_MOUSEMOVE:
        {
            std::stringstream ss;
            std::string new_mouse_message;
            int xPos, yPos;

            xPos = GET_X_LPARAM(lParam);
            yPos = GET_Y_LPARAM(lParam);
            ss << std::setw(colw) << "WM_MOUSEMOVE" << std::dec
               << " x=" << std::setw(6) << xPos << " y=" << std::setw(6) << yPos;

            for (auto&[wp, key] : mk_down)
                if (wp&wParam) ss << " " << key;

            new_mouse_message = ss.str();

            if (new_mouse_message != old_mouse_message) {
                std::cout << new_mouse_message << "\n";
                old_mouse_message = std::move(new_mouse_message);
            }
        }
        return 0;
    case WM_NCHITTEST:
        return HTCLIENT;
    case WM_SETCURSOR:
        return TRUE;
    case WM_DESTROY:
        std::cout << std::setw(colw) << "WM_DESTROY" << " ";
        PostQuitMessage(0);
        std::cout << "PostQuitMessage() done\n";
        return 0;
    default:
        DisplayMsg(uMsg, wParam, lParam);
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}    

int main() {
    try {
        WindowsClassRegistrator wcr(ScreenProcess);

        // use WS_VISIBLE so that you don't have to call ShowWindow()
        HWND hWnd =
            AssertNEQ<HWND>(NULL, "CreateWindowExW",
                CreateWindowExW(
                    WS_EX_APPWINDOW,
                    wcr.GetAtomAsStr(),
                    L"Title string",
                    WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 
                    CW_USEDEFAULT, // x
                    CW_USEDEFAULT, // y
                    0, // width
                    0, // height
                    nullptr,
                    nullptr,
                    wcr.GetInstance(),
                    nullptr
                )
            );

        MONITORINFO mi = { sizeof(mi) }; // mi.cbSize = sizeof(mi);
        AssertNEQ<BOOL>(FALSE, "GetMonitorInfo",
            GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY), &mi)
        );

        AssertNEQ<BOOL>(FALSE, "SetWindowPos",
            SetWindowPos(hWnd, HWND_TOP,
                mi.rcMonitor.left, mi.rcMonitor.top,
                (mi.rcMonitor.right - mi.rcMonitor.left) / 4, // 1/4 of the screen width
                (mi.rcMonitor.bottom - mi.rcMonitor.top), // height
                SWP_NOOWNERZORDER | SWP_FRAMECHANGED)
            );

        // paint a rectangle in the window
        AssertNEQ<BOOL>(FALSE, "Rectangle", Rectangle(
            AssertNEQ<HDC>(NULL, "GetDC", GetDC(hWnd)),
            10, 10, 100, 100)
        );

        MSG uMsg;
        while (AssertNEQ<BOOL>(-1, "GetMessage", GetMessage(&uMsg, nullptr, 0, 0))) {
            TranslateMessage(&uMsg); // assertion would depend on message type
            DispatchMessage(&uMsg); // assertion would depend on message type
        }
        DisplayMsg(uMsg.message, uMsg.wParam, uMsg.lParam); // WM_QUIT

        if (unmapped_messages.size()) {
            std::cout << "\nYou have collected unmapped messages: \n";

            for (const auto&[msg, text] : messages) {
                std::cout << "/* 0x" << std::setw(4) << std::setfill('0') << msg << " */ ";
                if (unmapped_messages.count(msg) || text[0] == ':') {
                    std::cout << "{0x" << std::setw(4) << std::setfill('0') << msg;
                }
                else {
                    std::cout << "{" << text;
                }
                std::cout << ", \"" << text << "\"},\n";
            }
        }

        return static_cast<int>(uMsg.wParam);
    }
    catch (const std::exception& ex) {
        std::cerr << "Exception: " << ex.what() << "\n";
    }
    return 1;
} // the registered windows class will be unregistered here, when wcr goes out of
  // scope