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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> JournalRecord хук в потоке виснет на GetMessage 
:(
    Опции темы
ldr12
Дата 9.2.2007, 15:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Столкнулся со следующей проблемой.

Создаю с помощью CreateThread() поток, в котором впоследствии вызываю установку хука при помощи SetWindowsHookEx с параметром WH_JOURNALRECORD. За установкой хука следует стандартный цикл обработки очереди сообщений потока GetMessage/TranslateMessage/DispatchMessage.

Проблема в том, что если хук ставится в главном потоке процесса, при отсутствии иных запущенных потоков - все работает превосходно. Если же до/после установки хука создается другой поток - все намертво зависает до нажатия Ctrl+Alt+Del.

Пробовал блокирующий GetMessage заменять неблокирующим PeekMessage - все то же самое - данная функция виснет намертво.

Перепробовал все возможные параметры CreateThread/SetWindowsHookEx/GetMessage - к должному результату не привело. Либо все виснет, либо не работает хук.

Опытным путем установил, что если между SetWindowsHookEx и GetMessage/PeekMessage поставить вызов MessageBox, который закрывается потом обычным щелчком мыши или нажатием Enter - то GetMessage начинает работать как ни в чем ни бывало и все потоки функционируют нормально.

Напрашивается мысль, что GetMessage необходимо в самом начале работы некое сообщение, вроде щелчка мыши или нажатия клавиши, адресованной вызывающему потоку? Пробовал вставлять туда keybd_event(), mouse_event(), Sleep() - не помогает. Да и почему же тогда работает в главном потоке, и чем мешает хуку запуск дочерних потоков? 

Что здесь можно сделать?
PM MAIL   Вверх
MetalFan
Дата 9.2.2007, 16:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 16
Всего: 128



ldr12, отвечу, как принято отвечать в таких случаях: "у тебя ошибка в 17 строке!"
пара вопросов:
1) нигде не упоминается о dll... 
2) зачем в потоке цыкл выборки сообщений? от кого их ждет? GetMessage в потоке остановит его выполнение до их появления


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
ldr12
Дата 9.2.2007, 17:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Дело не в наличии/отсутствии ошибок. Я интересуюсь самой сутью зависания, а повторить сей эксперимент может каждый.

1) WH_JOURNALRECORD/WH_JOURNALPLAYBACK хуки не нуждаются в DLL. Об этом написано даже в Remarks разделе соответствующей статьи MSDN.

2) GetMessage останавливает всё вообще. Сообщение не обрабатывается им, не поступает хуку, соответственно хук не может вызвать CallNextHookEx() и позволить другим приложениям эти сообщения обработать.
Зачем? А как в таком случае callback-функция хука будет получать извещения о WM_KEYDOWN или WM_LBUTTONDOWN? Есть сообщения окнам, есть сообщения потокам, в случае хуков действует второй вариант.

Приведу свой код в упрощенном виде для понятности:

Код

program Project1;

uses
  Windows,
  Messages;

var
  Msg: TMsg;
  Hook, ThID, ThHandle: DWORD;

function HookProc(Code, WParam, LParam: LongInt): LongInt; stdcall;
var
  Msg: PEventMsg;

begin
  Result := CallNextHookEx(Hook, Code, WParam, LParam);

  if Code = HC_ACTION then
  begin
    Msg := Pointer(LParam);
    //Далее можно что угодно делать с Msg.message, тут будет сообщение и с Msg.paramL, тут параметр
  end;
end;

procedure SetHook(Param: Pointer); stdcall;
begin
  Hook := SetWindowsHookEx(WH_JOURNALRECORD, @HookProc, GetModuleHandle(nil), 0);

  while GetMessage(Msg, 0, 0, 0) do
  begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
end;

begin
   ThHandle := CreateThread(nil, 0, @SetHook, nil, 0, ThID);

   //Здесь занимаюсь чем-нить другим, пока работает хук
end.


Выяснил одну детальку: если между SetWindowsHookEx и циклом поставить
Код
PostThreadMessage(ThHandle, WM_KEYDOWN, 0, VK_RETURN);

то ОДИН раз все запустится нормально. Если второй, третий, четвертый раз попробовать - будет снова виснуть. Если же на пятый раз заменить ThHandle на GetCurrentThread, СНОВА запустится нормально. Но на 6, 7, n+1 снова будет виснуть. И так до перезагрузки. Такое ощущение, что как будто бы не освобождаются какие-то уникальные ресурсы, выделяемые моему приложению... Хотя перед завершением работы программы делаю UnhookWindowsHookEx(Hook) и поток мирно завершается.

Это сообщение отредактировал(а) ldr12 - 9.2.2007, 17:45
PM MAIL   Вверх
ldr12
Дата 9.2.2007, 18:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



PostThreadMessage говорит ERROR_INVALID_THREAD_ID. Что, согласно MSDN случается в следующих случаях (Windows 2000):

1) Для потока не было создано очереди сообщений, что исключается, ибо SetWindowsHookEx был успешно вызван. Да и система бы не висела, не сработай он smile

2) Поток назначения не принадлежит десктопу или процессу потока отправления, что тоже не так, потому что поток сам из себя шлет сообщение.

3) Если указан неверный хендл потока, что тоже не так, потому что ThHandle - реальный идентификатор потока с правами THREAD_ALL_ACCESS, а в случае с GetCurrentThread (псевдоидентификатором, действительным для текущего потока) не помогает и DuplicateHandle, которым пытаюсь получить реальный.
PM MAIL   Вверх
MetalFan
Дата 10.2.2007, 00:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 16
Всего: 128



Цитата
WH_JOURNALRECORD/WH_JOURNALPLAYBACK хуки не нуждаются в DLL.

согласен. подзабыл слегка)

Цитата(ldr12 @  9.2.2007,  17:35 Найти цитируемый пост)
GetMessage останавливает всё вообще

полный бред)

в коде куча недочетов. 
1) нигде не анализируются резултаты работы функций.
2) зачем TranslateMessage, если не анализируется пользовательский ввод?
3) зачем CreateThread? не лучше ли BeginThread?
4) ThreadProc выглядит как function ThreadProc(Param: integer): Integer;
5) ошибка при установке хука. если устанавоивается хук, с функцией-ловушкой в теле приложения, а не библиотеки, то нужно hMod указать nil, а dwThreadId - id потока, с которым оне ассоциировано.
Цитата
hMod
[in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process. 
dwThreadId
[in] Specifies the identifier of the thread with which the hook procedure is to be associated. If this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread. 



--------------------
There are always someone smarter than you...
PM MAIL   Вверх
ldr12
Дата 10.2.2007, 02:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



1) Вот и славно, я тебе напомнил smile
2) Не бред, я все описал - GetMessage зависает и хук не получает сообщения, соответственно не может передать его дальше по цепочке хуков системы и ввод от пользователя просто блокируется, как при BlockInput()
3) Насчет недочетов.
  1. Результаты работы функций сто раз мною анализировались за последние сутки и на основании их сделаны вышеизложенные выводы smile Я не стал вписывать сто проверок в пример кода выше (отдельно протестированного) для наглядности, на самом деле мой код намного сложнее и без того.
  2. Анализируется именно пользовательский ввод. В т.н. JOURNAL как раз записываются все действия пользователя, ибо применяется он в основном для записи макросов. В любом случае, TranslateMessage тут ни при чем, хотя бы потому, что зависает на самом первом GetMessage.
  3. 
Код

{$IFDEF MSWINDOWS}
function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
  ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
  var ThreadId: LongWord): Integer;
var
  P: PThreadRec;
begin
  New(P);
  P.Func := ThreadFunc;
  P.Parameter := Parameter;
  IsMultiThread := TRUE;
  Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P,
    CreationFlags, ThreadID);
end;

Лишние усложнения и привязки к RTL ни к чему, да и на код не влияют. Все равно вызывается тот же самый CreateThread, да и параметры все без особых изменений.
  4. SizeOf(Integer) = SizeOf(Pointer) = 4, для системы разницы нет, а результат ThreadProc в WinAPI - такая же необязательная вещь, как всякие LParam и WParam в callback процедурах. Справедливости ради замечу, что тестировал все варианты smile
  5. Здесь тонкости перевода smile Не лишним будет вчитаться. Насчет dwThreadId вольный перевод: "лов сообщения будет идти для конкретного потока в системе либо для всех потоков в системе", а в MSDN JOURNALRECORD/JOURNALPLAYBACK хуки указываются как Global Only. Соответственно отпадает и необходимость в указании нуля в третьем параметре, но наоборот, приобретается зависимость к указанию модуля текущего процесса. Тут этот параметр играет ту же роль, как hInstance при регистрации класса окна. Справедливости ради замечу, что тестировал все варианты smile

Все вышепредложенные идеи протестировал, на всякий случай. Должного рез-та не принесло. Так что пока мы с тобой толчем воду в ступе smile

Рекомендую вышеуказанный код сохранить, скомпилировать, заменив нижний комментарий на WaitForSingleObject() или Sleep(INFINITE) и посмотреть, что будет smile

Any more ideas?

Это сообщение отредактировал(а) ldr12 - 10.2.2007, 02:47
PM MAIL   Вверх
MetalFan
Дата 10.2.2007, 12:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 16
Всего: 128



Вот, что у меня заработало!
но только одно "но". под отладкой виснет на PeekMessage. 
а когда отдельно запущено - то все ок. после Ctrl+Esc появляется лог с журналом)
Код

program Project1;
uses
  Windows,
  SysUtils,
  Classes,
  Messages;

var
  AppMsg: TMsg;
  lStrs: TStringList;

function HookProc(Code: Integer; WParam, LParam: LongInt): LongInt; stdcall;
var
  lMsg: PEventMsg;
  lStr: string;
begin
  Result := CallNextHookEx(0, code, wparam, lparam);
  if code = hc_action then
  begin
    lMsg := Pointer( lparam );
    lStr := Format( 'Msg:$%x, LPar:$%x, HPar:$%x, Hwnd:$%x', [lMsg^.message,lMsg^.paramL, lMsg^.paramH,lMsg^.hwnd]);
    lStrs.Add(lStr)
   end;
end;

begin
  lStrs :=  TStringList.Create;
  Hook := SetWindowsHookEx(WH_JOURNALRECORD, @HookProc, HInstance, 0);
  while true do
  begin
    if  PeekMessage(AppMsg, INVALID_HANDLE_VALUE, 0, 0, PM_REMOVE) then
    begin
      if AppMsg.message = WM_CANCELJOURNAL then Break;
      DispatchMessage(AppMsg);
    end;
    Sleep(10);
  end;
  UnhookWindowsHookEx( Hook );
  lStrs.SaveToFile( ChangeFileExt(ParamStr(0),'.log' ) );
  lStrs.Free;
end.



--------------------
There are always someone smarter than you...
PM MAIL   Вверх
ldr12
Дата 10.2.2007, 17:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Мля smile Искали ошибку там, где её нет smile Все дело-то как раз в запуске под отладкой smile
Запустил как твой, так и свой код не через F9 - все прекрасно работает. Запустил снова через Delphi - виснет.

Спасибо большое, я бы вряд ли в ближайшее время догадался искать ошибку там smile
PM MAIL   Вверх
MetalFan
Дата 10.2.2007, 21:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 16
Всего: 128



ldr12, да, видимо делфя что-то не поделила с отлаживаемым приложением... ))


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
ldr12
Дата 10.2.2007, 22:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Ну это ее проблемы, последнее слово за OS smile
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "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.0708 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


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

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