Форум: "Система";
Текущий архив: 2002.10.31;
Скачать: [xml.tar.bz2];
ВнизНе вызывается DLLProc(DLL_PROCESS_DETACH) Найти похожие ветки
← →
QymL (2002-08-21 20:53) [0]В DLL не могу обработать выгрузку библиотеки из процесса
Это глюк Delphi или неверный код:
library t1lib;
uses
Windows;
//uNet in "uNet.pas";
var hth:THandle;
id:CArdinal;
{$R *.res}
function go():DWord;stdcall;
var i:Cardinal;
begin
ExitThread(0);
end;
function DLLEntryPoint(dwReason: DWORD):BOOL; stdcall;
var i:Integer;
Begin
Case dwReason Of
DLL_PROCESS_ATTACH:
Begin
DisableThreadLibraryCalls(HInstance);
hth:=CreateThread(0,0,@go,0,0,id);
End;
DLL_PROCESS_DETACH:
Begin
WaitForSingleObjectEx(hth,INFINITE,true);
TerminateThread(hth,0);
CloseHandle(hth);
End
End;
Result:=true;
End;
begin
DLLProc := @DLLEntryPoint;
DLLEntryPoint(DLL_PROCESS_ATTACH)
end.
← →
Digitman (2002-08-22 08:49) [1]DLL_PROCESS_ATTACH/DETACH вызываются только если загрузка/выгрузка библиотеки инициируется осн.кодовым потоком процесса в run-time либо ОС в процессе PE load-time. В противном же случае (загрузка/выгрузка библиотеки инициируется доп.кодовым потоком в run-time) вызываются DLL_THREAD_ATTACH/DETACH.
← →
QymL (2002-08-22 10:05) [2]Да,я это знаю.
Я динамически загружаю DLL (LoadLibrary), а при вызове FreeLibrary DLL выгружается, не дожидаясь окончания работы потока.
← →
SEM (2002-08-22 11:15) [3]Дополнение:
Объявите DLLEntryPoint без stdcall.
← →
Digitman (2002-08-22 11:15) [4]Тогда непонятно, в чем проблема, если знаешь это
← →
QymL (2002-08-22 11:27) [5]2 SEM Пробовал, не помогает
2 Digitman: Проблема в преждевременной выгрузке DLL, как с ней справиться?
← →
Digitman (2002-08-22 11:46) [6]Что значит - "в преждевременной" ?
← →
SEM (2002-08-22 11:50) [7]Когда я убрал stdcall, у меня стал приходить DLL_PROCESS_DETACH
при FreeLibrary.
Что касается выше приведенного кода, то при загрузке Вашей Dll
на сообщение DLL_PROCESS_ATTACH происходит создание потока, который выполняет функцию go. В функции go происходит выход из потока. Почему Dll должна выгрузится? Когда Вы будите выгружать
свою dll (Наверное там, где Вы ее загрузили), то будет вызван
DLL_PROCESS_DETACH.
← →
QymL (2002-08-22 11:59) [8]
> SEM
Дело в том, что если для DLL вызывается FreeLibrary из EXE, то DLL выгружается, не завершая поток, т.е. если в потоке идет цикл, и по выгрузке DLL его надо завершить, то надо обработать DLL_PROCESS_DETACH, что и не получается.
← →
QymL (2002-08-22 12:00) [9]
> Digitman
См. пред. постинг
← →
Ученик (2002-08-22 12:07) [10]А точку останова в DLLEntryPoint(dwReason: DWORD):BOOL; пробовали ?
← →
QymL (2002-08-22 13:10) [11]
> Ученик
Поясните, пожалуйста
← →
Digitman (2002-08-22 13:13) [12]>QymL
> вызывается FreeLibrary из EXE
FreeLibrary может быть вызывана откуда угодно из любой точки исп.кода АП процесса (в т.ч. и из АП, распределенного под другие используемые тобой DLL, вызывающие данную DLL) и сколько угодно раз. Здесь гораздо важно четкое осознание того, в каком кодовом потоке происходит вызов FreeLibrary(). Нотификацию DLL_PROCESS_DETACH, которую ты "ловишь", можно "поймать" лишь в том случае, если ранее имела место нотификация DLL_PROCESS_ATTACH. Последняя же возникает лишь при явном или неявном вызове LoadLibrary() в контексте осн.кодового потока процесса, и неважно , какой код в этот момент исполняется - код хост-приложения или код некоей иной DLL, загруженной в АП тек.процесса.
Иными словами, если не было DLL_PROCESS_ATTACH, не жди и DLL_PROCESS_DETACH.
Лучше и надежней всего будет регистрировать ATTACH- и DETACH-нотификации (безразлично каких - PROCESS или THREAD) с определением ThreadId каждой.
В некоем частном случае я реализовал это так :
(см. усеченный код ниже)
← →
Digitman (2002-08-22 13:13) [13]
unit ThreadManager; // модуль в составе DLL
interface
uses Windows, Messages, Classes, SysUtils, SyncObjs, Tlhelp32, Hooks;
type
TThreadManager = class
private
FThreads: TStringList;
FLock: TCriticalSection;
function FindThread(const ThreadId: DWord): Integer;
procedure FreeThreadData(ThreadData: PThreadRegisterEntry);
procedure FreeObjects(List: TList);
protected
function GetThreadIndex(const ThreadId: DWord): Integer;
function GetThreadName(const ThreadId: DWord): String;
procedure SetThreadName(const ThreadId: DWord; const Value: String);
public
constructor Create;
destructor Destroy; override;
function Count: Integer;
function AddThread: Integer; overload;
function AddThread(const ThreadId: DWord): Integer; overload;
function AddThread(const ThreadName: String): Integer; overload;
function AddThread(const ThreadId: DWord; const ThreadName: String): Integer; overload;
function RemoveThread: Integer; overload;
function RemoveThread(const ThreadName: String): Integer; overload;
function RemoveThread(const ThreadId: DWord): Integer; overload;
property Threads[const ThreadId: DWord]: Integer read GetThreadIndex; default;
property ThreadName[const ThreadId: DWord]: String read GetThreadName write SetThreadName;
end;
...
var
AttachedThreads: TThreadManager = nil;
ProcessId: DWord = 0;
hProcessModule: THandle = 0;
ProcessMainThreadId: DWord = 0;
...
implementation
var
//получение Id основного кодового потока заданного процесса
function GetProcessMainThreadId(AProcessId: DWord): DWord;
var
hSnapShot: THandle;
ThreadInfo: TThreadEntry32;
Found: Boolean;
begin
Result := 0;
hSnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
Win32Check(hSnapShot <> INVALID_HANDLE_VALUE);
try
ThreadInfo.dwSize := SizeOf(TThreadEntry32);
Found := Thread32First(hSnapShot, ThreadInfo);
while Found do
if ThreadInfo.th32OwnerProcessID = ProcessId then
begin
Result := ThreadInfo.th32ThreadID;
Break;
end
else
begin
ThreadInfo.dwSize := SizeOf(TThreadEntry32);
Found := Thread32Next(hSnapShot, ThreadInfo);
end;
finally
CloseHandle(hSnapShot);
end;
end;
procedure DLLProcHandler(Reason: Integer);
var
CurrentThreadId: DWord;
begin
//Id тек.код.потока тек.процесса
CurrentThreadId := GetCurrentThreadId;
case Reason of
DLL_PROCESS_ATTACH:
begin
//Id тек.процесса
ProcessId := GetCurrentProcessId;
//Id осн.код.потока тек.процесса
ProcessMainThreadId := GetProcessMainThreadId(ProcessId);
//эмуляция нотификации DLL_THREAD_ATTACH,
//если тек.код.поток - не осн.код.поток тек.процесса
if ProcessMainThreadId <> CurrentThreadId then
//безусловная регистрация любого код.потока тек.процесса, вызвавшего LoadLibrary()
DLLProcHandler(DLL_THREAD_ATTACH);
...
// здесь ты стартуешь свой поток
// или что-то еще делаешь - библиотека только что загружена
// в АП тек.процесса
...
end;
DLL_PROCESS_DETACH:
begin
// здесь уничтожаешь свой поток
// или что-то еще делаешь - библиотека будет выгружена
// из АП тек.процесса сразу же за завершением обработки
// тек.нотификации
...
//эмуляция нотификации DLL_THREAD_DETACH,
//если тек.код.поток - не осн.код.поток тек.процесса
if ProcessMainThreadId <> CurrentThreadId then
DLLProcHandler(DLL_THREAD_DETACH);
end;
DLL_THREAD_ATTACH:
begin
//регистрация кодовых потоков тек.процесса, вызвавших LoadLibrary()
AttachedThreads.AddThread(CurrentThreadId);
end;
DLL_THREAD_DETACH:
begin
//снятие с регистрации кодовых потоков тек.процесса, вызвавших FreeLibrary()
AttachedThreads.RemoveThread(CurrentThreadId);
end;
end;
end;
function CurrentThreadId: THandle;
begin
Result:= GetCurrentThreadId;
end;
{ TThreadManager }
initialization
//хэндл модуля хост-процесса
hProcessModule := GetModuleHandle(nil);
//ловушка для DllEntryPoint
DLLProc:= @DLLProcHandler;
//создание объекта-регистратора
AttachedThreads := TThreadManager.Create;
try
//эмуляция нотификации DLL_PROCESS_ATTACH
//ибо она происходит далеко не всегда и, если происходит, то до установки ловушки
DLLProcHandler(DLL_PROCESS_ATTACH);
except
//аварийное уничтожение объекта-регистратора
AttachedThreads.Free;
raise;
end;
finalization
//штатное уничтожение объекта-регистратора
AttachedThreads.Free;
end.
декларация и реализация некоего класса-регистратора кодовых потоков, обращающихся к DLL, необязательна и неважна в данном случае для понимания сути, поэтому код, относящийся к этому, опускаю.
← →
Ученик (2002-08-22 13:22) [14]>QymL (22.08.02 13:10)
Поставить точку останова (Breakpoint, F5) в процедуре DLLEntryPoint
← →
SEM (2002-08-22 13:32) [15]Первоначальный пример заработал при замене ExitThread на
TerminateThread. Соответственно, на старом месте TerminateThread
необходимо убрать.
← →
QymL (2002-08-22 14:57) [16]
> Ученик
Ставил, DLL_PROCESS_ATTACH вызывался, а DLL_PROCESS_DETACH нет.
> SEM & Digitman
Попробую, спасибо
← →
paul_shmakov (2002-08-22 15:52) [17]2 QymL:
проверяй почту - я послал код. все вызывается, и DLL_PROCESS_DETACH в том числе.
← →
QymL (2002-08-22 16:30) [18]
> paul_shmakov
Спаасибо за код. Все работает, но не выполняетсяMessageBox(0,"Dll: DLL_PROCESS_DETACH wait succeed","",0)
, я не вижу MessageBox"ов. Почему?
← →
QymL (2002-08-24 14:33) [19]Может кто уже сталкивался и есть решение этой проблемы?
> SEM
В какой ОС?
← →
SEM (2002-08-26 11:27) [20]win2000Prof
← →
QymL (2002-08-26 13:07) [21]
> SEM © (22.08.02 13:32)
> Первоначальный пример заработал при замене ExitThread на
> TerminateThread. Соответственно, на старом месте TerminateThread
> необходимо убрать.
Первоначальный - это мой ?
← →
SEM (2002-08-26 13:49) [22]Ну да.
← →
SEM (2002-08-26 14:00) [23]При желании, могу кинуть исходники твоего кода :)
Кстати, там я вывожу messagebox на DLL_PROCESS_DETACH.
← →
QymL (2002-08-26 14:04) [24]
> SEM
Кинь мне на мыло или в форум
← →
SEM (2002-08-26 14:18) [25]Кинул.
← →
QymL (2002-08-26 15:03) [26]
> SEM © (26.08.02 14:18)
Project1.exe просто вылетает и все
← →
SEM (2002-08-26 15:49) [27]1.Вылетает сразу после нажатия Button1?
2.Какая ОС?
← →
paul_shmakov (2002-08-26 15:51) [28]все дело в том, что QymL использует Delphi 6 Enterprise. я только что обнаружил баг в его system.pas (размер файла 491801), который и приводит к тому, что DllProc не вызывается.
procedure _StartLib
//...
{ Call any DllProc }
PUSH ECX
MOV ECX,[ESP+4] // Вот здесь должно быть [ESP+8]
TEST ECX,ECX
JE @@noDllProc
MOV EAX,[EBP+12]
MOV EDX,[EBP+16]
CALL ECX
@@noDllProc:
//...
похоже, что сохранение регистра ECX они добавили позже, но совсем забыли, что нужно поправить и смещение параметра DllProc, изначально лежащего в [ESP+4], на [ESP+8].
Так что, если у вас еще не исправлено (может какой service pack это дело исправляет), то правьте руками.
2 QymL: я послал по почте версию вашей dll, в которой эторт байт исправлен - все работает.
← →
SEM (2002-08-26 15:56) [29]Может быть. У меня Delphi5.
← →
SEM (2002-08-26 15:58) [30]А насчет вылетания Project.exe, файл c:\aaa\bbb\test.txt создан?
Я писал это в письме.
← →
paul_shmakov (2002-08-26 16:15) [31]кстати, к вопросу о том, почему глюк раньше не обнаружили, или почему ничего не грохалось? все просто.
пока один из программистов borland не добавил команду PUSH ECX, стек выглядел следующем образом:
[ESP] - адрес возврата из процедуры _StartLib
[ESP+4] - значение переменной DllProc
потом добавили команду PUSH ECX. теперь [ECX+4] указавает туда, куда раньше указывал [ECX], т.е. на адрес возврата из процедуры _StartLib.
т.е. вызываться теперь из-за ошибки будет не наша DllProc, а процедура по "адресу возврата из _StartLib".
теперь посмотрим, а куда же указывает этот адрес возврата.
_StartLib вызывается из процедуры _InitLib (sysinit.pas).
procedure _InitLib;
asm
// ......
PUSH DllProc
MOV ECX,offset TlsProc
CALL _StartLib
end;
или на ассемблере:
// ......
PUSH DllProc
MOV ECX,offset TlsProc
CALL _StartLib
RET // !!!!!!!!!!!
строчка, отмеченная табуном восклицательных знаков - это как раз и есть тот самый адрес возврата из процедуры _StartLib.
т.е. вместо нашей DllProc вызываться будет процедура, состоящая из одной команды RET.
вот так, по счастливой случайности все работало и совсем не глючило :)
← →
paul_shmakov (2002-08-26 16:19) [32]> вот так, по счастливой случайности все работало и совсем не глючило :)
точнее: ничего не работало и совсем не глючило ;)
← →
Romkin (2002-08-26 16:30) [33]{ Call any DllProc }
PUSH ECX
MOV ECX,[ESP+8]
TEST ECX,ECX
JE @@noDllProc
MOV EAX,[EBP+12]
MOV EDX,[EBP+16]
CALL ECX
Update Pack 2
← →
paul_shmakov (2002-08-26 16:53) [34]2 Romkin:
" Update Pack 2"
вот и мораль - ставьте последние service packs.
← →
Digitman (2002-08-26 16:56) [35]Я так понял - речь идет искл-но о D6 ?
В D5 все работает вроде бы как положено
← →
paul_shmakov (2002-08-26 18:03) [36]да, только delphi 6, да и без второго сервис пака к тому же.
← →
Romkin (2002-08-26 18:16) [37]Мораль здесь - прежде чем переходить на новую версию ПО, дождитесь и установите сервис пак (лучше второй :-)))
или хотя бы смотрите bug report
← →
QymL (2002-08-26 19:13) [38]Спасибо всем, кто принимал обсуждение данного вопроса.
Страницы: 1 вся ветка
Форум: "Система";
Текущий архив: 2002.10.31;
Скачать: [xml.tar.bz2];
Память: 0.55 MB
Время: 0.01 c