Служба EXE не получает события питания

Я создал сервис exe, который будет автоматически запускаться при загрузке.

Я использую этот пример для создания сервиса: http://www.cromis.net/blog/2011/04/how-to-make-a-very-small-windows-service-executable/

Работает довольно хорошо. Служба должна "контролировать" состояние ПК, например, чтобы проверить, подключен ли ПК к источнику питания или нет. В случае изменения, например, от подключенного питания к аккумулятору или низкого уровня заряда аккумулятора, он отправит экстренное электронное письмо о критическом состоянии устройства.

Это работает довольно хорошо, когда работает как обычный exe, но не как служба. Цель состоит в том, чтобы иметь возможность делать это в любом состоянии компьютера (вошел в систему или нет), поэтому важно работать как служба.

Я создал дескриптор окна для получения сообщения WM_POWERBROADCAST, например:

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; 

Для инициализации я использую это:

FHwnd:=AllocateHWnd(wndProc);

Поскольку я знаю о состоянии 0-изоляции при работе в качестве службы, я изменил пример кода функции RegisterService() следующим образом:

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;

Но без какого-либо успеха. Я также использую поток для опроса сообщений с помощью функции Windows API getMessage() из окна, но результаты совпадают.

Что я могу сделать, чтобы ловить события электростанции? Это довольно странно, что сервисы не могут реагировать на изменения состояния или нет?

1 ответ

Когда служба использует SERVICE_ACCEPT_POWEREVENT служба должна использовать HandlerEx() обратный звонок через RegisterServiceCtrlHandlerEx() получить SERVICE_CONTROL_POWEREVENT уведомления от СКМ. Тем не менее, пример кода, на который вы ссылались (а также собственный Delphi TService рамки) использует устаревшие Handler() обратный звонок через RegisterServiceCtrlHandler() вместо этого, который не получает такие уведомления. Так что вам нужно обновить код, чтобы использовать HandlerEx() Обратный звонок вместо.

В качестве альтернативы, чтобы получать уведомления о мощности без использования обратного вызова SCM, взгляните на RegisterPowerSettingNotification() или же PowerSettingRegisterNotification(), который может отправлять уведомления о мощности в HWND или функцию обратного вызова, соответственно.

Другие вопросы по тегам