Модераторы: Snowy, bartram, MetalFan, bems, Poseidon, Riply
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Управление чужим оконным приложением из консоли, Хуки на создание окон. 
V
    Опции темы
Felan
Дата 19.6.2009, 11:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 284
Регистрация: 2.8.2007
Где: Самара

Репутация: 2
Всего: 7



Есть небольшая оконная утилита, с простой формой и несколькими диалоговыми, написана предположительно на C++.
Мне нежно сделать консольную утилиту, которая бы управляла ей. Т.е. запускала, устанавливала нужные параметры и запускала нужные методы. Ну вобщем жать на кнопки и заполнять идиты.

Запустить, найти окна, заполнить идиты, нажимать кнопки оказалось не проблемой. Проблемой оказалось получать ответы, т.к. эта утилита ответы свои выводит MessageBox-ами.

Хочу сделать так: Поставить хук на создание окон. Проверять, принадлежит ли окно заданному процессу, по содержимому опредлить, что получилось.

Почитал про хуки и пр. В итоге почти постоянно получаю AV при создании окон утилитой. Но иногда проходит нормально... но очень редко.

Длл с процедурой хука выглядит так:

Код

library hookDLL;

uses
  Windows,
  Messages,
  Sysutils,
  ReplyWindow in '..\ControlUtil\ReplyWindow.pas',
  iaSimpleLog in '..\..\Lib\iaClasses\iaSimpleLog.pas',
  Common in '..\ControlUtil\Common.pas',
  ReplyHookThread in '..\ControlUtil\ReplyHookThread.pas';

var
  vHookHandle: HHOOK = 0 ;
  vSrvWindow: HWND;

function CBTHook(CODE, WParam, LParam: DWORD): DWORD; stdcall;
var
  vCDS: TCopyDataStruct;
  vBuf: RWinInfo;
begin
  Result := CallNextHookEx(vHookHandle, CODE, WParam, LParam);
  case CODE of
    HCBT_ACTIVATE:
      begin
      end;
    HCBT_CREATEWND:
      begin
        vSrvWindow := FindWindow( nil, WINDOW_TEXT);
        vBuf.WndThreadID := GetWindowThreadProcessId(wParam, vBuf.WndProcessID);
        vBuf.WndParent := PCBTCreateWnd(LParam)^.lpcs^.hwndParent;

        ZeroMemory(@vBuf.WndClassName[0], Length(vBuf.WndClassName));
        StrPLCopy(@vBuf.WndClassName[0], PCBTCreateWnd(LParam)^.lpcs^.lpszClass, Length(vBuf.WndClassName));

        ZeroMemory(@vBuf.WndText[0], Length(vBuf.WndText));
        StrPLCopy(@vBuf.WndText[0], PCBTCreateWnd(LParam)^.lpcs^.lpszName, Length(vBuf.WndText));

        vCDS.dwData := 0;
        vCDS.cbData := SizeOf(vBuf);
        vCDS.lpData := @vBuf;

        SendMessage(vSrvWindow, WM_COPYDATA, 0, DWord(@vCDS));
        Result := 0;
      end;
    HCBT_DESTROYWND:
      begin
      end;
  end;
end;

procedure SetKeyHook(); stdcall; export;
begin
  if vHookHandle = 0 then
    begin
      vHookHandle := SetWindowsHookEx( WH_CBT, @CBTHook, hInstance, 0 );
    end;
end;

procedure DelKeyHook; stdcall; export;
begin
  if vHookHandle <> 0 then
    begin
      UnhookWindowsHookEx(vHookHandle);
      vHookHandle:=0;
    end; 
end;

exports
  SetKeyHook,
  DelKeyHook;

procedure DLLEntryPoint(dwReason:DWord);
begin
  case dwReason of
   DLL_PROCESS_ATTACH:
     begin
     end;
   DLL_PROCESS_DETACH:
     begin
     end;
   end;
end;

begin
  DllProc := @DLLEntryPoint;
  DLLEntryPoint( DLL_PROCESS_ATTACH );
end.




В управляющий утилите часть относящаяся к вопросу выглядит так:

Это функция которая ищит и заполняет окна целевой утилиты. Жмет на кнопку и выполняет цикл выборки сообщений, для получения данных от длл.

Код

procedure ProcessKeysProgGUI(aProcessInfo: TProcessInformation);
const
 WAIT_TIMEOUT = 100;
var
  vMainWindowHandle: THandle;
  vDeviceWindowHandle: THandle;
  vI: Integer;
  vMsg: TMsg;
begin
  //Поиск окна главной формы
  vMainWindowHandle := GetWindowHandle(aProcessInfo.dwThreadId, 'TMainForm', '');
  //Поиск окна выбора типа устройства
  vDeviceWindowHandle := GetChildWindowHandle(vMainWindowHandle, 'TGroupButton', vDevice);
  SendMessage(vDeviceWindowHandle, BM_CLICK, 0, 0);
  //Установка номера ключа
  vDeviceWindowHandle := GetChildWindowHandle(vMainWindowHandle, 'TComboBox', '');
  SendMessage(vDeviceWindowHandle, WM_SETTEXT, 0, LPARAM(Pchar(IntToStr(vKeyNumber))));
  //Установка данных ключа
  vDeviceWindowHandle := GetChildWindowHandle(vMainWindowHandle, 'TEdit', '');
  SendMessage(vDeviceWindowHandle, WM_SETTEXT, 0, LPARAM(Pchar(vKeyData)));

  //Установка хука на создание окон
  SetKeyHook();
  try
    //Запуск программирования.
    vDeviceWindowHandle := GetChildWindowHandle(vMainWindowHandle, 'TButton', 'Программирование');
    SendMessage(vDeviceWindowHandle, BM_CLICK, 0, 0);

    //Цикл выборки сообщений
    for vI := 0 to 1000000 do
      begin
        ProcessMessage(vMsg);
      end;
  finally
    DelKeyHook();
  end;
end
;

Так выглядит класс создающий окно, которое должно принимать сообщения:

Код

unit ReplyWindow;

interface

uses
  iaSimpleLog, Windows, Messages;

const
  WINDOW_TEXT         = 'ReplyWindow-{AE45F73A-5D19-442F-ABD7-E007EF290B45}';

type
  TReplyWindow = class
  private
    fLogger: IiaSimpleLog;
    FWindowHandle: HWND;
    procedure WndProc(var Msg: TMessage);
  public
    constructor Create(aLogger: IiaSimpleLog);
    destructor Destroy; override;
    property WindowHandle: HWND read FWindowHandle;
  end;

implementation

uses
  Classes, SysUtils, Common;

{ TReplyWindow }

constructor TReplyWindow.Create(aLogger: IiaSimpleLog);
begin
  inherited Create();

  fLogger := aLogger;
  FWindowHandle := AllocateHWnd(WndProc);
  SetWindowText(FWindowHandle, WINDOW_TEXT);
end;

destructor TReplyWindow.Destroy;
begin
  DeallocateHWnd(FWindowHandle);

  inherited;
end;

procedure TReplyWindow.WndProc(var Msg: TMessage);
var
  vCDS: TCopyDataStruct;
  vBuf: RWinInfo;
begin
  if (Msg.Msg = WM_COPYDATA) then
    begin
      Msg.Result := $FFFF;
      try
        fLogger.Log(Format('Пришло сообщение', []));
        vCDS := PCopyDataStruct(Msg.LParam)^;

        vBuf := PWinInfo(vCDS.lpData)^;
        fLogger.Log(Format('  Процесс: %u', [vBuf.WndProcessID]));
        fLogger.Log(Format('  Поток: %u', [vBuf.WndThreadID]));
        fLogger.Log(Format('  Родитель: %u', [vBuf.WndParent]));
        fLogger.Log(Format('  Класс окна: %s', [vBuf.WndClassName]));
        fLogger.Log(Format('  Текст окна: %s', [vBuf.WndText]));
      except
        on e: Exception do
          fLogger.Error(Format('Ошибка в оконной процедуре принимающего окна <%S>', [e.Message]));
      end;
    end
  else
    Msg.Result := DefWindowProc(FWindowHandle, Msg.Msg, Msg.wParam, Msg.lParam);
end;

end.


Самая большая проблема, что все это нельзя запустить под отладчиком, т.к. на точке останова виснет GUI всей системы (длл тормозит все процессы в которых происходит создание окна, а создаются они постоянно, хоть бы даже "подсказки"). PostMessage тоже использовать нельзя, т.к. при WM_COPYDATA данные будут потеряны...

В лог попадают данные типа

Код

2009-06-19 12:30:14 Log file created
2009-06-19 12:30:14 [Utils] Старт программы
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Пришло сообщение
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Процесс: 236
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Поток: 232
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Родитель: 0
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Класс окна: TPUtilWindow
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Текст окна:
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Пришло сообщение
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Процесс: 236
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Поток: 3828
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Родитель: 0
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Класс окна: TPUtilWindow
2009-06-19 12:30:15 [ INFO ] [ControlUtil] Текст окна:
2009-06-19 12:30:21 [Utils] Программа завершена



Т.е. сам механизм вроде работает.

Вобщем, может кто может подсказать куда копать. Или может как-то по другому все это можно реализовать?

ЗЫЖ Кажется мне, что я что-то не так делаю в районе передачи данных между процессами... :(
И еще, почему-то текст окон всегда пустой... вроде он есть... если это те окна. 


--------------------
// Любая сложная система - это темный лес. Каждый в этом лесу протаптывает свои тропинки, по ним и бегает. Лишь изредка, сходя с них, мы находим много интересного, а порою и страшного.
PM MAIL WWW ICQ   Вверх
Keeper89
Дата 19.6.2009, 16:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2580
Регистрация: 26.2.2009

Репутация: 14
Всего: 58



Цитата(Felan @  19.6.2009,  12:48 Найти цитируемый пост)
Мне нежно сделать консольную утилиту, которая бы управляла ей. Т.е. запускала, устанавливала нужные параметры и запускала нужные методы. Ну вобщем жать на кнопки и заполнять идиты.

Если нужно только это, я бы сделал проще (имхо) через FindWindow и FindWindowEx.

Добавлено через 3 минуты и 19 секунд
Гляньте примеры:


--------------------
PM MAIL WWW   Вверх
kami
Дата 21.6.2009, 23:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1806
Регистрация: 25.8.2007
Где: Санкт-Петербург

Репутация: 15
Всего: 72



Если нужно - могу дать свой код обмена хук>приложение через MMF.
тоже недавно мучился с этим вопросом, благодаря помощи участников форума все решилось.

Кстати, MMF не имеет неприятной особенности, которая появилась у обмена сообщениями (Post|Send[Thread]Message) в Vista - разграничение по правам процессов.

Это сообщение отредактировал(а) kami - 22.6.2009, 01:28
PM MAIL WWW   Вверх
Felan
Дата 22.6.2009, 08:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 284
Регистрация: 2.8.2007
Где: Самара

Репутация: 2
Всего: 7



Цитата(Keeper89 @  19.6.2009,  18:35 Найти цитируемый пост)
Если нужно только это, я бы сделал проще (имхо) через FindWindow и FindWindowEx.

Только с этим проблем нет.
Т.е. запустить нужный экзешник, заполнить его контролы и понажимать кнопки проблем не вызывает. Хотя и не через FindWindow делал а через EnumThreadWindows/EnumChildWindows.

Я вроде нашел косяки в функции хука. По крайней мере теперь все это работает.

Сейчас проблема в том, что это нельзя отладить, т.к. хук вешает весь GUI.
И еще, не удается получить текст создаваемого окна... это мне необходимо, что бы получить результат выполнения операции.
Походу собака порылась в том, что окна на момент выполнения хука еще нет, и соответсвенно нельзя получить его текст.

Код

function CBTHook(CODE, WParam, LParam: DWORD): DWORD; stdcall;
var
  vCDS: TCopyDataStruct;
  vBuf: RWinInfo;
  vSrvWindow: HWND;
begin
  case CODE of
    HCBT_ACTIVATE:
      begin
      end;
    HCBT_CREATEWND:
      begin
        try
          vLogger.Debug(Format('($%.8x) HCBT_CREATEWND - Начало', [HInstance]));
          vSrvWindow := FindWindow( nil, WINDOW_TEXT);

          vLogger.Debug(Format('($%.8x) Найдено окно сервера: $%.8x', [HInstance, vSrvWindow]));

          vBuf.WndHandle := WParam;
          vLogger.Debug(Format('($%.8x)    Новое окно: $%.8x', [HInstance, vBuf.WndHandle]));

          vBuf.WndThreadID := GetWindowThreadProcessId(wParam, vBuf.WndProcessID);
          vLogger.Debug(Format('($%.8x)    Процесс: $%.8x', [HInstance, vBuf.WndProcessID]));
          vLogger.Debug(Format('($%.8x)    Поток: $%.8x', [HInstance, vBuf.WndThreadID]));

          vBuf.WndParent := PCBTCreateWnd(LParam)^.lpcs^.hwndParent;
          vLogger.Debug(Format('($%.8x)    Родитель: $%.8x', [HInstance, vBuf.WndParent]));

          ZeroMemory(@vBuf.WndClassName[0], SizeOf(vBuf.WndClassName));
          GetClassName(WParam, @vBuf.WndClassName[0], SizeOf(vBuf.WndClassName) - 1);
          vLogger.Debug(Format('($%.8x)    Класс окна: %s', [HInstance, vBuf.WndClassName]));

          ZeroMemory(@vBuf.WndText[0], SizeOf(vBuf.WndText));
          GetWindowText(WParam, @vBuf.WndText[0], SizeOf(vBuf.WndText) - 1);

//          SendMessage(WParam, WM_GETTEXT, SizeOf(vBuf.WndText) - 1, DWORD(@vBuf.WndText[0]) );

          vLogger.Debug(Format('($%.8x)    Текст окна: %s', [HInstance, vBuf.WndText]));

          vCDS.dwData := 0;
          vCDS.cbData := SizeOf(vBuf);
          vCDS.lpData := @vBuf;

          vLogger.Debug(Format('($%.8x)    Отправка сообщения', [HInstance]));
          SendMessage(vSrvWindow, WM_COPYDATA, 0, DWORD(@vCDS));
          vLogger.Debug(Format('($%.8x) HCBT_CREATEWND Конец', [HInstance]));
        except
          on E: Exception do
            begin
              vLogger.Error(Format('($%.8x) HCBT_CREATEWND <%s>', [HInstance, e.Message]));
              raise;
            end;
        end;
      end;
    HCBT_DESTROYWND:
      begin
      end;
  end;
  Result := CallNextHookEx(vHookHandle, CODE, WParam, LParam);
end;


Вот думаю, как все это через  HCBT_ACTIVATE как-то надо получать текст... Хочу сделать таблицу хэндлов созданных окон, и при их активации уже слать WM_COPYDATA... только проблема в том, что нужное окно может никогда не активироваться... Если например в неподходящий момент пользователь кликнет на другое окно... 

Еще чет не пойму, можно ли этот хук поставить локально на процесс, а не на всю систему?

Цитата(kami @  22.6.2009,  01:26 Найти цитируемый пост)
Если нужно - могу дать свой код обмена хук>приложение через MMF.
тоже недавно мучился с этим вопросом, благодаря помощи участников форума все решилось.

Да не, спасибо smile
Я примеры и так видел. Сознательно от MMF отказался, т.к. надо будет делать очередь структур с данными об создаваемых окнах... синхронизировать ее... В общем из пушки по воробьям.

Цитата(kami @  22.6.2009,  01:26 Найти цитируемый пост)
Кстати, MMF не имеет неприятной особенности, которая появилась у обмена сообщениями (Post|Send[Thread]Message) в Vista - разграничение по правам процессов.

Хм... ну это не важно. Под висту наша система все равно работать не будет в принципе... Почему-то начальство решило, что это никому не нужно.


--------------------
// Любая сложная система - это темный лес. Каждый в этом лесу протаптывает свои тропинки, по ним и бегает. Лишь изредка, сходя с них, мы находим много интересного, а порою и страшного.
PM MAIL WWW ICQ   Вверх
Felan
Дата 25.6.2009, 14:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 284
Регистрация: 2.8.2007
Где: Самара

Репутация: 2
Всего: 7



Вобщем заборол. Все работает как надо.

Косяк был в том, что слушающее окно создавалось в секции инициализации общего модуля с программой и с длл. Получалось, что оно перекрывало нужное окно и обработчик был в другом процессе...

Спасибо все участвующим smile

Тема закрыта. 


--------------------
// Любая сложная система - это темный лес. Каждый в этом лесу протаптывает свои тропинки, по ним и бегает. Лишь изредка, сходя с них, мы находим много интересного, а порою и страшного.
PM MAIL WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: WinAPI и системное программирование"
Snowybartram
MetalFanbems
PoseidonRrader
Riply

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Delphi обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи
  • 99% ответов по WinAPI можно найти в MSDN Library, оставшиеся 1% здесь

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, bartram, MetalFan, bems, Poseidon, Rrader, Riply.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: WinAPI и системное программирование | Следующая тема »


 




[ Время генерации скрипта: 0.0516 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.