Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Перехват сообщений в сервисе


Автор: Mosian 17.7.2009, 12:17
Сервис написан без форм на APi
Необходимо получить системное сообщение об изменении конфигурации Windows (wm_devicechange)

В главном потоке сервиса подписываюсь на получение сообщений
Registerdevicenotification (handlService, @структура на то что хочу получать,  Flag);

Где flag = device_notify_window_handle если 1 параметр Окно
                   device_notify_service_handle  если 1 параметр сервис (мой случай).

Проблема первая (device_notify_service_handle) – пишет Undeclared , а (device_notify_window_handle) проходит объявление в Windows

ГДЕ ВЗЯТЬ device_notify_service_handle?

Проблема вторая, чем перехватывать эти сообщения? (Может создать поток и вызывать в нём GetMessage)
                              
Сильно не судите чайник.

Автор: Virtuals 17.7.2009, 13:01
Mosian, 
2. wm_devicechange сообщение оконное, соответственно и ловят его окна smile

Автор: kami 17.7.2009, 13:02
DEVICE_NOTIFY_SERVICE_HANDLE = 1 (гугл рулит).


Цитата(Mosian @  17.7.2009,  12:17 Найти цитируемый пост)
чем перехватывать эти сообщения?

Перекрыть DoCustomControl сервиса.
Только насколько я понял из других ссылок (пока искал значение DEVICE_NOTIFY_SERVICE_HANDLE), там не обойтись им, потому что в DoCustomControl передается только msg.WParam.

У себя я давно обошел это, если в сервис устанавливается на Win2k или более новую винду, то вызывается не RegisterServiceCtrlHandler, а RegisterServiceCtrlHandlerEx, что позволяет получать дополнительные уведомления от системы. Правда, для этого пришлось влезть в генофонд, ну да ладно - оно того стоило.
http://msdn.microsoft.com/en-us/library/ms683241(VS.85).aspx

Добавлено через 1 минуту и 35 секунд
Цитата(Virtuals @  17.7.2009,  13:01 Найти цитируемый пост)
wm_devicechange сообщение оконное, соответственно и ловят его окна

неправда ваша.

Добавлено через 4 минуты и 9 секунд
Вариант 2:
читаем внимательно http://msdn.microsoft.com/en-us/library/aa363431(VS.85).aspx:

Цитата

hRecipient [in] 
A handle to the window or service that will receive device events for the devices specified in the NotificationFilter parameter. The same window handle can be used in multiple calls to RegisterDeviceNotification. 

Services can specify either a window handle or service status handle.


Вывод: создаем в сервисе окошко (AllocateHWND) и пользуем его.

Автор: Mosian 17.7.2009, 14:14
Через AllocateHWND пробовал молчит окошко;
Возможно я неправильно передаю в RegisterServiceCtrlHandle  Handle сервиса
беру его из registerServiceCtrlHandler либо необходимо OpenService?
И где правильно вызывать RegisterServiceCtrlHandle в основном потоке либо без разницы

Автор: Mosian 17.7.2009, 15:42
Kami а где взять RegisterServiceCtrlHandlerEx

Автор: kami 17.7.2009, 17:20
Цитата(Mosian @  17.7.2009,  14:14 Найти цитируемый пост)
Через AllocateHWND пробовал молчит окошко;

и при этом указывал DEVICE_NOTIFY_WINDOW_HANDLE ?

Цитата(Mosian @  17.7.2009,  15:42 Найти цитируемый пост)
где взять RegisterServiceCtrlHandlerEx

Код

type
  TRegisterServiceCtrlHandlerEx=function (lpServiceName:PChar;
    lpHandlerProc: TFarProc; lpContext:DWORD):SERVICE_STATUS_HANDLE; stdcall;
  TRegisterServiceCtrlHandlerExW=function (lpServiceName:PWideChar;
    lpHandlerProc: TFarProc; lpContext:DWORD):SERVICE_STATUS_HANDLE; stdcall;

var
  advapi32:THandle;
  RegisterServiceCtrlHandlerEx:TRegisterServiceCtrlHandlerEx;
  //RegisterServiceCtrlHandlerExA:TRegisterServiceCtrlHandlerEx;
  //RegisterServiceCtrlHandlerExW:TRegisterServiceCtrlHandlerExW;

initialization
  advapi32 := LoadLibrary('advapi32.dll');
  if advapi32 <> 0 then
    begin
      RegisterServiceCtrlHandlerEx:=GetProcAddress(advapi32, 'RegisterServiceCtrlHandlerExA');
      //RegisterServiceCtrlHandlerExA:=GetProcAddress(advapi32, 'RegisterServiceCtrlHandlerExA');
      //RegisterServiceCtrlHandlerExW:=GetProcAddress(advapi32, 'RegisterServiceCtrlHandlerExW');
    end;
 ................
finalization
  FreeLibrary(advapi32);
................


Добавлено через 12 минут и 13 секунд
Цитата(Mosian @  17.7.2009,  14:14 Найти цитируемый пост)
Возможно я неправильно передаю в RegisterServiceCtrlHandle  Handle сервиса
беру его из registerServiceCtrlHandler

Читаем еще раз:
Цитата

RegisterDeviceNotification
hRecipient [in] 
A handle to the window or service that will receive device events for the devices specified in the NotificationFilter parameter. The same window handle can be used in multiple calls to RegisterDeviceNotification. 

Services can specify either a window handle or service status handle.

Смотрим, что возвращает RegisterServiceCtrlHandler:
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerEx(
  __in      LPCTSTR lpServiceName,
  __in      LPHANDLER_FUNCTION_EX lpHandlerProc,
  __in_opt  LPVOID lpContext
);

то есть, параметр передан правильно.
Теперь смотрим на
http://msdn.microsoft.com/en-us/library/ms683241(VS.85).aspx

Цитата

This parameter can also be one of the following extended control codes. Note that these control codes are not supported by the Handler function.

Control Code Meaning 
SERVICE_CONTROL_DEVICEEVENT

 
Вывод - если нужно получать не через оконную функцию, а через callback функцию сервиса, то Hander (который регистрируется через RegisterServiceCtrlHandler) ничего не получит, как бы ни старался, тогда только HandlerEx.

Автор: Mosian 20.7.2009, 14:32
Люди что я делаю не так HendlerEx не получает сообщения SERVICE_CONTROL_DEVICEEVENT да и другие доп. параметры введённые для данной функции.
Сервис должен реагировать на вкл. выкл. USB

Ниже я привёл код, где происходит рег. Функции обработчика сообщ.

Код

Type
    _DEV_BROADCAST_DEVICEINTERFACE = record
       dbcc_size:DWORD;
       dbcc_devicetype:DWORD;
       dbcc_reserved:DWORD;
       dbcc_classguid:TGUID;
       dbcc_name:array[0..0] of char;
   end;
   TDEV_BROADCAST_DEVICEINTERFACE = _DEV_BROADCAST_DEVICEINTERFACE;
   DBT_DEVTYP_DEVICEINTERFACE = 5;


//************ Далее в основном потоке сервиса пишу *******************

  SERV_STATUS.dwServiceType           := SERVICE_WIN32_OWN_PROCESS; 
  SERV_STATUS.dwCurrentState           := SERVICE_START_PENDING;     
  SERV_STATUS.dwControlsAccepted := SERVICE_ACCEPT_STOP;
  SERV_STATUS.dwWin32ExitCode    := NO_ERROR;
  SERV_STATUS.dwServiceSpecificExitCode := 0;
  SERV_STATUS.dwCheckPoint            := 0;
  SERV_STATUS.dwWaitHint                := 5000;


   SERV_Handle := RegisterServiceCtrlHandlerEx(c_ServiceName,@ServiceCtrlHandler,0); 
   //Регистрация проходит успешно и ServiceCtrlHandler реагирует на SERVICE_CONTROL_STOP

   if SERV_Handle <> 0 then
    begin
        SERV_STATUS.dwCurrentState :=SERVICE_RUNNING; 
        SERV_STATUS.dwCheckPoint   :=0;
        SERV_STATUS.dwWaitHint     :=0;
        SetServiceStatus(SERV_Handle,SERV_STATUS);    

        NF.dbcc_size:=sizeof(TDEV_BROADCAST_DEVICEINTERFACE);
        NF.dbcc_devicetype:=DBT_DEVTYP_DEVICEINTERFACE;
        RegDN := Registerdevicenotification(SERV_Handle,@NF,1 and 4);

        // RegDN вроде тоже успешна <>nil
    end


//****************** Сам обработчик ******************
//Он реагирует только на стандартные сообщения 

ServiceCtrlHandler(Opcode:Cardinal;dwEventType:DWORD;lpEventData:Pointer;lpContext:Pointer);stdcall;
 begin
   //ErrortoFile(inttostr(Opcode));
   case Opcode of
    SERVICE_CONTROL_CONTINUE:
      if SERV_STATUS.dwCurrentState <> SERVICE_RUNNING then
       begin
         SERV_STATUS.dwCurrentState := SERVICE_RUNNING;
         SERV_STATUS.dwCheckPoint    := 0;
         SERV_STATUS.dwWaitHint      := 0;
         SetServiceStatus (SERV_Handle,SERV_STATUS);
       end;
    SERVICE_CONTROL_STOP:
      if SERV_STATUS.dwCurrentState <> SERVICE_STOPPED then
        begin
          SERV_STATUS.dwCurrentState  := SERVICE_STOPPED;
          SERV_STATUS.dwCheckPoint    := 0;
          SERV_STATUS.dwWaitHint      := 0;
          SetServiceStatus (SERV_Handle,SERV_STATUS);
        end;
    SERVICE_CONTROL_INTERROGATE :;
   end;
 end;


M
Rrader
Mosian, пользуйтесь подсветкой кода!




Автор: kami 20.7.2009, 18:03
Цитата(Mosian @  20.7.2009,  14:32 Найти цитируемый пост)
ServiceCtrlHandler(Opcode: Cardinal; dwEventType: WORD; lpEventData: Pointer; lpContext: Pointer);stdcall;

Неверное объявление.
Должно быть:
Код

function ServiceCtrlHandler(dwControl: DWORD; dwEventType: DWORD; lpEventData, lpContext: pointer): BOOL; stdcall;


Цитата(Mosian @  20.7.2009,  14:32 Найти цитируемый пост)
//Он реагирует только на стандартные сообщения 

А в этом коде не видно, что обрабатываются коды SERVICE_CONTROL_DEVICEEVENT

Хочу еще обратить внимание на DEV_BROADCAST_DEVICEINTERFACE:
Цитата

dbcc_name 
A null-terminated string that specifies the name of the device.
When this structure is returned to a window through the WM_DEVICECHANGE message, the dbcc_name string is converted to ANSI as appropriate. Services always receive a Unicode string, whether they call RegisterDeviceNotificationW or RegisterDeviceNotificationA.

Это во-первых.
А во-вторых, нужно учитывать, что dbcc_name:array[0..0] of char; (для сервиса - WideChar) - это сишная структура, которая не мешает обращаться по другим индексам этого массива. В Delphi с этим могут возникнуть проблемы. (меня самого как-то "ткнули носом" в это на другом форуме).

Автор: Mosian 21.7.2009, 16:04
В ServiceCtrlHandler приходят только стандартные сообщенки сервиса (stop start)
Но SERVICE_CONTROL_DEVICEEVENT так и не работает
Что может быть  smile 


Код

function ServiceCtrlHandler(dwControl: DWORD; dwEventType: DWORD; lpEventData, lpContext: LParam): BOOL; stdcall;
 begin
     ErrortoFile(inttostr(dwControl));
   ......
 end;

{************************ Оснавная Функция сервиса ****************************}
procedure ServiceProc(argc : DWORD;var argv : array of PChar);stdcall;
var RegDN :pointer;
      dbi         :dev_broadcast_deviceinterface;
      size       :integer;
 begin
   RegDeregLog_Service;                                         //Подцепляем лог файл к службе если удачно fEventLog<>0
   SERV_STATUS.dwServiceType      := SERVICE_WIN32_OWN_PROCESS; //тип сервиса
   SERV_STATUS.dwCurrentState     := SERVICE_START_PENDING;     //Сервис находится в процессе запуска
   SERV_STATUS.dwControlsAccepted := SERVICE_ACCEPT_STOP;       //Сервис может быть остановлен.
   SERV_STATUS.dwWin32ExitCode           := NO_ERROR;
   SERV_STATUS.dwServiceSpecificExitCode := 0;
   SERV_STATUS.dwCheckPoint              := 0;
   SERV_STATUS.dwWaitHint                := 5000;           //Время необх. для полной рег. службы
   advapi32 := LoadLibrary('advapi32.dll');
   RegisterServiceCtrlHandlerEx:=GetProcAddress(advapi32, 'RegisterServiceCtrlHandlerExA');

   SERV_Handle := RegisterServiceCtrlHandlerEx(c_ServiceName,@ServiceCtrlHandler,1); //Регистрируем службу

   if SERV_Handle <> 0 then
    begin
      SERV_STATUS.dwCurrentState :=SERVICE_RUNNING; //меняем статус на запуск
      SERV_STATUS.dwCheckPoint   :=0;
      SERV_STATUS.dwWaitHint     :=0;
      SetServiceStatus(SERV_Handle,SERV_STATUS);    //Запуск сервиса

      size := sizeof(dev_broadcast_deviceinterface);
      zeromemory(@dbi, size);
      dbi.dbcc_size := size;
      dbi.dbcc_devicetype := dbt_devtyp_deviceinterface;
      dbi.dbcc_reserved := 0;
      dbi.dbcc_classguid := guid_devinterface_usb_device;
      dbi.dbcc_name := 0;
      RegDN := Registerdevicenotification(SERV_Handle,@dbi,1);

      //ОСНОВНОЙ КОД ПРОГРАММЫ
      repeat
        sleep(200);
      until SERV_STATUS.dwCurrentState = SERVICE_STOPPED;

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)