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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Подвисание при создании потока в хуке, ошибка 1444 - ERROR_INVALID_THREAD_ID 
V
    Опции темы
kami
Дата 17.5.2009, 15:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



В продолжение темы про использование NamedPipes.
По рекомендации dumb вместо пайпов стал использовать MMF.
Работа с mmf производится в доп.потоке, данные добавляются асинхронно.
Проблема:
при аттаче dll в процессы, они подвисают на создании этого доп.потока, обслуживающего mmf.
Подвисают на строках в конструкторе:
Код

constructor TMMFThread.Create(SyncMutexName: string; MMFSize: TDataLength);
var
  i: integer;
begin
  inherited Create(True); // создаем спящим
  FMutexName := SyncMutexName; // инициируем поля
  FMMFSize := MMFSize;
  i := 0;
  FWndHandle:=0;
  Resume; // пробуждаем
  while (not PostThreadMessage(ThreadID, WM_NULL, 0, 0)) and (i < 50) and (not Terminated) do // и ждем пока начнет работать очередь сообщений
// вот на этом цикле и идет подвисание.
    begin
      LogFile.WriteChronoStringCRLF('Wait pause. Error='+IntToStr(GetLastError)); // если работать без счетчика i, то мерзнет абсолютно все.
      Sleep(100);
      inc(i);
    end;
end;

Ошибка в лог выводится каждый раз одна и та же - 1444, ERROR_INVALID_THREAD_ID. Несмотря на то, что в действительности поток создается и работает нормально (данные от него через mmf приходят исправно).
Очередь сообщений доп.потока так же создается и используется:

Код

procedure TMMFThread.Execute;
var
  msg: TMSG;
begin
  PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
  // инициализация всего и вся.
  while not Terminated and GetMessage(msg, 0, 0, 0) do
    if msg.hwnd = 0 then
      case msg.Message of
        msgThreadClose: /
          begin
            Terminate;
            break;
          end;
      else
        DispatchMessage(msg);
      end
    else
      DispatchMessage(msg);
 // деинициализация всего и вся.
end;


тестовый пример - во вложении.

Где я не прав?

Добавлено через 1 минуту и 17 секунд
ай-я, пока редактировал - вложение потерялось :(
вот оно.

Присоединённый файл ( Кол-во скачиваний: 5 )
Присоединённый файл  mmf_hook.rar 8,34 Kb
PM MAIL WWW   Вверх
MetalFan
Дата 17.5.2009, 16:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



на сколько я помню, не рекомендуется делать лишние телодвижения(в т.ч. создавать потоки) в DllMain при загрузке библиотеки...

Добавлено через 9 минут и 52 секунды
хотя нет, я не прав - явного запрета на создание потока в DllMain я не нашел...
но... может стоит крайне упростить код, выполняемый в ней. не очень хорошо пускать цикл, начинать писать в файл, чего-то ждать, имхо.


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


Эксперт
***


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

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



Цитата(MetalFan @  17.5.2009,  16:11 Найти цитируемый пост)
на сколько я помню, не рекомендуется делать лишние телодвижения(в т.ч. создавать потоки) в DllMain при загрузке библиотеки...

точно, нашел такое...
DllMain callback function, в ремарках.

Если точнее, то
Цитата

Because DLL notifications are serialized, entry-point functions should not attempt to communicate with other threads or processes. Deadlocks may occur as a result.


А як жеш быть?
imho, лучше 1 раз потерять во времени при инициализации, чем каждый раз на вызове хука для синхронизации обращения к разделяемому ресурсу...

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


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


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

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



кстати, а как вообще написана DllMain?


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


Эксперт
***


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

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



Цитата(MetalFan @  17.5.2009,  16:11 Найти цитируемый пост)
может стоит крайне упростить код, выполняемый в ней. не очень хорошо пускать цикл, начинать писать в файл, чего-то ждать, имхо.

В файл пишется только для отладки, в готовом коде это будет убрано.

Цикл в конструкторе доп.потока могу убрать, но тогда нет уверенности в том, что при обращении к его методам он будет уже проинициализирован.

Сейчас пришла мысль:

А если инициализацию провести при первом вызове процедуры хука?
Правда, это тоже не рекомендуется - инициализация процесс длительный, Windows может не дождаться его окончания и прервать выполнение hook callback функции...

Добавлено через 3 минуты и 52 секунды
Цитата(MetalFan @  17.5.2009,  16:25 Найти цитируемый пост)
кстати, а как вообще написана DllMain?

Код

  hMap := OpenFileMapping(FILE_MAP_READ, False, @s[1]); // здесь содержатся только идентификаторы хуков.
  if (hMap <> 0) and (hMap <> INVALID_HANDLE_VALUE) then
    begin
      hr := MapViewOfFile(hMap,  FILE_MAP_READ,  0, 0,  SizeOf(THookRec));
      if hr <> nil then
        begin
          Move(hr^, HookRec, SizeOf(THookRec)); // скопировали их (идентификаторы хуков) себе
          UnMapViewOfFile(hr);
          begin
            s := 'Start sending data to main='#13#10;
            if Assigned(f) then
              F.Write(s[1], Length(s));

            if not Assigned(MMFThread) then
              MMFThread := TMMFThread.Create(GetMapName, 1024 * 1024); // непосредственно создание Thread-а обмена
              // именно на его конструкторе и "зависаем".
            MMFThread.AsyncAdd := True;
          end
        end
      else
        begin
          s := 'Cant get map view. Error=' + IntToStr(GetLastError) + #13#10;
          if Assigned(f) then
            F.Write(s[1], Length(s));
        end;
      CloseHandle(hMap);
    end
  else
    begin
      s := 'Cant open file mapping. Error=' + IntToStr(GetLastError) + #13#10;
      if Assigned(f) then
        F.Write(s[1], Length(s));
    end;

PM MAIL WWW   Вверх
MetalFan
Дата 17.5.2009, 16:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(kami @  17.5.2009,  16:21 Найти цитируемый пост)
imho, лучше 1 раз потерять во времени при инициализации, чем каждый раз на вызове хука для синхронизации обращения к разделяемому ресурсу...

расшифруй плиз...
в общем тут дело такое - создать то поток можно, но нельзя в него "лезть" из dllmain. а инициализировать можно все в Execute потока.
ибо, как написано у Рихтера, все DllMain вызываются линейно.
и у тебя как раз такая ситуация, что поток не будет запущен, пока не отработает DllMain c DLL_THREAD_ATTACH, а она не запустится, пока не отработает DLLMain с DLL_PROCESS_ATTACH


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


Эксперт
***


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

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



Цитата(MetalFan @  17.5.2009,  16:31 Найти цитируемый пост)
расшифруй плиз

Хук пишет данные в mmf, основная программа "выгребает" их.
Чтобы не было накладок (один недописал, второй уже считал и обнулил mmf) идет синхронизация с помощью мьютексов.
Если добавлять данные в mmf непосредственно из потока, в котором вызыван хук, то неизвестно, сколько времени будет потеряно на WaitFor-ожидание, что для хука недопустимо. Посему было сделано так - данные из хука в доп.поток, работающий с mmf, передаются с помощью PostThreadMessage, что не задерживает выполнение хука. А этот доп.поток пускай ждет сколько угодно - его ожидания не лимитируют.

Это и имел ввиду, говоря "лучше день потерять, зато потом за 5 минут долететь"    smile 

Цитата(MetalFan @  17.5.2009,  16:31 Найти цитируемый пост)
и у тебя как раз такая ситуация, что поток не будет запущен, пока не отработает DllMain c DLL_THREAD_ATTACH, а она не запустится, пока не отработает DLLMain с DLL_PROCESS_ATTACH

Ух ты...
спасибо, буду знать.
PM MAIL WWW   Вверх
MetalFan
Дата 17.5.2009, 17:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



kami, а какой смысл в конструкторе потока слать PostThreadMessage? чтобы убедиться, что создалась очередь сообщений потока?неужели это настолько критично?


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


Эксперт
***


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

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



Цитата(MetalFan @  17.5.2009,  17:16 Найти цитируемый пост)
неужели это настолько критично?

В условиях данной задачи (имеется ввиду - полной задачи, а не этого упрощения) - некритично.
Но я не люблю что-то сделав, возвращаться потом к тому же вопросу для доработки. (например, в разрабатываемом классе есть ф-и AddData, GetAllData, но нет GetFirstData).
Вдруг в другой задаче нужно будет отправить критически важные данные сразу после создания MMFThread?

Предпочитаю делать что-либо с претензией на какую-никакую, а универсальность  smile 
PM MAIL WWW   Вверх
MetalFan
Дата 17.5.2009, 19:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



кстати... к алгоритму можно придраться... имхо - он не надежен.
ибо Post{Thread}Message не гарантирует доставку сообщения. а ты наверняка передаешь с пом.него данные, которые затем будут очищаться в потоке? а если потоку сказали Terminate, он завершится не обработав возможно ожидающие в очереди сообщения...
в общем мое мнение: сообщения можно здесь использовать только для нотификации, но никак не для передачи важных данных.

Добавлено через 11 секунд
з.ы. конечно же я могу быть не прав


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


Эксперт
***


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

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



Подробное описание проблем с DllMain (серия на 4 поста).
Далее, из ERROR_INVALID_THREAD_ID - двойку вам за знание TThread. Поток запускается ТОЛЬКО после выхода из конструктора (в AfterConstruction). Хитроумные манипуляции с Resume здесь не к месту. Вставьте этот код на Execute. Даже, если вы оставите подобный подход - нет гарантии, что поток успеет создастся до вызова PostThreadMessage - вам всё равно нужна доп. синхронизация.
И ещё MetalFan прав насчёт возмодности пропуска сообщений. Для синхронизации потоков надо использовать события, крит. секции и др. объекты.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
kami
Дата 17.5.2009, 21:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



Цитата(MetalFan @  17.5.2009,  19:41 Найти цитируемый пост)
ты наверняка передаешь с пом.него данные, которые затем будут очищаться в потоке?

Совершенно верно.
PostMessage(msgAddData, integer(pData), DataSize);
Цитата(MetalFan @  17.5.2009,  19:41 Найти цитируемый пост)
а если потоку сказали Terminate, он завершится не обработав возможно ожидающие в очереди сообщения...

Поток будет терминирован, если:
1. Завершено приложение, в которое внедрен хук.
2. Вызывано UnHookWindowsHookEx для всех установленных из dll хуков.

И в том и в другом случае приходящие после этого данные уже не актуальны.
PM MAIL WWW   Вверх
kami
Дата 17.5.2009, 22:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



Цитата(CodeMonkey @  17.5.2009,  21:53 Найти цитируемый пост)
двойку вам за знание TThread. Поток запускается ТОЛЬКО после выхода из конструктора (в AfterConstruction)

Протестую!
Код

constructor TThread.Create(CreateSuspended: Boolean);
begin
  inherited Create;
  AddThread;
  FSuspended := CreateSuspended;
  FCreateSuspended := CreateSuspended;
  FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID); 
  // ThreadID становится доступен сразу в конструкторе. Инициация потока (CreateThread) произведена, чего ж еще надо?
  // с точки зрения Windows все более чем нормально. С точки зрения Delphi - все основополагающие манипуляции
  // а именно - добавление ThreadProc в список, IsMultiThread:=True так же выполнены в конструкторе TThread.
  if FHandle = 0 then
    raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]);
end;


А AfterConstruction только и делает, что вызывает Resume при необходимости.
Я же вызывал его в явном виде в своем классе после inherited Create(True). Просветите меня, если это ошибочно.
Цитата(CodeMonkey @  17.5.2009,  21:53 Найти цитируемый пост)
 Вставьте этот код на Execute.

В Execute выполняется код, предназначенный к выполнению в доп.потоке, инициализация данных и ожидание начала работы потока должны происходить не в нем.
Цитата(CodeMonkey @  17.5.2009,  21:53 Найти цитируемый пост)
нет гарантии, что поток успеет создастся до вызова PostThreadMessage - вам всё равно нужна доп. синхронизация.

Как раз-таки этот цикл гарантирует, что:
1. поток запущен и вошел в Execute.
2. Очередь сообщений инициализирована и работает.

Добавлено через 4 минуты и 52 секунды
Цитата(CodeMonkey @  17.5.2009,  21:53 Найти цитируемый пост)
И ещё MetalFan прав насчёт возмодности пропуска сообщений. Для синхронизации потоков надо использовать события, крит. секции и др. объекты.

Тем самым стопоря работу осн.потока.
Самым простым методом синхронизации будет создание окна в доп.потоке и передача данных в него с помощью SendMessage, что гарантирует поступление данных в поток до выхода из оконной процедуры, т.е. до выхода из SendMessage.

Такая возможность заложена, в примере (вложение первого поста) реализована.

Добавлено через 11 минут и 44 секунды
Цитата(CodeMonkey @  17.5.2009,  21:53 Найти цитируемый пост)
Подробное описание проблем с DllMain (серия на 4 поста)

Уже понял из msdn.
Кошмар!
dll придется пересматривать :(

Это сообщение отредактировал(а) kami - 17.5.2009, 22:14
PM MAIL WWW   Вверх
MetalFan
Дата 18.5.2009, 12:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(kami @  17.5.2009,  22:14 Найти цитируемый пост)
Цитата(CodeMonkey @  17.5.2009,  21:53 Найти цитируемый пост)
Для синхронизации потоков надо использовать события, крит. секции и др. объекты. 

Тем самым стопоря работу осн.потока.

ну уж извините) и рыбку съесть и....  не получится. либо синхронизация либо возможная порча данных. а синхронизация с пом. SendMessage конечно проста, но имхо не самый быстрый и надежный вариант.

Это сообщение отредактировал(а) MetalFan - 18.5.2009, 12:40


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


Эксперт
***


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

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



Цитата
Тем самым стопоря работу осн.потока.

Цитата
ну уж извините) и рыбку съесть и....  не получится

Нет, почему же: MsgWaitForMultipleObjects (см. также TThread.WaitFor). 
Да даже, если просто тупо WaitForSingleObject - там "завис"-то будет в сотые доли секунды. И уж всяко меньше вашего Sleep(100). Что, долго потоку раскручиваться, что-ли?

Цитата
Я же вызывал его в явном виде в своем классе после inherited Create(True). Просветите меня, если это ошибочно.

А о том, что Resume в AfterConstruction вызывается - вы не подумали?
Такие действия надо делать так:
Цитата
  FThread := TThread.Create(...);
  ...
  WaitForThreadToBeReady(FThread);
  // do something


Цитата
Как раз-таки этот цикл гарантирует, что

Да, что-то я не так этот код прочитал  smile 
Тогда проверяйте свой код - должен работать. Может, конечно, первая итерация и с ERROR_INVALID_THREAD_ID будет, но вторая - успешно.
Что-то мне кажется, вы с i напутали.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "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.0625 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


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

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