如何使用 RegisterPowerSettingNotification

How to use RegisterPowerSettingNotification

我想在我的计算机电源发生变化时得到通知。

首先,我创建了一个简单的 Delphi 应用程序并监听 WM_POWERBROADCAST 在主窗体。

WM_POWERBROADCAST

type
  TForm38 = class(TForm)
  public
    procedure WM_POWERBROADCAST(var Msg: TMessage); message WM_POWERBROADCAST;
  end;

implementation

procedure TForm38.WM_POWERBROADCAST(var Msg: TMessage);
begin
  Caption := Msg.LParam.ToString;
end;

然后我收到了通知,但是 Msg.LParam 总是 0(零)

然后我尝试调用 RegisterPowerSettingNotification and found an example in this old SO Question,但我仍然遇到同样的问题:Msg.LParam 始终为 0(零)

注册电源设置通知

type
  TForm38 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FHPOWERNOTIFY: HPOWERNOTIFY;
  public
    { Public declarations }
    procedure WM_POWERBROADCAST(var Msg: TMessage); message WM_POWERBROADCAST;
  end;

implementation

const
  GUID_ACDC_POWER_SOURCE: TGUID = '{5D3E9A59-E9D5-4B00-A6BD-FF34FF516548}';

procedure TForm38.FormCreate(Sender: TObject);
begin
  FHPOWERNOTIFY := RegisterPowerSettingNotification(Handle, GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);
end;

procedure TForm38.FormDestroy(Sender: TObject);
begin
  UnregisterPowerSettingNotification(FHPOWERNOTIFY);
end;

procedure TForm38.WM_POWERBROADCAST(var Msg: TMessage);
begin
  Caption := Msg.LParam.ToString;
end;

应用程序 运行 Windows 10。

我做错了什么?

结果

使用这个问题的答案中的代码,我最终写了这个 class:

unit PowerWatcherU;

interface

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

{$M+}

type
  TPowerSource = (PoAc = 0, PoDc = 1, PoHot = 2);
  TPowerSourceChanged = procedure(const PowerSource: TPowerSource) of object;

  TPowerWatcher = class(TComponent)
  private
    FMyHWND: HWND;
    FHPOWERNOTIFY: HPOWERNOTIFY;
    FOnPowerSourceChanged: TPowerSourceChanged;
    procedure DoPowerSourceChanged(const Value: TPowerSource);
    procedure WndHandler(var Msg: TMessage);
    procedure SetOnPowerSourceChanged(const Value: TPowerSourceChanged);
  published
    property OnPowerSourceChanged: TPowerSourceChanged read FOnPowerSourceChanged write SetOnPowerSourceChanged;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

const
  GUID_ACDC_POWER_SOURCE: TGUID = '{5D3E9A59-E9D5-4B00-A6BD-FF34FF516548}';

implementation

uses
  System.SysUtils;

{ TPowerWatcher }

constructor TPowerWatcher.Create;
begin
  inherited;
  FMyHWND := AllocateHWND(WndHandler);
  FHPOWERNOTIFY := RegisterPowerSettingNotification(FMyHWND, GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);
end;

destructor TPowerWatcher.Destroy;
begin
  DeallocateHWND(FMyHWND);
  UnregisterPowerSettingNotification(FHPOWERNOTIFY);
  inherited;
end;

procedure TPowerWatcher.DoPowerSourceChanged(const Value: TPowerSource);
begin
  if Assigned(FOnPowerSourceChanged) then
    FOnPowerSourceChanged(Value);
end;

procedure TPowerWatcher.SetOnPowerSourceChanged(const Value: TPowerSourceChanged);
begin
  FOnPowerSourceChanged := Value;
end;

procedure TPowerWatcher.WndHandler(var Msg: TMessage);
begin
  if (Msg.Msg = WM_POWERBROADCAST) and (Msg.WParam = PBT_POWERSETTINGCHANGE) then
  begin
    if PPowerBroadcastSetting(Msg.LParam)^.PowerSetting = GUID_ACDC_POWER_SOURCE then
      DoPowerSourceChanged(TPowerSource(PPowerBroadcastSetting(Msg.LParam)^.Data[0]));
  end
  else
    Msg.Result := DefWindowProc(FMyHWND, Msg.Msg, Msg.WParam, Msg.LParam);
end;

end.

您可能正在遭受 window 重新创建的困扰。您发布的代码对我来说工作正常,但在 Win10 中可能并非如此。除此之外,唯一奇怪的是您通过命名方法 WM_POWERBROADCAST 来复制标识符,尽管这不应该导致代码中断。使用专用 HWND 的工作示例:

unit Unit1;

interface

uses
  Windows, SysUtils, Classes, Forms, StdCtrls, Vcl.Controls, Vcl.ExtCtrls,
  Messages;

type

  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    private
      FMyHWND : HWND;
      FHPowerNotify: HPOWERNOTIFY;
    public
      procedure WndHandler(var Msg: TMessage);
 end;

var
  Form1: TForm1;

implementation
{$R *.dfm}

const
  GUID_ACDC_POWER_SOURCE: TGUID = '{5D3E9A59-E9D5-4B00-A6BD-FF34FF516548}';

procedure TForm1.FormCreate(Sender: TObject);
begin
  FMyHWND := AllocateHWND(WndHandler);
  FHPowerNotify := RegisterPowerSettingNotification(FMyHWND,
                                                    GUID_ACDC_POWER_SOURCE, 
                                                    DEVICE_NOTIFY_WINDOW_HANDLE);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  UnregisterPowerSettingNotification(FHPowerNotify);
  DeallocateHWND(FMyHWND);
end;

procedure TForm1.WndHandler(var Msg: TMessage);
begin
  if (Msg.Msg = WM_POWERBROADCAST) and
     (Msg.WParam = PBT_POWERSETTINGCHANGE) then
  begin
    if PPowerBroadcastSetting(Msg.LParam)^.PowerSetting = GUID_ACDC_POWER_SOURCE then
      case cardinal(PPowerBroadcastSetting(Msg.LParam)^.Data[0]) of
        0: Caption := 'AC Power';
        1: Caption := 'DC Power';
        2: Caption := 'HOT - UPS, etc';
      end;
  end else
    msg.Result := DefWindowProc(FMyHWND, Msg.Msg, Msg.WParam, Msg.LParam);
end;

end.