Search code examples
delphiguidwindows-messages

How to get TGUID from the GUID_POWERSCHEME_PERSONALITY setting?


I've been working on wrapping the full WM_POWERBROADCAST Windows Message, and encapsulating it inside of an event driven component. A majority of it is working fine, and I have also further captured all of the PBT_POWERSETTINGCHANGE setting GUID's - except for one GUID_POWERSCHEME_PERSONALITY.

According to the docs, I am supposed to translate the Data member to a TGUID...

The active power scheme personality has changed. All power schemes map to one of these personalities. The Data member is a GUID that indicates the new active power scheme personality.

The problem is with the data type provided is as follows:

Data: packed array[0..0] of UCHAR;

How do I acquire a TGUID out of this Data member?

Here's how I am currently capturing the rest of these settings, upon capturing the PBT_POWERSETTINGCHANGE event...

procedure TPowerMonitor.HandlePowerSetting(const Val: PPowerBroadcastSetting);
var
  VDWord: DWORD;
  function IsVal(G: String): Boolean;
  begin
    Result:= Assigned(Val);
    if Result then
      Result:= IsEqualGUID(StringToGUID(G), Val.PowerSetting);
  end;
begin
  if IsVal('{5d3e9a59-e9D5-4b00-a6bd-ff34ff516548}') then begin
    //GUID_ACDC_POWER_SOURCE
    VDWord:= DWORD(Val.Data[0]);
    if Assigned(Self.FOnSourceChange) then
      FOnSourceChange(Self, TPowerSource(VDWord));
  end else
  if IsVal('{a7ad8041-b45a-4cae-87a3-eecbb468a9e1}') then begin
    //GUID_BATTERY_PERCENTAGE_REMAINING
    VDWord:= DWORD(Val.Data[0]);
    if Assigned(Self.FOnBatteryPercent) then
      FOnBatteryPercent(Self, VDWord);
  end else
  if IsVal('{6fe69556-704a-47a0-8f24-c28d936fda47}') then begin
    //GUID_CONSOLE_DISPLAY_STATE
    VDWord:= DWORD(Val.Data[0]);
    if Assigned(FOnConsoleDisplayState) then
      FOnConsoleDisplayState(Self, TPowerDisplayState(VDWord));
  end else
  if IsVal('{786E8A1D-B427-4344-9207-09E70BDCBEA9}') then begin
    //GUID_GLOBAL_USER_PRESENCE
    VDWord:= DWORD(Val.Data[0]);
    if Assigned(FOnGlobalUserPresence) then
      FOnGlobalUserPresence(Self, TPowerUserPresence(VDWord));
  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
    VDWord:= DWORD(Val.Data[0]);
    if Assigned(FOnMonitorPower) then
      FOnMonitorPower(Self, TPowerDisplayState(VDWord));
  end else
  if IsVal('{E00958C0-C213-4ACE-AC77-FECCED2EEEA5}') then begin
    //GUID_POWER_SAVING_STATUS
    VDWord:= DWORD(Val.Data[0]);
    if Assigned(FOnPowerSavingStatus) then
      FOnPowerSavingStatus(Self, TPowerSavingStatus(VDWord));
  end else
  if IsVal('{245d8541-3943-4422-b025-13A784F679B7}') then begin
    //GUID_POWERSCHEME_PERSONALITY
    //TODO: How to read "Data" as a TGUID?
    //GUID_MIN_POWER_SAVINGS (8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c)
    //GUID_MAX_POWER_SAVINGS (a1841308-3541-4fab-bc81-f71556f20b4a)
    //GUID_TYPICAL_POWER_SAVINGS (381b4222-f694-41f0-9685-ff5bb260df2e)
  end else
  if IsVal('{2B84C20E-AD23-4ddf-93DB-05FFBD7EFCA5}') then begin
    //GUID_SESSION_DISPLAY_STATUS
    VDWord:= DWORD(Val.Data[0]);
    if Assigned(FOnSessionDisplayState) then
      FOnSessionDisplayState(Self, TPowerDisplayState(VDWord));
  end else
  if IsVal('{3C0F4548-C03F-4c4d-B9F2-237EDE686376}') then begin
    //GUID_SESSION_USER_PRESENCE
    VDWord:= DWORD(Val.Data[0]);
    if Assigned(FOnSessionUserPresence) then
      FOnSessionUserPresence(Self, TPowerUserPresence(VDWord));
  end else
  if IsVal('{98a7f580-01f7-48aa-9c0f-44352c29e5C0}') then begin
    //GUID_SYSTEM_AWAYMODE
    VDWord:= DWORD(Val.Data[0]);
    if Assigned(FOnAwayMode) then
      FOnAwayMode(Self, TPowerAwayMode(VDWord));
  end else begin
    //Unrecognized GUID
    //TODO: Handle error
  end;
end;

Solution

  • This is documented here:

    POWERBROADCAST_SETTING structure

    The struct is what is know as a "variable length structure". The final member is, potentially, longer than actually declared. The DataLength member tells you how much data is actually present in the Data member. For example, you can read it out with a call to System.Move().

    If the payload really is a GUID then check that DataLength is the size of a GUID and copy the data into your GUID variable.

    var
      GUID: TGUID;
    .... 
    // check that DataLength = SizeOf(TGUID) 
    Move(Data, GUID, DataLength);
    

    The data for other power scheme GUIDs is typically a DWORD, which is handled in an analogous fashion.