WM_COMMAND catch button press in c++ win32

10,852

Child windows (i.e. windows with the WS_CHILD window style) are identified by a unique numeric value, often called control ID or window ID. It is passed to the parent when it receives a WM_COMMAND message, for example. You never assigned a control ID to your button controls, though, and the parent window cannot identify them. In case of a child window, the hMenu parameter in the call to CreateWindow is overloaded to carry the unique identifier:

hMenu
For a child window, hMenu specifies the child-window identifier, an integer value used by a dialog box control to notify its parent about events. The application determines the child-window identifier; it must be unique for all child windows with the same parent window.

In other words, your application picks a numeric value to assign to controls. Since the lower IDs are used by the dialog manager already (e.g. IDOK), it is common practice to start assigning control IDs starting at 100 (see Why do dialog editors start assigning control IDs with 100?).

In your WM_COMMAND handler you can then compare LOWORD(wParam) to the identifier assigned to your button controls.

You need to apply the following changes to your code.

// Declare control IDs. This is usually done in a file called Resource.h
#define IDC_SELECT_VIDEO (100)

Change your window creation code:

SelectVideoBTN = CreateWindow(
            L"BUTTON",  // Predefined class; Unicode assumed 
            L"Select Video's",      // Button text 
            WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // Styles 
            10,         // x position 
            460,        // y position 
            100,        // Button width
            25,         // Button height
            hWnd,       // Parent window
            (HMENU)IDC_SELECT_VIDEO, // Assign appropriate control ID
            (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
            NULL);      // Pointer not needed.

Check for the control ID in your WM_COMMAND handler:

    switch (message)
    {
    case WM_COMMAND:
        if (LOWORD(wParam) == IDC_SELECT_VIDEO) {
            loader::alert("hello");
        }
        break;
    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }


If your window procedure isn't called at all, this could mean that you aren't dispatching messages on the calling thread. A GUI thread always needs a message loop. The standard message loop suffices:
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
Share:
10,852
Gerwin
Author by

Gerwin

Updated on June 04, 2022

Comments

  • Gerwin
    Gerwin almost 2 years

    I'm trying to get the button press event in c++ win32 using WM_Command

    HWND hBtn;
    HWND  hBtnParent = HWND("UploadVideo");
    HWND SelectVideoBTN, UploadBTN;
    HWND hWnd;
    
    HINSTANCE hUpload;
    WNDCLASSEX wcexUpload;
    int nCmdShowUpload = 1;
    using namespace std;
    
    LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
    LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        loader::alert("rrr");
        switch (message)
        {
        case WM_COMMAND:
            if (LOWORD(wParam) == WORD(SelectVideoBTN)) {
                loader::alert("hello");
            }
            break;
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
        }
        return 0;
    }
    
    SelectVideoBTN = CreateWindow(
                L"BUTTON",  // Predefined class; Unicode assumed 
                L"Select Video's",      // Button text 
                WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // Styles 
                10,         // x position 
                460,         // y position 
                100,        // Button width
                25,        // Button height
                hWnd,     // Parent window
                NULL,       // No menu.
                (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
                NULL);      // Pointer not needed.
    
            UploadBTN = CreateWindow(
                L"BUTTON",  // Predefined class; Unicode assumed 
                L"Upload",      // Button text 
                WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // Styles 
                390,         // x position 
                460,         // y position 
                100,        // Button width
                25,        // Button height
                hWnd,     // Parent window
                NULL,       // No menu.
                (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
                NULL);      // Pointer not needed.
    

    I've been looking at this example - http://forums.devshed.com/programming-42/create-button-clicked-148407.html - but I can't quite get it to work, it won't even call the CALLBACK WindowProcedure - is there anyone who could help me?

    The buttons are present on the window I've created, I create the window by doing -

    WNDCLASSEX vidUploader;
    
        vidUploader.cbSize = sizeof(WNDCLASSEX);
    
        vidUploader.style = CS_HREDRAW | CS_VREDRAW;
        vidUploader.lpfnWndProc = WndProc;
        vidUploader.cbClsExtra = 0;
        vidUploader.cbWndExtra = 0;
        vidUploader.hInstance = hUpload;
        vidUploader.hIcon = LoadIcon(hUpload, MAKEINTRESOURCE(IDI_P2GOVIDEOUPLOADER20));
        vidUploader.hCursor = LoadCursor(NULL, IDC_ARROW);
        vidUploader.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        vidUploader.lpszMenuName = MAKEINTRESOURCE(IDC_P2GOVIDEOUPLOADER20);
        vidUploader.lpszClassName = (LPCWSTR)(L"UploadVideo");
        vidUploader.hIconSm = LoadIcon(wcexUpload.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
        RegisterClassEx(&vidUploader);
    
        hInst = hUpload; // Store instance handle in our global variable
    

    and then to create the window

        hWnd = CreateWindow((LPCWSTR)(L"UploadVideo"), (LPCWSTR)(L"Upload Video's"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hUpload, NULL);
    if (!hWnd)
            {
                MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Win32 Guided Tour"), NULL);
    
                return 1;
            }
    
    
        // The parameters to ShowWindow explained:
        // hWnd: the value returned from CreateWindow
        // nCmdShow: the fourth parameter from WinMain
        ShowWindow(hWnd,
            nCmdShowUpload);
    
        UpdateWindow(hWnd);
    
  • Gerwin
    Gerwin about 8 years
    I've done what you said, but when I put the .dll file into the program (I'm creating a plugin) the window is generated on load, but when I click on the buttons they don't work, I've edited my question
  • IInspectable
    IInspectable about 8 years
    @Gerwin: Please don't change a question in a way, that invalidates a published answer. I reverted your edits to the initial version. If you have a new question, click the Ask Question button. If you want to add more information, you can also edit your question if it doesn't change the core issue.
  • Gerwin
    Gerwin about 8 years
    Alright, thank you for the information I just have 1 issue, I can't use a message loop, because it would make the program stuck in the loop & not load the rest of the program, as I'm creating a plugin
  • Jonathan Potter
    Jonathan Potter about 8 years
    Then the thing that loads your plugin needs to run a message loop.
  • Gerwin
    Gerwin about 8 years
    @JonathanPotter there's no alternative method?
  • Jonathan Potter
    Jonathan Potter about 8 years
    You can't have a window without a message loop. Something has to run it. You could try using a separate thread I guess but that could get messy.
  • Gerwin
    Gerwin about 8 years
    @JonathanPotter the developers recommended creating a seperate thread, I'll give that a shot, thank you :)
  • Remy Lebeau
    Remy Lebeau about 8 years
    Just FYI, using a Control ID is not the only way to identify a child control. WM_COMMAND and WM_NOTIFY messages that are sent to a parent window carry the child control's HWND as well, eg: case WM_COMMAND: if (HWND(lParam) == SelectVideoBTN) { ... }