Як дізнатися яка програма завантажила мою DLL
Як дізнатися, яка програма завантажила мою DLL? Треба отримати про неї будь-яку інформацію - HANDLE or ім'я файлу or і т.д.
Дивись Tool Help Functions.
Це які CreateToolhelp32Snapshot і т.д? Тоді 1. WinNT 4.0 Unsupported 2. Яка саме функція?
Мені треба в момент завантаження DLL (не обов'язково при завантаженні, але імхо в інший час не вийде точно) дізнатись яка прога її завантажує.
Тоді дивись у хелпі DllProc. Але краще почитати у Ріхтера про DllMain.
Зазвичай hInstance основного модуля в процесі дорівнює $00400000 (якщо його Image Base не перевизначено програмістом, який його створив). Цим і можна скористатися для визначення шляху до програми, що завантажила динамічну бібліотеку.
function GetApplicationName: string; var Buffer: array [0..MAX_PATH-1] of begin GetModuleFileName($00400000, @Buf[0], SizeOf(Buffer)); Result:=PChar(@Buf[0]); end;
Віктор Щербаков До завдання питання вивчив DllProc і нічого цікавого там не знайшов - HMODULE DLL і причина виклику функції (наскільки я пам'ятаю).
Dimka Maslov Спробую. >Зазвичай hInstance . А якщо ні?)
2 Dimka Maslov: щодо звичайного $400000 - не правда, для отримання цього числа потрібно використовувати GetModuleHandle(nil)
Points to a procedure invoked by a DLL введення point.
var DLLProc: Pointer;
DLLProc використовується як особлива процедура, що вмикається протягом усього часу DLL"s введенням пункту є названим.
procedure LibraryProc(Reason: Integer);
Якщо процедура є введена, цей один параметр містить значення між 0 і 3 як визначено following group of constants in Windowsunit.
DLL_PROCESS_DETACH = 0; DLL_PROCESS_ATTACH = 1; DLL_THREAD_ATTACH = 2; DLL_THREAD_DETACH = 3;
Для подальших details на зображенні цих constants, refer to the description of функцій DllEntryPoint в Win32 API online help.
Note: DLL_PROCESS_ATTACH is pased to the procedure only if the DLL's initialization code calls the procedure and specifies DLL_PROCESS_ATTACH as a parameter.
-------------------------------------------------- ------------- Резюме : у тілі DllProc (див.хелп, як її "перехопити") з параметром DLL_PROCESS_ATTACH або DLL_THREAD_ATTACH достатньо викликати MainThreadId або GetCurrentThreadId, щоб, отримавши Id процесу/потоку, використовувати цей Id для отримання інформації про процес/поток, що "підключився" до твоєї DLL.
Все дуже просто. Щоб дізнатися ім'я процесу, що завантажив бібліотеку, достатньо скористатися стандартною функцією ParamStr(0). Це дасть правильний результат, навіть якщо бібіотека використовується з MSVC++!
>Dimka Maslov Я не перевіряв, але ! Якщо бібл-ку динамічно зажадав additional thread якогось процесу?
А чому просто в DLL entry point не поставити: begin MessageBox(0, PChar(Application.ExeName), "Hi", 0); end.
Чи ця інформація повинна в програмі виходити? Якщо так, то щось типу:
HModule:=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0); lpme.dwSize:=sizeof(MODULEENTRY32); Module32First(HModule, lpme); // робота з отриманою структурою While Module32Next(HModule, lpme) do begin //робота з отриманою структурою end;
Fellomena Ніяких Application не буде. Буде тільки Windows.pas :) CreateToolhelp32Snapshot див. вище - WINNT Unsupported :(
ALL Згадав, щоможна запустити кілька копій програми - тому шлях до неї не потрібен (хоча щось я не здогадався про paramstr) Спробую GetCurrentProcessID PS Можете українською пояснити, що за псевдо handle в HANDLE GetCurrentProcess(VOID); ?? У чому це полягає?
2 VBill: Application у разі клас VCL а об'єкт системи. Так що звідки ти його викликатимеш - не має значення. Якщо твою dll завантажить у своє АП Explorer, то видасться повний шлях до файлу, що запускає, тобто. /../explorer.exe
>Fellomena 1. Не забувай, що DLLEntryPoint може бути викликана і в додатковому потоку, а значить, ф-ції GDI не можна викликати без синхронізації з осн. потоком.
2 Digitman: 1. Можна в DLLEntryPoint аналізувати, що спричинило виклик і відповідним чином реагувати на це. 2. Що ти хочеш сказати своїм другим пунктом відповіді? Спробуй зроби елементарний глобальний хук зі строчкою, запропонованою мною раніше, в begin .. end. секції dll з якої буде встановлюватися хук - побачиш, що буде, коли будь-яка програма відобразить цю dll у своє АП (при спрацьовуванні хука природно)!
2. Знову ж таки - посилаючись на твій приклад: MessageBox(0, PChar(Application.ExeName), "Hi", 0);
Адже цей рядок звернеться, що виконується в контексті перевизначеної процедури DllProc, звернеться не до змінної Application хост-процесу, а до змінної Application екземпляра бібл-ки, який = nil в цей момент! І яких (примірників сегмента даних бібл-ки) буде стільки, скільки процесів/потоків намагаються "завантажити" бібл-ку (відобразити, як ти говориш, АП її сегментів на власне АП).
Саме це й хотів сказати я) І хуки-то тут ні до чого зовсім) Їжу, к.г., зрозуміло, що АП біблі-ки відображається на АП хостпроцесу)
2. Чому тоді при відображенні якимось процесом dll Application - це Application, як ти говориш, хост-процесу, а перевизначеної - екземпляра бібліотеки ? Поясни з прикладу plz. І ти згоден, що begin MessageBox(0, PChar(Application.ExeName), "Hi", 0); end. При "приєднанні" dllпроцесомбуде виводитися ім'я виконуваного модуля з якого цей процес був завантажений?
Малося на увазі, що в uses буде тільки Windows.pas та й по дрібницях, але не Forms, що містить цей (T) Application.
PS.Почитав останні повідомлення - мізки пухнуть :) Пішов переробляти
>Digitman Я перевірив, якщо навіть бібліотека динамічно чіпляється в додатковому потоці ParamStr(0) все одно поверне ім'я exe файлу, ось її код:
функція ParamStr(Index: Integer): string; var P: PChar; Buffer: array[0..260] of Char; begin if Index = 0 then SetString(Result, Buffer, GetModuleFileName(0, Buffer, SizeOf(Buffer))) lese begin P := GetCommandLine; while True do begin P := GetParamStr(P, Result); if (Index = 0) or (Result = "") then Break; Dec(Index); end; end; end;
тобто у Index = 0 вона працює так написав paul_shmakov, при Index <> 0 вона бере інформацію з GetCommandLine, а ця функція у всіх потоках дає однаковий результат, де б її не викликали.
function TApplication.GetExeName: string; begin Result := ParamStr(0); end;
тобто Application.ExeName === ParamStr(0) і не залежить від того в якому модулі знаходиться об'єкт Application
Якщо підчеплений в Uses Forms, то завжди в Controls виконується procedure InitControls; var AtomText: array[0..31] of Char; begin WindowAtom :=GlobalAddAtom(StrFmt(AtomText, "Delphi%.8X", [GetCurrentProcessID])); ControlAtom := GlobalAddAtom( StrFmt(AtomText, "ControlOfs%.8X%.8X", [HInstance, GetCurrentThreadID]))); CanvasList := TThreadList.Create; InitIMM32; Mouse := TMouse.Create; Screen := TScreen.Create(nil);Application := TApplication.Create(nil);InitCtl3D; Application.ShowHint := True; RegisterIntegerConsts(TypeInfo(TCursor), IdentToCursor, CursorToIdent); end;
А код ініціалізації модулів виконується справжньою DllMain до передачі управління блок begin end. Тому приклад Fellomena (20.03.02 16:31) працюватиме правильно.
>Dimka Maslov Не заперечуватиму. Судячи з коду - справді так.
>Alx2 Не буду зараз влазити в нетрі, просто загострю увагу на наступному:
finalization if Application <> nil then . ;
за яких нештатних умов м.б. виконано умову Application <> nil , якщо екзр TApplication створюється автоматично і безумовно в InitControls() ?
щось підозріло якось.
Ось з paramstr(0) більш переконливо. Але знову ж таки, наведіть мені фрагменти вихідних джерел і доведіть/спростуйте!
функція ParamStr(Index: Integer): string; var P: PChar; Buffer: array[0..260] of Char; begin if Index = 0 then SetString(Result, Buffer, GetModuleFileName(0, Buffer, SizeOf(Buffer))) lese begin P := GetCommandLine; while True do begin P := GetParamStr(P, Result); if (Index = 0) or (Result = "") then Break; Dec(Index); end; end; end;
function GetModuleFileName(Module: Integer; Filename: PChar; Size: Integer): Integer; stdcall; external kernel name "GetModuleFileNameA";
GetModuleFileName функціяотримує повний шлях і ім'я файлу для виконуваного файлу, що містить вказаний модуль.
Windows 95: функція GetModuleFilename повертатиме довгі імена файлів, якщо номер версії програми більше або дорівнює 4.00 і довга назва файлу доступна. В іншому випадку вона повертає лише імена файлів у форматі 8.3.
HMODULE hModule, // дескриптор модуля для пошуку імені файлу для LPTSTR lpFilename, // покажчик на буфер для шляху модуля DWORD nSize // розмір буфера, у символах );
Ідентифікує модуль, ім'я виконуваного файлу якого запитується. Якщо цей параметр має значення NULL, GetModuleFileName повертає шлях до файлу, використаного для створення процесу виклику.
Вказує на буфер, який заповнюється шляхом і назвою файлу даного модуля.
Вказує довжину буфера lpFilename у символах. Якщо довжина шляху та імені файлу перевищує це обмеження, рядок скорочується.
Якщо функція виконується успішно, повертається значення довжини рядка, скопійованого в буфер, у символах. Якщо функція не працює, повертається нульове значення. Щоб отримати розширену інформацію про помилку, викличте GetLastError.
>Rooman (20.03.02 19:15) >Но опять же, приведіть мені фрагменти вихідних даних >і докажіть/оперевірте
Типа, екзамен? :) Якщо немає можливості самостійно переглянути форми модуля, лови:
конструктор TApplication.Create(AOwner: TComponent); var P: PChar; Назва модуля: масив [0..255] символів; begin inherited Create(AOwner); FBiDiMode := bdLeftToRight; FTopMostList := TList.Create; FWindowHooks := TList.Create; FHintControl := nil; FHintWindow := нуль; FHintColor := DefHintColor; FHintPause := DefHintPause; FHintShortCuts := True; FHintShortPause :=DefHintShortPause; FHintHidePause := DefHintHidePause; FShowHint := False; FActive := True; FIcon := TIcon.Create; FIcon.Handle := LoadIcon(MainInstance, "MAINICON"); FIcon.OnChange := IconChanged;GetModuleFileName(MainInstance, ModuleName, SizeOf(ModuleName));OemToAnsi(ModuleName, ModuleName); P := AnsiStrRScan(ModuleName, "\"); if P <> nil then StrCopy(ModuleName, P + 1); P := AnsiStrScan(ModuleName, "."); if P <> nil then P^ := #0; AnsiLower(ModuleName + 1); FTitle := ModuleName;якщо не єLibrary then CreateHandle;- Спеціально на випадок DLL UpdateFormatSettings := True; UpdateMetricSettings := True; FShowMainForm := True; FAllowTesting := True; FTestLib: = 0; end;
Не зовсім конструктор, вірніше зовсім не конструктор (про це вже було в попередніх повідомленнях)
function TApplication.GetExeName: string; begin Result := ParamStr(0); end;
Інші твої цитати нічого нового не дають. Як би сказав Digitman, і їжу зрозуміло :))
PUSH ECX PUSH ESI PUSH EDI
MOV ESI,offset InitContext LEA EDI,[EBP- (type TExcFrame) - (type TInitContext)] MOV ECX,(type TInitContext)/4 REP MOVSD
POP InitContext.DLLSaveEDI POP InitContext.DLLSaveESI MOV InitContext.DLLSaveEBP,EBP MOV InitContext.DLLSaveEBX,EBX MOV InitContext.InitTable,EAX MOV InitContext.Module,ED LEA ECX, [EBP- (type TExcFrame) - (type TInitContext)] MOV InitContext. @notShutDown MOV ECX,[EAX].PackageInfoTable.UnitCount @@notShutDown: MOV InitContext.InitCount,ECX
MOV EAX,[EBP+12] INC EAX MOV InitContext.DLLInitState,AL DEC EAX
POP ECX MOVEDX,[ECX] MOV InitContext.ExitProcessTLS,EDX JE @@noTLSproc CALL dword ptr [ECX+EAX*4] @@noTLSproc:
MOV EDX,[ESP+4] TEST EDX,EDX JE @@noDllProc MOV EAX,[EBP+12] CALL EDX @@noDllProc:
CMP MainInstance,0 JNE @@haveExe MOV IsLibrary,1 FNSTCW Default8087CW // save host exe"s FPU preferences
MOV EAX,[EBP+12] DEC EAX JNE _Halt0CALL InitUnits//Ось тут і народиться Application при використанні модуля Forms :) RET 4 end;
>Digitman>якщо екз-р TApplication створюється автоматично >і безумовно в InitControls() ? А що це не так?
>за яких нештатних умов >м.б. виконано умову Application <> nil,
Digitman, на мою думку, це просто правило хорошого тону. А позаштатна ситуація може бути такою: програма могла заздалегідь поховати цей Application. Щоправда, не знаю з якою метою, але це вже інші проблеми.
var FileName: array[0..MAX_PATH] of Char; begin if GetModuleFileName(GetModuleHandle(nil), FileName, MAX_PATH) <> 0 then begin // ok. FileName містить ім'я файлу процесу, в який завантажено dll. // і навіщо TApplication? end;