Search code examples
c++buildervcl

Catching WM_POWERBROADCAST in a TWinControl child control


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

Solution

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