![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Не вызывается DLLProc(DLL_PROCESS_DETACH) [D6, WinXP]
QymL © (2002-08-21 20:53:00)В 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.
QymL (2002-08-22 16:30:00) [21]
> paul_shmakov
Спаасибо за код. Все работает, но не выполняетсяMessageBox(0,"Dll: DLL_PROCESS_DETACH wait succeed","",0), я не вижу MessageBox"ов. Почему?
paul_shmakov (2002-08-22 15:52:00) [22]2 QymL:
проверяй почту - я послал код. все вызывается, и DLL_PROCESS_DETACH в том числе.
QymL (2002-08-22 14:57:00) [23]
> Ученик
Ставил, DLL_PROCESS_ATTACH вызывался, а DLL_PROCESS_DETACH нет.
> SEM & Digitman
Попробую, спасибо
SEM (2002-08-22 13:32:00) [24]Первоначальный пример заработал при замене ExitThread на
TerminateThread. Соответственно, на старом месте TerminateThread
необходимо убрать.
Ученик (2002-08-22 13:22:00) [25]>QymL (22.08.02 13:10)
Поставить точку останова (Breakpoint, F5) в процедуре DLLEntryPoint
Digitman (2002-08-22 13:13:00) [26]
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, необязательна и неважна в данном случае для понимания сути, поэтому код, относящийся к этому, опускаю.
Digitman (2002-08-22 13:13:00) [27]>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 каждой.
В некоем частном случае я реализовал это так :
(см. усеченный код ниже)
QymL (2002-08-22 13:10:00) [28]
> Ученик
Поясните, пожалуйста
Ученик (2002-08-22 12:07:00) [29]А точку останова в DLLEntryPoint(dwReason: DWORD):BOOL; пробовали ?
QymL (2002-08-22 12:00:00) [30]
> Digitman
См. пред. постинг
QymL (2002-08-22 11:59:00) [31]
> SEM
Дело в том, что если для DLL вызывается FreeLibrary из EXE, то DLL выгружается, не завершая поток, т.е. если в потоке идет цикл, и по выгрузке DLL его надо завершить, то надо обработать DLL_PROCESS_DETACH, что и не получается.
SEM (2002-08-22 11:50:00) [32]Когда я убрал stdcall, у меня стал приходить DLL_PROCESS_DETACH
при FreeLibrary.
Что касается выше приведенного кода, то при загрузке Вашей Dll
на сообщение DLL_PROCESS_ATTACH происходит создание потока, который выполняет функцию go. В функции go происходит выход из потока. Почему Dll должна выгрузится? Когда Вы будите выгружать
свою dll (Наверное там, где Вы ее загрузили), то будет вызван
DLL_PROCESS_DETACH.
Digitman (2002-08-22 11:46:00) [33]Что значит - "в преждевременной" ?
QymL (2002-08-22 11:27:00) [34]2 SEMПробовал, не помогает
2 Digitman:Проблема в преждевременной выгрузке DLL, как с ней справиться?
Digitman (2002-08-22 11:15:00) [35]Тогда непонятно, в чем проблема, если знаешь это
SEM (2002-08-22 11:15:00) [36]Дополнение:
Объявите DLLEntryPoint без stdcall.
QymL (2002-08-22 10:05:00) [37]Да,я это знаю.
Я динамически загружаю DLL (LoadLibrary), а при вызове FreeLibrary DLL выгружается, не дожидаясь окончания работы потока.
Digitman (2002-08-22 08:49:00) [38]DLL_PROCESS_ATTACH/DETACH вызываются только если загрузка/выгрузка библиотеки инициируется осн.кодовым потоком процесса в run-time либо ОС в процессе PE load-time. В противном же случае (загрузка/выгрузка библиотеки инициируется доп.кодовым потоком в run-time) вызываются DLL_THREAD_ATTACH/DETACH.