3

My application populates some panels by means of incoming messages, using the SendStructMessage() function in Message.hpp.

SendStructMessage() need a valid windows handle to send to.

I have encapsulated the SendStrucMessage() in a function, like this:

bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
    if(!Application || !Application->MainForm || !Application->MainForm->Handle)
    {
        Log(lError) << "Failed to get a valid handle when trying to send application message";
        return false;
    }
    HWND h = Application->MainForm->Handle;

    AppMessageStruct data;
    data.mMessageEnum = msgID;
    data.mData = s;

    LRESULT res =  SendStructMessage(h, UWM_MESSAGE, 0, &data);
    if(res)
    {
        Log(lError) << "Sending message: "<<msgID<<" was unsuccesful";
        return false;
    }

    return true;
}

Trying to call this from either the MainForm's OnShow or OnCreate event doesn't work, as in either case the Application->MainForm->Handle is still NULL.

My question is, in a VCL application's startup phase, where can one be sure that the Application->MainForm->Handle is actually created?

Currently I kick off a Timer checking for a valid handle:

void __fastcall TMain::WaitForHandleTimerTimer(TObject *Sender)
{
    if(Application->MainForm->Handle)
    {
        WaitForHandleTimer->Enabled = false;

        //Send a message to main ui to update sequence shortcuts
        if(sendAppMessage(abSequencerUpdate) != true)
        {
            Log(lDebug)<<"Sending sequencer update to UI was unsuccesful";
        }
    }
}

Is there a better way?

1
  • 1
    This question and it's comments (as well as the answer) may help. While it's tagged Delphi, the VCL is entirely written in that language and the same information applies. The comments to the question contain some other links that are relevant here and should be extremely useful. Commented Aug 15, 2017 at 20:57

1 Answer 1

1

The TWinControl::Handle property getter creates a new HWND at the time the property is read, if an HWND hasn't already been created yet. If an error occurs while creating the HWND, an exception will be thrown.

So, your !Handle condition will always be false, because the Handle property can never return NULL (the WindowHandle property can, though).

bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
    if (!((Application) && (Application->MainForm)))
    {
        Log(lError) << "Failed to get a valid handle when trying to send application message";
        return false;
    }

    AppMessageStruct data;
    data.mMessageEnum = msgID;
    data.mData = s;

    LRESULT res = SendStructMessage(Application->MainForm->Handle, UWM_MESSAGE, 0, &data);
    if (res)
    {
        Log(lError) << "Sending message: " << msgID << " was unsuccesful";
        return false;
    }

    return true;
}

If you want to check if the Handle has been created without actually creating it, use the Form's HandleAllocated() method:

bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
    if (!((Application) && (Application->MainForm) && (Application->MainForm->HandleAllocated())))
    {
        Log(lError) << "Failed to get a valid handle when trying to send application message";
        return false;
    }

    AppMessageStruct data;
    data.mMessageEnum = msgID;
    data.mData = s;

    LRESULT res = SendStructMessage(Application->MainForm->Handle, UWM_MESSAGE, 0, &data);
    if (res)
    {
        Log(lError) << "Sending message: " << msgID << " was unsuccesful";
        return false;
    }

    return true;
}

Otherwise, don't use SendMessage()/SendStructMessage() at all. You can call the Form's Perform() method instead, which will deliver the message directly to the Form's assigned WindowProc without requiring any HWND at all:

bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
    if (!((Application) && (Application->MainForm))
    {
        Log(lError) << "Failed to get a valid handle when trying to send application message";
        return false;
    }

    AppMessageStruct data;
    data.mMessageEnum = msgID;
    data.mData = s;

    LRESULT res = Application->MainForm->Perform(UWM_MESSAGE, 0, (LPARAM)&data);
    if (res)
    {
        Log(lError) << "Sending message: " << msgID << " was unsuccesful";
        return false;
    }

    return true;
}

Alternatively, consider removing the MainForm dependency from sendAppMessage(). You can send to Application->Handle instead, and then have the MainForm register a callback method using Application->HookMainWindow().

bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
    if (!((Application) && (Application->Handle))
    {
        Log(lError) << "Failed to get a valid handle when trying to send application message";
        return false;
    }

    AppMessageStruct data;
    data.mMessageEnum = msgID;
    data.mData = s;

    LRESULT res = SendStructMessage(Application->Handle, UWM_MESSAGE, 0, &data);
    if (res)
    {
        Log(lError) << "Sending message: " << msgID << " was unsuccesful";
        return false;
    }

    return true;
}

__fastcall TMainForm::TMainForm(TComponent *Owner)
    : TForm(Owner)
{
    Application->HookMainWindow(&AppMessage);
}

__fastcall TMainForm::~TMainForm()
{
    Application->UnhookMainWindow(&AppMessage);
}

bool __fastcall TMainForm::AppMessage(TMessage &Message)
{
    if (Message.Msg == UWM_MESSAGE)
    {
        WindowProc(Message);
        return true;
    }
    return false;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Proposed alternative messaging mechanisms seem really valuable! Especially the Perform method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.