Windows для профессионалов

Явная выгрузка DLL


Если необходимость в DLL отпадает, ее можно выгрузить из адресного пространства процесса, вызвав функцию.

BOOL FreeLibrary(HINSTANCE hinstDll);

Вы должны передать в FreeLibrary значение типа HINSTANCE, которое идентифицирует выгружаемую DLL. Это значение Вы получаете после вызова LoadLibrary(Ex).

DLL можно выгрузить и с помощью другой функции:

VOID FreeLibraryAndExitThread( HlNSTANCE hinstDll, DWORD dwExitCode);

Она реализована в Kernel32.dll так:

VOID FreeLibraryAndExitThread(HINSTANCE hinstDll, DWORD dwExitCode)
{

FreeLibrary(hinstDll);
ExitThread(dwExitCode);

}

На первый взгляд, в ней нет ничего особенного, и Вы, наверное, удивляетесь, с чего это Microsoft решила ее написать. Но представьте такой сценарий. Вы пишете DLL, которая при первом отображении на адресное пространство процесса создает поток. Последний, закончив свою работу, отключает DLL от адресного пространства процесса и завершается, вызывая сначала FreeLibrary, а потом ExttThread.

Если поток станет сам вызывать FreeLibrary и ExitThread, возникнет очень серьезная проблема: FreeI.ibrary тут же отключит DLL от адресного пространства процесса. После возврата из FreeLibrary код, содержащий вызов ExttThread, окажется недоступен, и поток попытается выполнить не известно что. Это приведет к нарушению доступа и завершению всего процесса!

С другой стороны, если поток обратится к FreeLibraryAndExitThread, она вызовет FreeLibrary, и та сразу же отключит DLL, Но следующая исполняемая инструкция находится в KerneI32.dlI, а нс в только что отключенной DLL. Значит, поток сможет продолжить выполнение и вызвать ExitThread, которая корректно завершит его, не возвращая управления.

Впрочем, FreeLibraryAndExitThread может и не понадобиться. Мне она пригодилась лишь раз, когда я занимался весьма нетипичной задачей. Да и код я писал под Windows NT 3-1, где этой функции не было. Наверное, поэтому я так обрадовался, обнаружив ее в более новых версиях Windows.



На самом деле LoadLibrary и LoadLibraryEx лишь увеличивают счетчик числа пользователей указанной библиотеки, a FreeLibrary и FreeLibraryAndExitThread его уменьшают Так, при первом вызове LoadLibrary дум загрузки DLL система проецирует образ DLL-файла иа адресное пространство вызывающего процесса и присваивает единицу счетчику числа пользователей этой DLL Если поток того же процесса вызывает LoadLibrary для той же DLL еще раз, DLL больше не проецируется; система просто увеличивает счетчик числа ее пользователей — вот и все.


Чтобы выгрузить DLL из адресного пространства процесса, FreeLibrary придется теперь вызывать дважды: первый вызов уменьшит счетчик до 1, второй — до 0. Обнаружив, что счетчик числа пользователей DLL обнулен, система отключит ее. После этого попытка вызова какой-либо функции из данной DLL приведет к нарушению доступа, так как код по указанному адресу уже не отображается на адресное пространство процесса.

Система поддерживает в каждом процессе свой счетчик DLL, т. e. если поток процесса А вызывает приведенную ниже функцию, а затем тот же вызов делает поток в процессе В, то MyLib.dll проецируется на адресное пространство обоих процессов, а счетчики числа пользователей DLL в каждом из них приравниваются 1.

HINSTANCE hinstDll = LoadLibrary("MyLib.dll");

Если же поток процесса В вызовет далее:

FreeLibrary(hinst011);

счетчик числа пользователей DLL в процессе В обнулится, что приведет к отключению DLL oт адресного пространства процесса В. Но проекция DLL на адресное пространство процесса А нс затрагивается, и счетчик числа пользователей DLL в нем остается прежним.

Чтобы определить, спроецирована ли DLL на адресное пространство процесса, поток может вызывать функцию GеtМоdu1еНапd1е:

HINSTANCE GetModuleHandle(PCTSTR pszModuleName);

Например, следующий код загружает MyLib.dll, только если она еще не спроецирована на адресное пространство процесса

HINSTANCE hinstDll = GetHoduleHandle("MyLib");
// подразумевается расширение .dll if (hinstDll == NULL)
{

hinstDll = LoadLibrary("MyLib");
// подразумевается расширение .dll

}

Если у Вас есть значение HINSTANCE для DLL, можно определить полное (вместе с путем) имя DLL или EXE с помощью GetModuleFileName

DWORD GetModuleFileName( HINSTANCE hinstModule, PTSTR pszPathName, DWORD cchPath);

Первый параметр этой функции — значение типа HINSTANCE нужной DLL (или EXE). Второй параметр, pszPathName, задает адрес буфера, в который она запишет полное имя файла Третий, и последний, параметр (cchPath) определяет размер буфера в символах.


Содержание раздела