Catching WM_POWERBROADCAST in a TWinControl child control

Viewed 52

I have a TWinControl that needs to catch WM_POWERBROADCAST messages, but they never seem to reach it despite adding the message handler to the control's VCL_MESSAGE_MAP. I've also tried a custom WndProc() and that also never receives these messages. Other messages are working fine.

I can catch the message successfully in the main form, but it's never passed to my controls.

BEGIN_MESSAGE_MAP
    VCL_MESSAGE_HANDLER(WM_PAINT, TMessage, WMPaint); // Works
    VCL_MESSAGE_HANDLER(WM_ERASEBKGND, TMessage, WMEraseBackground); // Works
    VCL_MESSAGE_HANDLER(WM_POWERBROADCAST, TMessage, WMPower); // Doesn't work!
END_MESSAGE_MAP(inherited);
1 Answers

WM_POWERBROADCAST is sent only to top-level windows, never to child windows. So, you have a few choices:

  • have your WinControl intercept the message that is sent to the hidden TApplication window by using the TApplication.HookMainWindow() method. Be sure to remove the hook when your WinControl is destroyed.
__fastcall TMyControl::TMyControl(TComponent *Owner)
    : TWinControl(Owner)
{
    Application->HookMainWindow(&AppHook);
}

__fastcall TMyControl::~TMyControl()
{
    Application->UnhookMainWindow(&AppHook);
}

bool __fastcall TMyControl::AppHook(TMessage &Message)
{
    if (Message.Msg == WM_POWERBROADCAST)
    {
        // ...
    }
    return false;
}
  • intercept the message that is sent to the TForm window, either by applying a MESSAGE_MAP to the Form class, or by overriding the Form's virtual WndProc() method, and then have the From forward the message to your WinControl.
BEGIN_MESSAGE_MAP
  ...
  VCL_MESSAGE_HANDLER(WM_POWERBROADCAST, TMessage, WMPowerBroadcast);
END_MESSAGE_MAP(inherited); 

...

void __fastcall TForm1::WMPowerBroadcast(TMessage &Message)
{
    inherited::Dispatch(&Message);
    MyControl->Perform(Message.Msg, Message.WParam, Message.LParam);
}

Or:

void __fastcall TForm1::WndProc(TMessage &Message)
{
    inherited::WndProc(Message);
    if (Message.Msg == WM_POWERBROADCAST)
        MyControl->Perform(Message.Msg, Message.WParam, Message.LParam);
}
  • have your WinControl create its own hidden top-level window by using the RTL's AllocateHWnd() function.
private:
    HWND FPowerWnd;
    void __fastcall PowerWndProc(TMessage &Message);

...

__fastcall TMyControl::TMyControl(TComponent *Owner)
    : TWinControl(Owner)
{
    FPowerWnd = AllocateHWnd(&PowerWndProc);
}

__fastcall TMyControl::~TMyControl()
{
    DeallocateHWnd(FPowerWnd);
}

void __fastcall TMyControl::PowerWndProc(TMessage &Message)
{
    if (Message.Msg == WM_POWERBROADCAST)
    {
        // ...
    }
    else
    {
        Message.Result = ::DefWindowProc(FPowerWnd, Message.Msg, Message.WParam, Message.LParam);
    }
}
Related