Search code examples
c++buildersendmessagewindows-messages

Passing TMessage as a one-liner


I use C++ Builder and I pass messages to window which are handled in WndProc to update user interface. Something like:

struct TWmGuiUpdatedStruct
    {
    int id1;
    int id2;
    };

TWmGuiUpdatedStruct gui { 1, 2 };
    
TMessage msg;
msg.Message = WM_MY_GUI_UPDATED;
msg.WParam = WM_MY_GUI_UPDATED_MESSAGE_LIST;
msg.LParam = reinterpret_cast<NativeInt>(&guiparam);

// send to all forms in a loop
for (int i = 0; i < Screen->FormCount; i++) Screen->Forms[i]->Perform(msg.Message, msg.WParam, msg.LParam); 
// in WndProc
if (fMessage.Msg == WM_MY_GUI_UPDATED && msg.WParam == WM_MY_GUI_UPDATED_MESSAGE_LIST)
    {
    TWmGuiUpdatedStruct* gui = reinterpret_cast<TWmGuiUpdatedStruct*>(fMessage.LParam);
    // use gui->id1 and guid->id2 here...
    fMessage.Result = 0;
    return;
    }
TForm::WndProc(fMessage);

I find it over-complicated to initialize all in so many lines.

I need something simpler, like:

TMessage msg { WM_MY_GUI_UPDATED, {WM_MY_GUI_UPDATED_MESSAGE_LIST,  reinterpret_cast<NativeInt>(&guiparam), 0} };

But it doesn't compile - it wants System::Word instead.

Is there a simpler way to initialize this to pass such GUI update messages?


Solution

  • If you look at the declaration of TMessage, it is a struct containing a union holding 2 structs.

    struct DECLSPEC_DRECORD TMessage
    {
    public:
        unsigned Msg;
    public:
        union
        {
            struct
            {
                System::Word WParamLo;
                System::Word WParamHi;
                System::Word LParamLo;
                System::Word LParamHi;
                System::Word ResultLo;
                System::Word ResultHi;
            };
            struct
            {
                NativeUInt WParam;
                NativeInt LParam;
                NativeInt Result;
            };
        };
    };
    

    Per cppreference, Struct and union initialization explains that when dealing with a "nested initialization", you can only initialize the first member of a union, but you are trying to initialize the second member.

    Since C++Builder does not support designated initializers at this time, you will have to do something like this instead:

    WPARAM wParam = WM_MY_GUI_UPDATED_MESSAGE_LIST;
    LPARAM lParam = reinterpret_cast<NativeInt>(&guiparam);
    
    TMessage msg { WM_MY_GUI_UPDATED, { { LOWORD(wParam), HIWORD(wParam), LOWORD(lParam), HIWORD(lParam), 0, 0 } } };
    

    With that said, I would suggest simply getting rid of the TMessage altogether, you don't actually need it:

    Screen->Forms[i]->Perform(WM_MY_GUI_UPDATED, WM_MY_GUI_UPDATED_MESSAGE_LIST, reinterpret_cast<LPARAM>(&guiparam));
    

    Otherwise, if you must use TMessage, then I would suggest creating a wrapper function to create a new TMessage, eg:

    TMessage MakeTMessage(unsigned Msg, WPARAM wParam, LPARAM lParam)
    {
        TMessage msg;
        msg.Msg = Msg;
        msg.WParam = wParam;
        msg.LParam = lParam;
        msg.Result = 0;
        return msg;
    }
    

    And then call a control's WindowProc() instead of Perform(), eg:

    TMessage msg = MakeTMessage(WM_MY_GUI_UPDATED, WM_MY_GUI_UPDATED_MESSAGE_LIST, reinterpret_cast<LPARAM>(&guiparam));
    ...
    Screen->Forms[i]->WindowProc(msg);