How to receive WM_POWERBROADCAST inside of a thread?

岁酱吖の 提交于 2019-12-20 02:43:15

问题


I've been beating my head for over a day now, going through tons of resources trying to figure out how to receive the WM_POWERBROADCAST Windows message from within a thread.

Currently, I am using AllocateHWnd(WndMethod) inside of a stand-alone component. When I create an instance of said component in a standard VCL Forms Application, everything works fine, and I receive the WM_POWERBROADCAST message every time, as needed.

However, when I create an instance of the very same component from within a TThread, I'm no longer receiving this particular Windows message. I'm receiving all kinds of other messages, just not this particular one.

In my searching for a solution, I've found many resources related to how a Windows Service requires some extra work in order to receive this message. But I'm not using a service, at least not yet. I've also found a couple people mention that a thread needs to have a message loop in order to receive this message, and I've implemented a thread from another answer here, but again, I never receive this particular message.

Below is the complete component how I'm receiving this message, which again works perfectly if this is in a VCL application's main thread. I'm guessing the main thread needs to receive this message and forward it into the thread.

How do I make this receive the WM_POWERBROADCAST message when inside of a TThread?

unit JD.Power.Monitor;

(*
  JD Power Monitor
  by Jerry Dodge

  Purpose: To monitor the current state of power on the computer, and trigger
  events when different power related changes occur.

  Component: TPowerMonitor
  - Create an instance of TPowerMonitor component
  - Choose desired power settings to get notified of using Settings property
  - Implement event handlers for those events you wish to monitor
  - Component automatically takes care of the rest of the work
*)

interface

uses
  System.Classes, System.SysUtils, System.Generics.Collections,
  Winapi.ActiveX, Winapi.Windows, Winapi.Messages;

type
  TPowerSetting = (psACDCPowerSource, psBatteryPercentage,
    psConsoleDisplayState, psGlobalUserPresence, psIdleBackgroundTask,
    psMonitorPower, psPowerSaving, psPowerSchemePersonality,
    psSessionDisplayStatus, psSessionUserPresence, psSystemAwayMode);
  TPowerSettings = set of TPowerSetting;

  TPowerSource = (poAC, poDC, poHot);

  TPowerDisplayState = (pdOff, pdOn, pdDimmed);

  TPowerUserPresence = (puPresent = 0, puInactive = 2);

  TPowerSavingStatus = (psSaverOff, psSaverOn);

  TPowerAwayMode = (paExiting, paEntering);

  TPowerPersonality = (ppHighPerformance, ppPowerSaver, ppAutomatic);

  TPowerMonitorSettingHandles = array[TPowerSetting] of HPOWERNOTIFY;

  TPowerQueryEndSessionEvent = procedure(Sender: TObject; var EndSession: Boolean) of object;

  TPowerEndSessionEvent = procedure(Sender: TObject) of object;

  TPowerSettingSourceChangeEvent = procedure(Sender: TObject;
    const Src: TPowerSource) of object;

  TPowerSettingBatteryPercentEvent = procedure(Sender: TObject;
    const Perc: Single) of object;

  TPowerSettingDisplayStateEvent = procedure(Sender: TObject;
    const State: TPowerDisplayState) of object;

  TPowerSettingUserPresenceEvent = procedure(Sender: TObject;
    const Presence: TPowerUserPresence) of object;

  TPowerSettingSavingEvent = procedure(Sender: TObject;
    const Status: TPowerSavingStatus) of object;

  TPowerAwayModeEvent = procedure(Sender: TObject;
    const Mode: TPowerAwayMode) of object;

  TPowerPersonalityEvent = procedure(Sender: TObject;
    const Personality: TPowerPersonality) of object;

  TPowerMonitor = class(TComponent)
  private
    FHandle: HWND;
    FSettingHandles: TPowerMonitorSettingHandles;
    FSettings: TPowerSettings;
    FBatteryPresent: Boolean;
    FOnQueryEndSession: TPowerQueryEndSessionEvent;
    FOnEndSession: TPowerEndSessionEvent;
    FOnPowerStatusChange: TNotifyEvent;
    FOnResumeAutomatic: TNotifyEvent;
    FOnResumeSuspend: TNotifyEvent;
    FOnSuspend: TNotifyEvent;
    FOnSourceChange: TPowerSettingSourceChangeEvent;
    FOnBatteryPercent: TPowerSettingBatteryPercentEvent;
    FOnConsoleDisplayState: TPowerSettingDisplayStateEvent;
    FOnGlobalUserPresence: TPowerSettingUserPresenceEvent;
    FOnIdleBackgroundTask: TNotifyEvent;
    FOnMonitorPower: TPowerSettingDisplayStateEvent;
    FOnPowerSavingStatus: TPowerSettingSavingEvent;
    FOnSessionDisplayState: TPowerSettingDisplayStateEvent;
    FOnSessionUserPresence: TPowerSettingUserPresenceEvent;
    FOnAwayMode: TPowerAwayModeEvent;
    FOnPersonality: TPowerPersonalityEvent;
    procedure UnregisterSettings;
    procedure RegisterSettings;
    procedure SetSettings(const Value: TPowerSettings);
  protected
    procedure HandlePowerSetting(const Val: PPowerBroadcastSetting);
    procedure WndMethod(var Msg: TMessage);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Settings: TPowerSettings read FSettings write SetSettings;
    property OnQueryEndSession: TPowerQueryEndSessionEvent
      read FOnQueryEndSession write FOnQueryEndSession;
    property OnEndSession: TPowerEndSessionEvent
      read FOnEndSession write FOnEndSession;
    property OnPowerStatusChange: TNotifyEvent
      read FOnPowerStatusChange write FOnPowerStatusChange;
    property OnResumeAutomatic: TNotifyEvent
      read FOnResumeAutomatic write FOnResumeAutomatic;
    property OnResumeSuspend: TNotifyEvent
      read FOnResumeSuspend write FOnResumeSuspend;
    property OnSuspend: TNotifyEvent
      read FOnSuspend write FOnSuspend;
    property OnSourceChange: TPowerSettingSourceChangeEvent
      read FOnSourceChange write FOnSourceChange;
    property OnBatteryPercent: TPowerSettingBatteryPercentEvent
      read FOnBatteryPercent write FOnBatteryPercent;
    property OnConsoleDisplayState: TPowerSettingDisplayStateEvent
      read FOnConsoleDisplayState write FOnConsoleDisplayState;
    property OnGlobalUserPresence: TPowerSettingUserPresenceEvent
      read FOnGlobalUserPresence write FOnGlobalUserPresence;
    property OnIdleBackgroundTask: TNotifyEvent
      read FOnIdleBackgroundTask write FOnIdleBackgroundTask;
    property OnMonitorPower: TPowerSettingDisplayStateEvent
      read FOnMonitorPower write FOnMonitorPower;
    property OnPowerSavingStatus: TPowerSettingSavingEvent
      read FOnPowerSavingStatus write FOnPowerSavingStatus;
    property OnSessionDisplayState: TPowerSettingDisplayStateEvent
      read FOnSessionDisplayState write FOnSessionDisplayState;
    property OnSessionUserPresence: TPowerSettingUserPresenceEvent
      read FOnSessionUserPresence write FOnSessionUserPresence;
    property OnAwayMode: TPowerAwayModeEvent
      read FOnAwayMode write FOnAwayMode;
    property OnPersonality: TPowerPersonalityEvent
      read FOnPersonality write FOnPersonality;
  end;

implementation

{ TPowerMonitor }

constructor TPowerMonitor.Create(AOwner: TComponent);
begin
  inherited;
  FBatteryPresent:= False;
  FHandle := AllocateHWnd(WndMethod);
end;

destructor TPowerMonitor.Destroy;
begin
  UnregisterSettings;
  DeallocateHWnd(FHandle);
  inherited;
end;

procedure TPowerMonitor.SetSettings(const Value: TPowerSettings);
begin
  UnregisterSettings;
  FSettings := Value;
  RegisterSettings;
end;

procedure TPowerMonitor.WndMethod(var Msg: TMessage);
var
  Handled: Boolean;
begin
  Handled := True;
  case Msg.Msg of
    WM_POWERBROADCAST: begin
      //TODO: Why is this never received when inside of a thread?
      case Msg.WParam of
        PBT_APMPOWERSTATUSCHANGE: begin
          //Power status has changed.
          if Assigned(FOnPowerStatusChange) then
            FOnPowerStatusChange(Self);
        end;
        PBT_APMRESUMEAUTOMATIC: begin
          //Operation is resuming automatically from a low-power state.
          //This message is sent every time the system resumes.
          if Assigned(FOnResumeAutomatic) then
            FOnResumeAutomatic(Self);
        end;
        PBT_APMRESUMESUSPEND: begin
          //Operation is resuming from a low-power state. This message
          //is sent after PBT_APMRESUMEAUTOMATIC if the resume is triggered
          //by user input, such as pressing a key.
          if Assigned(FOnResumeSuspend) then
            FOnResumeSuspend(Self);
        end;
        PBT_APMSUSPEND: begin
          //System is suspending operation.
          if Assigned(FOnSuspend) then
            FOnSuspend(Self);
        end;
        PBT_POWERSETTINGCHANGE: begin
          //A power setting change event has been received.
          HandlePowerSetting(PPowerBroadcastSetting(Msg.LParam));
        end;
        else begin

        end;
      end;
    end
    else Handled := False;
  end;
  if Handled then
    Msg.Result := 0
  else
    Msg.Result := DefWindowProc(FHandle, Msg.Msg,
      Msg.WParam, Msg.LParam);
end;

procedure TPowerMonitor.HandlePowerSetting(const Val: PPowerBroadcastSetting);
var
  Pers: TPowerPersonality;
  function ValAsDWORD: DWORD;
  begin
    Result:= DWORD(Val.Data[0]);
  end;
  function ValAsGUID: TGUID;
  begin
    Result:= StringToGUID('{00000000-0000-0000-0000-000000000000}'); //Default
    if SizeOf(TGUID) = Val.DataLength then begin
      Move(Val.Data, Result, Val.DataLength);
    end;
  end;
  function IsVal(G: String): Boolean;
  begin
    Result:= Assigned(Val);
    if Result then
      Result:= IsEqualGUID(StringToGUID(G), Val.PowerSetting);
  end;
  function IsValGuid(G: String): Boolean;
  begin
    Result:= Assigned(Val);
    if Result then
      Result:= IsEqualGUID(StringToGUID(G), ValAsGUID);
  end;
begin
  if IsVal('{5d3e9a59-e9D5-4b00-a6bd-ff34ff516548}') then begin
    //GUID_ACDC_POWER_SOURCE
    if Assigned(FOnSourceChange) then
      FOnSourceChange(Self, TPowerSource(ValAsDWORD));
  end else
  if IsVal('{a7ad8041-b45a-4cae-87a3-eecbb468a9e1}') then begin
    //GUID_BATTERY_PERCENTAGE_REMAINING
    //We assume that if we get this message, that there is a battery connected.
    //Otherwise if this never occurs, then a battery is not present.
    //TODO: How to handle if battery is detached and no longer present?
    FBatteryPresent:= True;
    if Assigned(FOnBatteryPercent) then
      FOnBatteryPercent(Self, ValAsDWORD);
  end else
  if IsVal('{6fe69556-704a-47a0-8f24-c28d936fda47}') then begin
    //GUID_CONSOLE_DISPLAY_STATE
    if Assigned(FOnConsoleDisplayState) then
      FOnConsoleDisplayState(Self, TPowerDisplayState(ValAsDWORD));
  end else
  if IsVal('{786E8A1D-B427-4344-9207-09E70BDCBEA9}') then begin
    //GUID_GLOBAL_USER_PRESENCE
    if Assigned(FOnGlobalUserPresence) then
      FOnGlobalUserPresence(Self, TPowerUserPresence(ValAsDWORD));
  end else
  if IsVal('{515c31d8-f734-163d-a0fd-11a08c91e8f1}') then begin
    //GUID_IDLE_BACKGROUND_TASK
    if Assigned(FOnIdleBackgroundTask) then
      FOnIdleBackgroundTask(Self);
  end else
  if IsVal('{02731015-4510-4526-99e6-e5a17ebd1aea}') then begin
    //GUID_MONITOR_POWER_ON
    if Assigned(FOnMonitorPower) then
      FOnMonitorPower(Self, TPowerDisplayState(ValAsDWORD));
  end else
  if IsVal('{E00958C0-C213-4ACE-AC77-FECCED2EEEA5}') then begin
    //GUID_POWER_SAVING_STATUS
    if Assigned(FOnPowerSavingStatus) then
      FOnPowerSavingStatus(Self, TPowerSavingStatus(ValAsDWORD));
  end else
  if IsVal('{245d8541-3943-4422-b025-13A784F679B7}') then begin
    //GUID_POWERSCHEME_PERSONALITY
    if IsValGuid('{8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c}') then begin
      Pers:= TPowerPersonality.ppHighPerformance;
    end else
    if IsValGuid('{a1841308-3541-4fab-bc81-f71556f20b4a}') then begin
      Pers:= TPowerPersonality.ppPowerSaver;
    end else
    if IsValGuid('{381b4222-f694-41f0-9685-ff5bb260df2e}') then begin
      Pers:= TPowerPersonality.ppAutomatic;
    end else begin
      //TODO: Handle unrecognized GUID
      Pers:= TPowerPersonality.ppAutomatic;
    end;
    if Assigned(FOnPersonality) then
      FOnPersonality(Self, Pers);
  end else
  if IsVal('{2B84C20E-AD23-4ddf-93DB-05FFBD7EFCA5}') then begin
    //GUID_SESSION_DISPLAY_STATUS
    if Assigned(FOnSessionDisplayState) then
      FOnSessionDisplayState(Self, TPowerDisplayState(ValAsDWORD));
  end else
  if IsVal('{3C0F4548-C03F-4c4d-B9F2-237EDE686376}') then begin
    //GUID_SESSION_USER_PRESENCE
    if Assigned(FOnSessionUserPresence) then
      FOnSessionUserPresence(Self, TPowerUserPresence(ValAsDWORD));
  end else
  if IsVal('{98a7f580-01f7-48aa-9c0f-44352c29e5C0}') then begin
    //GUID_SYSTEM_AWAYMODE
    if Assigned(FOnAwayMode) then
      FOnAwayMode(Self, TPowerAwayMode(ValAsDWORD));
  end else begin
    //TODO: Handle Unrecognized GUID
  end;
end;

function PowerSettingGUID(const Setting: TPowerSetting): TGUID;
begin
  case Setting of
    psACDCPowerSource: Result:= StringToGUID('{5d3e9a59-e9D5-4b00-a6bd-ff34ff516548}');
    psBatteryPercentage: Result:= StringToGUID('{a7ad8041-b45a-4cae-87a3-eecbb468a9e1}');
    psConsoleDisplayState: Result:= StringToGUID('{6fe69556-704a-47a0-8f24-c28d936fda47}');
    psGlobalUserPresence: Result:= StringToGUID('{786E8A1D-B427-4344-9207-09E70BDCBEA9}');
    psIdleBackgroundTask: Result:= StringToGUID('{515c31d8-f734-163d-a0fd-11a08c91e8f1}');
    psMonitorPower: Result:= StringToGUID('{02731015-4510-4526-99e6-e5a17ebd1aea}');
    psPowerSaving: Result:= StringToGUID('{E00958C0-C213-4ACE-AC77-FECCED2EEEA5}');
    psPowerSchemePersonality: Result:= StringToGUID('{245d8541-3943-4422-b025-13A784F679B7}');
    psSessionDisplayStatus: Result:= StringToGUID('{2B84C20E-AD23-4ddf-93DB-05FFBD7EFCA5}');
    psSessionUserPresence: Result:= StringToGUID('{3C0F4548-C03F-4c4d-B9F2-237EDE686376}');
    psSystemAwayMode: Result:= StringToGUID('{98a7f580-01f7-48aa-9c0f-44352c29e5C0}');
  end;
end;

procedure TPowerMonitor.RegisterSettings;
var
  V: TPowerSetting;
begin
  for V := Low(TPowerSetting) to High(TPowerSetting) do begin
    if V in FSettings then begin
      FSettingHandles[V]:= RegisterPowerSettingNotification(FHandle,
        PowerSettingGUID(V), 0);
    end;
  end;
end;

procedure TPowerMonitor.UnregisterSettings;
var
  V: TPowerSetting;
begin
  for V := Low(TPowerSetting) to High(TPowerSetting) do begin
    if V in FSettings then begin
      UnregisterPowerSettingNotification(FSettingHandles[V]);
    end;
  end;
end;

end.

Further, based on the other answer as mentioned, here's how I'm attempting to capture this message using only a thread, although I'm sure this is not actually what I need since WM_POWERBROADCAST is not a posted message:

unit JD.ThreadTest;

interface

uses
  System.Classes, Winapi.Messages, Winapi.Windows;

type
  TDataThread = class(TThread)
  private
    FTitle: String;
    FWnd: HWND;
    FWndClass: WNDCLASS;
    procedure HandlePower(AMsg: TMsg);
  protected
    procedure Execute; override;
    procedure DoTerminate; override;
  public
    constructor Create(const Title: String); reintroduce;
  end;

implementation

constructor TDataThread.Create(const Title: String);
begin
  inherited Create(True);
  FTitle := Title;
  with FWndClass do begin
    Style := 0;
    lpfnWndProc := @DefWindowProc;
    cbClsExtra := 0;
    cbWndExtra := 0;
    hInstance := HInstance;
    hIcon := 0;
    hCursor := LoadCursor(0, IDC_ARROW);
    hbrBackground := COLOR_WINDOW;
    lpszMenuName := nil;
    lpszClassName := PChar(Self.ClassName);
  end;
end;

procedure TDataThread.Execute;
var
  Msg: TMsg;
begin
  if Winapi.Windows.RegisterClass(FWndClass) = 0 then Exit;
  FWnd := CreateWindow(FWndClass.lpszClassName, PChar(FTitle), WS_DLGFRAME,
    0, 0, 698, 517, 0, 0, HInstance, nil);
  if FWnd = 0 then Exit;
  while GetMessage(Msg, FWnd, 0, 0) = True do begin
    if Terminated then Break;
    case Msg.message of
      WM_POWERBROADCAST: begin
        HandlePower(Msg); //Never receives this message
      end;
      else begin
        TranslateMessage(msg);
        DispatchMessage(msg)
      end;
    end;
  end;
end;

procedure TDataThread.HandlePower(AMsg: TMsg);
begin

end;

procedure TDataThread.DoTerminate;
begin
  if FWnd <> 0 then DestroyWindow(FWnd);
  Winapi.Windows.UnregisterClass(PChar(Self.ClassName), FWndClass.hInstance);
  inherited;
end;

end.

PS: The end goal is to make this component re-usable, and to be using it inside of a thread, which will be spawned inside of a service.


EDIT

Just to show some perspective, here's a screenshot of my results, when I put my computer into sleep mode. The form on the left is my 100% working UI application, without any worker thread. It receives many messages through the WM_POWERBROADCAST message. The one on the right is where I attempt to capture this message inside of a thread - updated with the code below in Remy's answer.

Obviously the "Power Setting" specific messages are not received, because I haven't called RegisterPowerSettingNotification - but there are other cases when I should still receive this message regardless, such as PBT_APMSUSPEND.

unit JD.ThreadTest;

interface

uses
  System.Classes, System.SysUtils, Winapi.Messages, Winapi.Windows;

type

  TMessageEvent = procedure(Sender: TObject; Message: TMessage) of object;

  TDataThread = class(TThread)
  private
    FTitle: String;
    FWnd: HWND;
    FWndClass: WNDCLASS;
    FOnMessage: TMessageEvent;
    FMsg: TMessage;
    procedure HandleMessage(var Message: TMessage);
  protected
    procedure Execute; override;
    procedure DoTerminate; override;
    procedure DoOnMessage;
  public
    constructor Create(const Title: String); reintroduce;
    property OnMessage: TMessageEvent read FOnMessage write FOnMessage;
  end;

implementation

function DataThreadWndProc(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
  Thread: TDataThread;
  Message: TMessage;
begin
  if Msg = WM_NCCREATE then
  begin
    Thread := TDataThread(PCREATESTRUCT(lParam)^.lpCreateParams);
    SetWindowLongPtr(Wnd, GWLP_USERDATA, LONG_PTR(Thread));
  end else
    Thread := TDataThread(GetWindowLongPtr(Wnd, GWLP_USERDATA));

  if Thread <> nil then
  begin
    Message.Msg := Msg;
    Message.WParam := wParam;
    Message.LParam := lParam;
    Message.Result := 0;
    Thread.HandleMessage(Message);
    Result := Message.Result;
  end else
    Result := DefWindowProc(Wnd, Msg, wParam, lParam);
end;

constructor TDataThread.Create(const Title: String);
begin
  inherited Create(True);
  FTitle := Title;
  with FWndClass do
  begin
    Style := 0;
    lpfnWndProc := @DataThreadWndProc;
    cbClsExtra := 0;
    cbWndExtra := 0;
    hInstance := HInstance;
    hIcon := 0;
    hCursor := LoadCursor(0, IDC_ARROW);
    hbrBackground := COLOR_WINDOW;
    lpszMenuName := nil;
    lpszClassName := 'TDataThread';
  end;
end;

procedure TDataThread.Execute;
var
  Msg: TMsg;
begin
  if Winapi.Windows.RegisterClass(FWndClass) = 0 then Exit;
  FWnd := CreateWindow(FWndClass.lpszClassName, PChar(FTitle), WS_DLGFRAME, 0, 0, 698, 517, 0, 0, HInstance, Self);
  if FWnd = 0 then Exit;
  while GetMessage(Msg, 0, 0, 0) do
  begin
    if Terminated then Exit;
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;
end;

procedure TDataThread.DoOnMessage;
begin
  if Assigned(FOnMessage) then
    FOnMessage(Self, FMsg);
end;

procedure TDataThread.DoTerminate;
begin
  if FWnd <> 0 then DestroyWindow(FWnd);
  Winapi.Windows.UnregisterClass(FWndClass.lpszClassName, HInstance);
  inherited;
end;

procedure TDataThread.HandleMessage(var Message: TMessage);
begin
  FMsg:= Message;
  Synchronize(DoOnMessage);
  case Message.Msg of
    WM_POWERBROADCAST:
    begin

    end;
  else
    Message.Result := DefWindowProc(FWnd, Message.Msg, Message.WParam, Message.LParam);
  end;
end;

end.

回答1:


WM_POWERBROADCAST is not a posted message, so your message loop will never see it. You need a window procedure to receive that message. Your thread code is using DefWindowProc() directly as the window procedure. Change the call to RegisterClass() to register a custom procedure instead, that then calls DefWindowProc() for unhandled messages. GetMessage() will dispatch any sent message directly to the window procedure, and DispatchMessage() will dispatch any posted messages to the same window procedure.

unit JD.ThreadTest;

interface

uses
  System.Classes, Winapi.Messages, Winapi.Windows;

type
  TDataThread = class(TThread)
  private
    FTitle: String;
    FWnd: HWND;
    FWndClass: WNDCLASS;
    procedure HandleMessage(var Message: TMessage);
  protected
    procedure Execute; override;
    procedure DoTerminate; override;
  public
   constructor Create(const Title: String); reintroduce;
  end;

implementation

function DataThreadWndProc(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
  Thread: TDataThread;
  Message: TMessage;
begin
  if Msg = WM_NCCREATE then
  begin
    Thread := TDataThread(PCREATESTRUCT(lParam)^.lpCreateParams);
    SetWindowLongPtr(Wnd, GWLP_USERDATA, LONG_PTR(Thread));
  end else
    Thread := TDataThread(GetWindowLongPtr(Wnd, GWLP_USERDATA));

  if Thread <> nil then
  begin
    Message.Msg := Msg;
    Message.WParam := wParam;
    Message.LParam := lParam;
    Message.Result := 0;
    Thread.HandleMessage(Message);
    Result := Message.Result;
  end else
    Result := DefWindowProc(Wnd, Msg, wParam, lParam);
end;

constructor TDataThread.Create(const Title: String);
begin
  inherited Create(True);
  FTitle := Title;
  with FWndClass do
  begin
    Style := 0;
    lpfnWndProc := @DataThreadWndProc;
    cbClsExtra := 0;
    cbWndExtra := 0;
    hInstance := HInstance;
    hIcon := 0;
    hCursor := LoadCursor(0, IDC_ARROW);
    hbrBackground := COLOR_WINDOW;
    lpszMenuName := nil;
    lpszClassName := 'TDataThread';
  end;
end;

procedure TDataThread.Execute;
var
  Msg: TMsg;
begin
  if Winapi.Windows.RegisterClass(FWndClass) = 0 then Exit;
  FWnd := CreateWindow(FWndClass.lpszClassName, PChar(FTitle), WS_OVERLAPPED, 0, 0, 0, 0, 0, 0, HInstance, Self);
  if FWnd = 0 then Exit;
  while GetMessage(Msg, 0, 0, 0) do
  begin
    if Terminated then Exit;
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;
end;

procedure TDataThread.DoTerminate;
begin
  if FWnd <> 0 then DestroyWindow(FWnd);
  Winapi.Windows.UnregisterClass(FWndClass.lpszClassName, HInstance);
  inherited;
end;

procedure TDataThread.HandleMessage(var Message: TMessage);
begin
  case Message.Msg of
    WM_POWERBROADCAST:
    begin
      // ...
    end;
  else
    Message.Result := DefWindowProc(FWnd, Message.Msg, Message.WParam, Message.LParam);
  end;
end;

end.


来源:https://stackoverflow.com/questions/42739404/how-to-receive-wm-powerbroadcast-inside-of-a-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!