Service exe does not receive power events

2.3k Views Asked by At

I have created a service exe that will autourun at boot.

I am using this example to create the service: http://www.cromis.net/blog/2011/04/how-to-make-a-very-small-windows-service-executable/

Works pretty well. The service needs to 'monitor' the state of the PC, for example to check if the PC is connected to a powersupply or not. In case of a change, for example from connected power to battery or low battery status, it will send an emergency email about the critical state of the device.

This works pretty well when running as a normal exe but not as a service. The goal is to be able to do this at any state of the pc (logged in or not), so it is essential to be run as a service.

I have created a window handle to receive the WM_POWERBROADCAST message, for example:

procedure TEventAlerter.wndProc(var Msg : TMessage);
var
  handled: Boolean;

begin
  log( 'wndProc processed - '+intToStr( Msg.Msg ));
  // Assume we handle message
  handled := TRUE;
  case( Msg.Msg ) of
    WM_POWERBROADCAST : begin
                         case( Msg.WParam ) of
                           PBT_APMPOWERSTATUSCHANGE : powerChangeEvent(Msg.WParam);
                           PBT_APMBATTERYLOW        : powerLowEvent(Msg.WParam);
                          else powerEvent(Msg.WParam);
                         end;
                        end;
    else handled:= FALSE;
  end;

  if( handled ) then
   begin
    // We handled message - record in message result
    Msg.Result := 0
   end
  else
   begin
    // We didn't handle message
    // pass to DefWindowProc and record result
    Msg.Result := DefWindowProc(fHWnd, Msg.Msg, Msg.WParam, Msg.LParam);
   end;
end; 

To initialize I am using this:

FHwnd:=AllocateHWnd(wndProc);

Because I am aware of 0-isolation state when running as a service, I changed some example code of RegisterService() function with this:

ServiceStatus.dwServiceType := SERVICE_WIN32_OWN_PROCESS or SERVICE_INTERACTIVE_PROCESS;
ServiceStatus.dwCurrentState := SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP or
                                    SERVICE_ACCEPT_PAUSE_CONTINUE or
                                    SERVICE_ACCEPT_POWEREVENT;

But without any success. I have also use a thread to poll messages with windows API function getMessage() from the window but the results are the same.

What can I do to catch powerstate events? It's kind of weird that services cannot react on powerstate changes or not?

1

There are 1 best solutions below

12
Remy Lebeau On

When a service uses SERVICE_ACCEPT_POWEREVENT, the service must use a HandlerEx() callback via RegisterServiceCtrlHandlerEx() to receive SERVICE_CONTROL_POWEREVENT notifications from the SCM. However, the example code you linked to (as well as Delphi's own TService framework) uses the outdated Handler() callback via RegisterServiceCtrlHandler() instead, which does not receive such notifications. So you need to update the code to use a HandlerEx() callback instead.

Alternatively, to receive power notifications without using an SCM callback, have a look at RegisterPowerSettingNotification() or PowerSettingRegisterNotification(), which can send power notifications to an HWND or callback function, respectively.