c++winapiwindowhwndcreatewindow

Why does the window handle not get stored properly?


First you get a MCWE. You know probably better than me how to use it but I would recommend to build a C++ project from scratch and add it then. It uses Unicode character set, make sure to use windows as the SubSystem.

#include <vector>
#include <windows.h>
#include <windowsx.h>

#using <System.dll>

const int CANVAS_HOR_OFFSET = 10;
const int CANVAS_VER_OFFSET = 10;
const int CANVAS_WIDTH = 640;
const int CANVAS_HEIGTH = 480;

const int OUTPUT_VER_OFFSET = 10;
const int OUTPUT_HEIGTH = 120;

static class UiObjects
{
public:
    enum ObjectClass { Custom };

    struct Object {
        LPWSTR lpClassName;
        LPWSTR lpWindowName;
        DWORD dwStyle;
        LONG x;
        LONG y;
        LONG nWidth;
        LONG nHeigth;
        HMENU hMenu;
        HWND hObjectWnd;
        ObjectClass objectType;
        COLORREF color;
    };

    Object Canvas, Output;

    std::vector<Object> Objects;

    UiObjects();
} MyObjects;

UiObjects::UiObjects() {
    Canvas = {
        L"myVirtualSandbox Canvas Class",
        L"Canvas",
        WS_VISIBLE | WS_CHILD | WS_BORDER,
        CANVAS_HOR_OFFSET,
        CANVAS_VER_OFFSET,
        CANVAS_WIDTH,
        CANVAS_HEIGTH,
        NULL,
        NULL,
        ObjectClass::Custom
    };
    Objects.push_back(Canvas);

    Output = {
        L"EDIT",
        L"Output",
        WS_VISIBLE | WS_CHILD | WS_BORDER,
        CANVAS_HOR_OFFSET,
        CANVAS_VER_OFFSET + CANVAS_HEIGTH + OUTPUT_VER_OFFSET,
        CANVAS_WIDTH,
        OUTPUT_HEIGTH,
        NULL,
        NULL,
        ObjectClass::Custom
    };
    Objects.push_back(Output);
}

LRESULT CALLBACK wndProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

LRESULT CALLBACK canvasWndProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

int __stdcall wWinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR lpCmdLine,
    _In_ int nShowCmd
) {
    const wchar_t CLASS_NAME[] = L"myVirtualSandbox Window Class";

    HWND hApplicationWnd;
    MSG msg = { };
    // A window class defines a set of behaviors that several windows 
    // might have in common. Every window must be associated with a 
    // window class. To register a window class, fill in a "WNDCLASS" 
    // structure and call "RegisterClass" function afterwards.
    WNDCLASS applicationWndClass = { };

    applicationWndClass.lpfnWndProc = wndProc;
    applicationWndClass.hInstance = hInstance;
    applicationWndClass.lpszClassName = CLASS_NAME;

    RegisterClass(&applicationWndClass);

    // Create the window.
    hApplicationWnd = CreateWindow(
        CLASS_NAME,                     // Window class
        L"Learn to Program Windows",    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style
        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
    );

    if (hApplicationWnd == NULL)
    {
        return 0;
    }

    ShowWindow(hApplicationWnd, nShowCmd);

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

    return 0;
}

LRESULT CALLBACK wndProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
) {
    switch (uMsg)
    {
    case WM_CREATE:
    {
        HINSTANCE hInstance;
        WNDCLASS canvasWndClass;

        hInstance = (HINSTANCE)GetModuleHandle(NULL);

        canvasWndClass = { };

        canvasWndClass.lpfnWndProc = canvasWndProc;
        canvasWndClass.hInstance = hInstance;
        canvasWndClass.lpszClassName = L"myVirtualSandbox Canvas Class";

        RegisterClass(&canvasWndClass);

        // The following loop iterates through the container of user 
        // interface objects.
        for (int i = 0; i < MyObjects.Objects.size(); i++) {
            UiObjects::Object& Object = MyObjects.Objects[i];

            HWND hObjectWnd;

            hObjectWnd = CreateWindow(
                Object.lpClassName, // Window class
                Object.lpWindowName,    // Window text
                Object.dwStyle, // Styles 
                Object.x, // Horizontal position 
                Object.y, // Vertical position 
                Object.nWidth, // Width
                Object.nHeigth, // Height
                hWnd, // Parent window
                Object.hMenu, // No menu.
                hInstance,
                NULL // Pointer not needed.
            );

            MyObjects.Objects[i].hObjectWnd = hObjectWnd;
        }

        break;
    }

    case WM_DESTROY:
    {
        PostQuitMessage(0);
        break;
    }

    // To paint the window the "WM_PAINT" message has to be 
    // received by the window. It is sent by either the program 
    // itself or the operating system.
    case WM_PAINT:
    {
        // The "rcPaint" member of the "PAINTSTRCUT" structure 
        // returns a "RECT" structure that specifies the upper 
        // left and lower right corners of the rectangle in which 
        // the painting is requested.
        PAINTSTRUCT paintStruct;
        HDC hDeviceContext;

        HBRUSH hBrush;

        hDeviceContext = BeginPaint(hWnd, &paintStruct);
        hBrush = CreateSolidBrush(RGB(66,66,66));

        // Paint application background.
        FillRect(
            hDeviceContext,
            &paintStruct.rcPaint,
            hBrush
        );

        DeleteObject(hBrush);

        EndPaint(hWnd, &paintStruct);

        break;
    }
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK canvasWndProc(
    HWND hCanvasWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
) {
    switch (uMsg)
    {

    case WM_LBUTTONDOWN:
    {
        POINT point = { };

        point.x = GET_X_LPARAM(lParam);
        point.y = GET_Y_LPARAM(lParam);

        System::Diagnostics::Debug::WriteLine("Clicked in Canvas.");

        HWND hOutputWnd = MyObjects.Output.hObjectWnd;

        // Check if hObjectWnd is a valid handle
        if (IsWindow(MyObjects.Output.hObjectWnd))
        {
            SendMessage(MyObjects.Output.hObjectWnd, WM_SETTEXT, NULL, (LPARAM)L"Test.");
        }
        else {
            System::Diagnostics::Debug::WriteLine("Is not..");
        }
        break;
    }
    }

    return DefWindowProc(hCanvasWnd, uMsg, wParam, lParam);
}

I create windows by iterating through Objects in the WM_CREATE message of the parent window.

I also register a custom window class to be able to have custom event handling on that object. In case of WM_LBUTTONDOWN of this "sub"-procedure I want to modify another child windows text by sending a message to it.

The Output is "Is not.." since the window handle is not passed properly but what am I doing wrong?

I tried to pass the variable as a pointer but that didn´t make a difference at all.


Solution

  • Inside your WM_CREATE loop, you are assigning the new HWND to a copy of the Object from the vector. You are not updating the original Object in the vector.

    Change this:

    UiObjects::Object Object = MyObjects.Objects[i];
    

    To this:

    UiObjects::Object& Object = MyObjects.Objects[i];
                     ^
    

    Also, note that Canvas, Output, etc are separate Objects than the ones stored in the Objects vector. So, Output.hObjectWnd etc are likewise not being updated by your WM_CREATE loop, either.


    UPDATE: Indeed, you are filling Canvas, Output, etc with data, and then pushing copies of those objects into your Objects vector. So, even if your WM_CREATE code were assigning the new HWNDs to the Objects elements correctly, Canvas, Output, etc would still not get the HWNDs correctly.

    To do what you are attempting, you need to change your Objects vector to hold Object* pointers instead of Object instances, eg:

    #include <vector>
    #include <windows.h>
    #include <windowsx.h>
    
    #using <System.dll>
    
    const int CANVAS_HOR_OFFSET = 10;
    const int CANVAS_VER_OFFSET = 10;
    const int CANVAS_WIDTH = 640;
    const int CANVAS_HEIGTH = 480;
    
    const int OUTPUT_VER_OFFSET = 10;
    const int OUTPUT_HEIGTH = 120;
    
    static class UiObjects
    {
    public:
        enum ObjectClass { Custom };
    
        struct Object {
            LPWSTR lpClassName;
            LPWSTR lpWindowName;
            DWORD dwStyle;
            LONG x;
            LONG y;
            LONG nWidth;
            LONG nHeigth;
            HMENU hMenu;
            HWND hObjectWnd;
            ObjectClass objectType;
            COLORREF color;
        };
    
        Object Canvas, Output;
    
        std::vector<Object*> Objects;
    
        UiObjects();
    } MyObjects;
    
    UiObjects::UiObjects() {
        Canvas = {
            L"myVirtualSandbox Canvas Class",
            L"Canvas",
            WS_VISIBLE | WS_CHILD | WS_BORDER,
            CANVAS_HOR_OFFSET,
            CANVAS_VER_OFFSET,
            CANVAS_WIDTH,
            CANVAS_HEIGTH,
            NULL,
            NULL,
            ObjectClass::Custom
        };
        Objects.push_back(&Canvas);
    
        Output = {
            L"EDIT",
            L"Output",
            WS_VISIBLE | WS_CHILD | WS_BORDER,
            CANVAS_HOR_OFFSET,
            CANVAS_VER_OFFSET + CANVAS_HEIGTH + OUTPUT_VER_OFFSET,
            CANVAS_WIDTH,
            OUTPUT_HEIGTH,
            NULL,
            NULL,
            ObjectClass::Custom
        };
        Objects.push_back(&Output);
    }
    
    LRESULT CALLBACK wndProc(
        HWND hWnd,
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
    );
    
    LRESULT CALLBACK canvasWndProc(
        HWND hWnd,
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
    );
    
    int __stdcall wWinMain(
        _In_ HINSTANCE hInstance,
        _In_opt_ HINSTANCE hPrevInstance,
        _In_ LPWSTR lpCmdLine,
        _In_ int nShowCmd
    ) {
        const wchar_t CLASS_NAME[] = L"myVirtualSandbox Window Class";
    
        // A window class defines a set of behaviors that several windows 
        // might have in common. Every window must be associated with a 
        // window class. To register a window class, fill in a "WNDCLASS" 
        // structure and call "RegisterClass" function afterwards.
        WNDCLASS applicationWndClass = { };
    
        applicationWndClass.lpfnWndProc = wndProc;
        applicationWndClass.hInstance = hInstance;
        applicationWndClass.lpszClassName = CLASS_NAME;
    
        if (!RegisterClass(&applicationWndClass))
        {
            return 0;
        }
    
        // Create the window.
        HWND hApplicationWnd = CreateWindow(
            CLASS_NAME,                     // Window class
            L"Learn to Program Windows",    // Window text
            WS_OVERLAPPEDWINDOW,            // Window style
            // Size and position
            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
            NULL,       // Parent window    
            NULL,       // Menu
            hInstance,  // Instance handle
            NULL        // Additional application data
        );
    
        if (hApplicationWnd == NULL)
        {
            return 0;
        }
    
        ShowWindow(hApplicationWnd, nShowCmd);
    
        MSG msg = { };
        while (GetMessage(&msg, NULL, 0, 0) > 0)
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return 0;
    }
    
    LRESULT CALLBACK wndProc(
        HWND hWnd,
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
    ) {
        switch (uMsg)
        {
        case WM_CREATE:
        {
            HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
    
            WNDCLASS canvasWndClass = { };
            canvasWndClass.lpfnWndProc = canvasWndProc;
            canvasWndClass.hInstance = hInstance;
            canvasWndClass.lpszClassName = L"myVirtualSandbox Canvas Class";
    
            RegisterClass(&canvasWndClass);
    
            // The following loop iterates through the container of user 
            // interface objects.
            for (int i = 0; i < MyObjects.Objects.size(); ++i) {
                UiObjects::Object* pObject = MyObjects.Objects[i];
    
                pObject->hObjectWnd = CreateWindow(
                    pObject->lpClassName, // Window class
                    pObject->lpWindowName,    // Window text
                    pObject->dwStyle, // Styles 
                    pObject->x, // Horizontal position 
                    pObject->y, // Vertical position 
                    pObject->Width, // Width
                    pObject->nHeigth, // Height
                    hWnd, // Parent window
                    pObject->hMenu, // No menu.
                    hInstance,
                    NULL // Pointer not needed.
                );
            }
    
            break;
        }
    
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            break;
        }
    
        // To paint the window the "WM_PAINT" message has to be 
        // received by the window. It is sent by either the program 
        // itself or the operating system.
        case WM_PAINT:
        {
            // The "rcPaint" member of the "PAINTSTRCUT" structure 
            // returns a "RECT" structure that specifies the upper 
            // left and lower right corners of the rectangle in which 
            // the painting is requested.
            PAINTSTRUCT paintStruct = { };
    
            HDC hDeviceContext = BeginPaint(hWnd, &paintStruct);
            HBRUSH hBrush = CreateSolidBrush(RGB(66,66,66));
    
            // Paint application background.
            FillRect(
                hDeviceContext,
                &paintStruct.rcPaint,
                hBrush
            );
    
            DeleteObject(hBrush);
    
            EndPaint(hWnd, &paintStruct);
    
            break;
        }
        }
    
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    
    LRESULT CALLBACK canvasWndProc(
        HWND hCanvasWnd,
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam
    ) {
        switch (uMsg)
        {
    
        case WM_LBUTTONDOWN:
        {
            POINT point = { };
    
            point.x = GET_X_LPARAM(lParam);
            point.y = GET_Y_LPARAM(lParam);
    
            System::Diagnostics::Debug::WriteLine("Clicked in Canvas.");
    
            HWND hOutputWnd = MyObjects.Output.hObjectWnd;
    
            // Check if hObjectWnd is a valid handle
            if (IsWindow(hOutputWnd))
            {
                SendMessage(hOutputWnd, WM_SETTEXT, NULL, (LPARAM)L"Test.");
            }
            else {
                System::Diagnostics::Debug::WriteLine("Is not..");
            }
            break;
        }
        }
    
        return DefWindowProc(hCanvasWnd, uMsg, wParam, lParam);
    }