Passing TMessage as a one-liner

35 views Asked by At

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?

1

There are 1 answers

3
Remy Lebeau On BEST ANSWER

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);