Опыт использования системных ловушек или как я создавал программу по надзору за записью компакт-дисков

Гуляя по форумам, я несколько раз видел, что людей интересует вопрос, как отслеживать процесс записи. И даже видел ответы, что это якобы невозможно. Но вот в один прекрасный момент в магазине по продаже компакт-дисков мне ставят подобную задачу. Т.е. необходимо отслеживать, сколько дисков прописано за день и время когда произведена запись.

Подумав с недельку, я предложил создать свою программу записи дисков. Благо существуют неплохие компоненты Magic CD DVD Burner. Доложив о своем решении и получив добро, я с головой ушел в процесс написания и отладки программы. После непродолжительного времени я с гордостью несу сей продукт к заказчику, и объясняю, как с ним работать. Сказали, что я деньги за работу получу после обкатки программы. Все бы хорошо. Но через пару дней мне говорят, что их не устраивает такой вариант. Мотивируя тем, что нужно обучать персонал, не предусмотрена программой возможность клонирования дисков. Да и привыкли они использовать программы Nero и CloneCD, и что они не желают отказываться от указанных программ. Далее говорят, что дают мне две трети от суммы договора, если я на данном этапе прекращаю работу над решением поставленной задачи, либо в 1.5 раза больше, если продолжу и доведу до конца начатое дело.

Долго я думал, как же решить данную проблему. И поскольку программа должна была функционировать в среде Windows, мне пришла в голову идея использовать ловушку системных сообщений. Первым, что мне пришло в голову, это отлавливание нажатия кнопки в окне предшествующем записи. Но тут меня ждало разочарование. В CloneCD, по всей видимости, используются нестандартные элементы управления. И таким образом не отлавливается событие - BN_CLICKED.

/Опять бессонные ночи, головная боль и море выпитого пива. В результате, замечаю общую черту указанных мне программ. После процесса записи выскакивает окошко (по типу MessageBox) с сообщением об удачном или неудачном процессе записи. И тут у меня родилось решение поставленной задачи. Необходимо реализовать ловушку на события по работе окнами, т.е. их создание (create) или уничтожение (destroy). Поскольку решение проблемы было найдено, я устроил пару дней заслуженного отдыха.

После небольшого отдыха я опять приступил к работе. Быстренько накидал текст основной программы, которая будет запускать ловушку.

program Burn_Hook;

uses Windows, Messages, Kol;

function SetMyHook(Hook : Boolean) : Boolean; stdcall; external 'hook_burn.dll';

function WindowProc(Wnd: HWnd; Message, WParam: Word; LParam: Longint): Longint;  stdcall;

begin

  WindowProc := 0;

  case Message of

    wm_Destroy : begin

                   PostQuitMessage(0);

                   Exit;

                 end;

  end;

  WindowProc := DefWindowProc(Wnd, Message, WParam, LParam);

end;


 

procedure MainWinProc; stdcall;

const

  WindowClass : TWndClass = (style : 0;

                             lpfnWndProc : @WindowProc;

                             cbClsExtra : 0;

                             cbWndExtra : 0;

                             hInstance : 0;

                             hIcon : 0;

                             hCursor : 0;

                             hbrBackground : 0;

                             lpszMenuName : #0;

                             lpszClassName : 'MyHook_Burn');

var

  Message: TMsg;

  Window : HWnd;

  HK : HKey;

begin

  HK := RegKeyOpenWrite(HKEY_CURRENT_USER,'Software\Microsoft\Windows\CurrentVersion\Run');

  if HK <> 0 then

  begin

    if not RegKeyValExists(HK, 'Burn_Hook') then

      RegKeySetStr(HK, 'Burn_Hook','Burn_Hook.exe');

    RegKeyClose(HK);

  end;

 

  if Windows.RegisterClass(WindowClass) = 0 then

    Halt(255);

  Window := CreateWindow('MyHook_Burn', 'MyHook_Burn', 0, 0, 0, 0, 0, 0, 0, HInstance, nil);

  ShowWindow(Window, SW_HIDE);

  SetMyHook(true);

 

  while GetMessage(Message, 0, 0, 0) do

    begin

      TranslateMessage(Message);

      DispatchMessage(Message);

    end;

  SetMyHook(false);

end;

 

begin

  MainWinProc;

end.


Надеюсь здесь все понятно. В первых строчках основной процедуры устанавливаем автозапуск программы. Для этого прописываем программу в секцию RUN системного реестра. Теперь можно не беспокоиться, программа сама будет запускаться при включении компьютера. Далее примитивная блокировка от запуска последующей копии программы, создание окна, установка ловушки и запуск цикла обработки сообщений.
Осталось дело за малым. Нужно написать динамическую библиотеку, которая реализует саму ловушку.


 

library hook_burn;

 

uses  Windows, Kol, Messages;

 

var

 SysHook : HHook = 0;

 Wnd     : Hwnd = 0;

 

function EnumChildWnd(h: hwnd): Bool; stdcall;

var

  f: TextFile;

  s1,str : array [0..255] of char;

  s,sfile : string;

begin

   Result := True;

   GetClassName(h, str, 255);

   s := str;

   if s = 'Static' then

   begin

     GetWindowsDirectory(s1, 255);

     sfile:=s1+'\Burn.log';

     GetWindowText(h, str, 255);

     s := str;

     if (Length(s) < 6) or (Pos(#13,s)>0) or (Pos(#10,s)>0) or (Pos('?',s)>0) then

     begin

       s := ' ';

     end

     else

     begin

       AssignFile(f, sfile);

       if not FileExists(sfile) then

       begin

         Rewrite(f);

         CloseFile(f);

       end;

       Append(f);

       Writeln(f, Date2StrFmt('dd.MM.yyyy ', Now)+Time2StrFmt('HH:mm:ss', Now)+'  ='+S);

       Flush(f);

       CloseFile(f);

       Result := False;

     end;

   end;

end;


 

function SysMsgProc(code : integer; wParam : word; lParam : longint) : longint; export; stdcall;

var

 windtext: array [0..255] of char;

 str:String;

begin

 Result := CallNextHookEx(SysHook, Code, wParam, lParam);

 case code of

  HCBT_DESTROYWND:

   begin

    Wnd := wParam;

    GetWindowText(Wnd, windtext, 255);

    Str:=windtext;

    if (UpperCase(str)<>'CLONECD') and (Pos('NERO',UpperCase(str))=0) and

       (Pos('BURNING ROM',UpperCase(str))=0)  then Exit;

    EnumChildWindows(Wnd,@EnumChildWnd,0);

   end;

 end;

end;

 

function SetMyHook (Hook : Boolean) : Boolean; export; stdcall;

begin

  Result := false;

  if Hook  then

  begin

      if SysHook = 0 then

        SysHook := SetWindowsHookEx(WH_CBT, @SysMsgProc, HInstance, 0);

      Result := (SysHook <> 0);

  end

  else

  begin

      if SysHook <> 0 then

        begin

          UnhookWindowsHookEx(SysHook);

          SysHook := 0;

          Result := true;

        end;

  end;

end;

exports

  SetMyHook;

begin

end.



Итак, ловушка запускается прцедурой SetMyHook. В качестве первого параметра функции SetWindowsHookEx передается WH_CBT. Таким образом, наша ловушка будет срабатывать при любой манипуляции с окнами в среде Windows. В качестве второго параметра в функцию SetWindowsHookEx передается адрес функции, которая выполняется при срабатывании ловушки. Рассмотрим реализацию данной функции.
Первой строчкой идет вызов функции CallNextHookEx, которая передает управление другим зарегистрированным ловушкам данного типа. Далее анализируем тип пойманного сообщения. Если это уничтожение окна, и данное окно относится к программам Nero или CloneCD, то производим его обработку. Считываем статический текст из данного окна. Отсеиваем лишние сообщения, а оставшиеся выводим в файл лога. Дополнительно записываем дату и время выдачи данного сообщения.

Дополнительно хочу пояснить, почему я выбрал событие – уничтожение окна. Тут просто. Событие типа – создание, просто не отлавливалось при выдаче нужных нам окон, а активация приводила к нежелательному эффекту. То есть программа завершила запись, выдано окно, ловушка сработала, запись в лог произведена. В это время на компьютере переключаются на другие окна. Далее возврат к нашему окну. И что в этом случае. Окно опять активировалось и как следствие в лог уходит лишняя строка.
Для правильного функционирования программу нужно положить папку Windows или Windows\System.

Вот и все. Программа создана, заказчики довольны. Особый восторг заказчика вызвали размеры программы. Burn_Hook.exe – 7.5 Кб и hook_burn.dll – 16 Кб.

feandy. 29.02.2004 16:46

feandy1@rbcmail.ru



Опубликовал admin
1 Мар, Понедельник 2004г.



Программирование для чайников.