Как выгрузить dll из процесса

Обновлено: 29.06.2024

компоновщик MSVC поддерживает отложенную загрузку библиотек dll. эта функция освобождает вас от необходимости использовать функции Windows SDK LoadLibrary и GetProcAddress для реализации отложенной загрузки DLL.

Без отложенной загрузки единственным способом загрузки библиотеки DLL во время выполнения является использование LoadLibrary и GetProcAddress ; Операционная система ЗАГРУЖАЕТ библиотеку DLL при загрузке исполняемого файла или библиотеки DLL.

При отложенной нагрузке при неявной компоновке библиотеки DLL компоновщик предоставляет параметры для задержки загрузки DLL до тех пор, пока программа не вызовет функцию в этой библиотеке DLL.

Приложение может задержать загрузку библиотеки DLL с помощью параметра компоновщика /DELAYLOAD (импорт отложенной загрузки) с вспомогательной функцией. (Реализация вспомогательной функции по умолчанию предоставляется корпорацией Майкрософт.) Вспомогательная функция загружает библиотеку DLL по запросу в среде выполнения, вызывая метод LoadLibrary и GetProcAddress .

Попробуйте задержать загрузку библиотеки DLL, если:

Программа может не вызывать функцию в библиотеке DLL.

Функция в библиотеке DLL может не вызываться до окончания выполнения программы.

Отложенная загрузка библиотеки DLL может быть задана во время сборки EXE-или DLL-проекта. Проект DLL, который задерживает загрузку одной или нескольких библиотек DLL, не должен вызывать точку входа, загруженную с задержкой, в DllMain .

Указание библиотек DLL для задержки загрузки

С помощью параметра компоновщика можно указать, какие библиотеки DLL следует отложить загрузку /delayload: dllname . Если вы не планируете использовать собственную версию вспомогательной функции, необходимо также связать программу с delayimp.lib (для классических приложений) или dloadhelper.lib (для приложений UWP).

Ниже приведен простой пример отложенной загрузки библиотеки DLL:

Создание ОТЛАДОЧНОЙ версии проекта. Пошаговое выполнение кода с помощью отладчика, и вы заметите, что user32.dll загружается только при вызове MessageBox .

Явная выгрузка библиотеки DLL, загруженной с задержкой

/delay:unload Параметр компоновщика позволяет коду явно выгружать БИБЛИОТЕКУ DLL, которая была загружена с задержкой. По умолчанию импорт с отложенной загрузкой остается в таблице адресов импорта (IAT). Однако при использовании /delay:unload в командной строке компоновщика вспомогательная функция поддерживает явную выгрузку библиотеки DLL с помощью __FUnloadDelayLoadedDLL2 вызова и СБРАСЫВАЕТ объект IAT в исходную форму. Недопустимые указатели будут перезаписаны. IAT — это поле в ImgDelayDescr структуре, содержащее адрес копии ИСХОДНОЙ IAT, если она существует.

Пример выгрузки библиотеки DLL с отложенной загрузкой

В этом примере показано, как явно выгрузить библиотеку DLL, MyDll.dll которая содержит функцию fnMyDll :

Важные примечания по выгрузке библиотеки DLL с отложенной загрузкой:

реализацию функции можно найти в __FUnloadDelayLoadedDLL2 файле delayhlp.cpp в include каталоге MSVC. Дополнительные сведения см. в разделе сведения о вспомогательной функции отложенной загрузки.

name Параметр __FUnloadDelayLoadedDLL2 функции должен точно соответствовать (включая регистр), который содержит библиотека импорта. (Эта строка также находится в таблице импорта в изображении.) Содержимое библиотеки импорта можно просмотреть с помощью команды DUMPBIN /DEPENDENTS . если вы предпочитаете совпадение строк без учета регистра, можно обновить __FUnloadDelayLoadedDLL2 для использования одной из строковых функций CRT без учета регистра или вызова API Windows.

Операции привязки, загруженные с задержкой

Поведение компоновщика по умолчанию — создать связанную таблицу адресов импорта (IAT) для DLL с отложенной загрузкой. Если библиотека DLL привязана, вспомогательная функция пытается использовать привязанные сведения вместо вызова GetProcAddress на каждом из импортируемых ссылок. Если либо отметка времени, либо предпочтительный адрес не совпадают с меткой в загруженной библиотеке DLL, то вспомогательная функция предполагает, что связанная таблица адресов импорта устарела. Она продолжается так, как если бы не существовала IAT.

Если вы никогда не планируете привязать отложенно загруженные импорты библиотеки DLL, укажите /delay:nobind в командной строке компоновщика. Компоновщик не создает связанную таблицу адресов импорта, сохраняющую место в файле изображения.

Загрузка всех импортов для DLL с отложенной загрузкой

__HrLoadAllImportsForDll Функция, определенная в delayhlp.cpp , указывает компоновщику загрузить все импорты из библиотеки DLL, указанной с помощью /delayload параметра компоновщика.

При загрузке всех импортов одновременно можно централизовать обработку ошибок в одном месте. Можно избежать структурированной обработки исключений вокруг всех фактических вызовов операций импорта. Это также позволяет избежать ситуации, когда приложение завершается неудачно с помощью процесса: например, если вспомогательный код не может загрузить импорт, после успешной загрузки других.

Вызов __HrLoadAllImportsForDll не изменяет поведение обработчиков и обработки ошибок. Дополнительные сведения см. в разделе Обработка ошибок и уведомление.

__HrLoadAllImportsForDll выполняет сравнение с учетом регистра с именем, хранящимся в самой библиотеке DLL.

Ниже приведен пример использования __HrLoadAllImportsForDll в функции, вызываемой TryDelayLoadAllImports для попытки загрузить ИМЕНОВАННУЮ библиотеку DLL. CheckDelayException Для определения поведения исключения используется функция.

Результат можно использовать TryDelayLoadAllImports для управления вызовом функций импорта.

Обработка ошибок и предупреждений

Если программа использует библиотеки DLL с отложенной загрузкой, она должна обеспечивать устойчивую обработку ошибок. Сбои, возникающие во время выполнения программы, приводят к необработанным исключениям. Дополнительные сведения об обработке ошибок и уведомлениях о задержке при загрузке DLL см. в разделе Обработка ошибок и уведомления.

Импорт с отложенной загрузкой импорта

Операции импорта с отложенной загрузкой можно выгрузить с помощью DUMPBIN /IMPORTS . Эти импорты отображаются с немного отличающимися сведениями, чем стандартные операции импорта. Они разделяются на отдельные разделы /imports списка и явно помечены как импортированные с отложенной загрузкой. Если на изображении есть сведения о выгрузке, это отмечается. Если сведения о привязке присутствуют, то метка времени и даты для целевой библиотеки DLL указывается вместе с связанными адресами импорта.

Ограничения для библиотек DLL с отложенной загрузкой

Существует несколько ограничений на отложенную загрузку импортов DLL.

Импорт данных не поддерживается. Обходной путь заключается в явной обработке импорта данных с помощью LoadLibrary (или GetModuleHandle после того, как вы узнаете, что вспомогательная служба отложенной загрузки загрузила библиотеку DLL) и GetProcAddress .

Отложенная загрузка Kernel32.dll не поддерживается. Эта библиотека DLL должна быть загружена для работы вспомогательных подпрограмм с отложенной загрузкой.

Привязка перенаправленных точек входа не поддерживается.

Процесс может иметь другое поведение, если библиотека DLL загружается с задержкой, а не загружается при запуске. Она может быть видна, если в точке входа в библиотеке DLL с отложенной загрузкой возникают инициализации для каждого процесса. В других случаях — статический протокол TLS (локальное хранилище потока), объявленный с помощью __declspec(thread) , который не обрабатывается при загрузке библиотеки DLL с помощью LoadLibrary . Динамический TLS, с использованием TlsAlloc ,, TlsFree TlsGetValue и TlsSetValue , по-прежнему доступен для использования в статических DLL-библиотеках или в DLLs, загружаемых с задержкой.

Повторно инициализируйте указатели статических глобальных функций на импортированные функции после первого вызова каждой функции. Это необходимо потому, что первое использование указателя функции указывает на преобразователь, а не на загруженную функцию.

В настоящее время невозможно отложить загрузку отдельных процедур из библиотеки DLL при использовании стандартного механизма импорта.

Пользовательские соглашения о вызовах (например, использование кодов условий на архитектурах x86) не поддерживаются. Кроме того, регистры с плавающей запятой не сохраняются на какой-либо платформе. Примите во внимание, если пользовательские вспомогательные подпрограммы или подпрограммы-обработчики используют типы с плавающей запятой: подпрограммы должны сохранять и восстанавливать полное состояние с плавающей запятой на компьютерах, использующих соглашения о вызовах с плавающей запятой. Будьте внимательны относительно загрузки DLL CRT, особенно при вызове функций CRT, которые принимают параметры с плавающей запятой в стеке обработчика числовых данных (NDP) в функции справки.

Когда загруженная динамическая библиотека больше не нужна - ее можно освободить, вызвав функцию BOOL FreeLibrary(HMODULE hLibModule) и передав ей дескриптор библиотеки, ранее возвращенный функцией LoadLibrary. Обратите внимание - DLL можно именно освободить, но не выгрузить! Выгрузка DLL из памяти не гарантируется, даже если работу с ней завершили все ранее загрузившие ее процессы.

Задержка выгрузки предусмотрена специально - на тот случай, если эта же DLL через некоторое время вновь понадобится какому-то процессу. Такой трюк оптимизирует работу часто используемых динамических библиотек, но плохо подходит для редко используемых DLL, загружаемых лишь однажды на короткое время. Никаких документированных способов насильно выгрузить динамическую библиотеку из памяти нет; а те, что есть - работают с ядром на низком уровне и не могут похвастаться переносимостью. Поэтому здесь мы их рассматривать не будем. К тому же - тактика освобождения и выгрузки DLL по-разному реализована в каждой версии Windows: Microsoft, стремясь подобрать наилучшую стратегию, непрерывно изменяет этот алгоритм; а потому и отказывается его документировать.

Нельзя не обратить внимания на одно очень важное обстоятельство: динамическая библиотека не владеет никакими ресурсами - ими владеет, независимо от способа компоновки, загрузивший ее процесс. Динамическая библиотека может открывать файлы, выделять память и т. д., но память не будет автоматически освобождена после вызова FreeLibrary, а файлы не окажутся сами собой закрыты - все это произойдет лишь после завершения процесса, но не раньше! Естественно, если программист сам не освободит все ненужные ресурсы вручную, с помощью функций CloseHandle, FreeMemory и подобных им.

Если функция FreeLibrary пропущена, DLL освобождается (но не факт, что выгружается!) только после завершения вызвавшего процесса. Могут возникнуть сомнения: раз FreeLibrary немедленно не выгружает динамическую библиотеку из памяти, так зачем она вообще нужна? Не лучше ли тогда все пустить на самотек - все равно ведь загруженные DLL будут гарантированно освобождены после завершения процесса? Что ж, доля правды тут есть, и автор сам порой так и поступает; но при недостатке памяти операционная система может беспрепятственно использовать место, занятое освобожденными динамическими библиотеками под что-то полезное - а если DLL еще не освобождены, их придется "скидывать" в файл подкачки, теряя драгоценное время. Поэтому лучше освобождайте DLL сразу же после их использования!

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

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

VOID FreeLibraryAndExitThread( HlNSTANCE hinstDll, DWORD dwExitCode);

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

VOID FreeLibraryAndExitThread(HINSTANCE hinstDll, DWORD dwExitCode)

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

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

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

На самом деле 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");

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

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

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

HINSTANCE GetModuleHandle(PCTSTR pszModuleName);

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

HINSTANCE hinstDll = GetHoduleHandle("MyLib");

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

// подразумевается расширение .dll

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

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

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

С помощью реестра можно включить автоматическую выгрузку DLL-файлов. Ее целесообразность обусловлена тем, что интервал времени, через который оболочка Windows выгружает неиспользуемые DLL-файлы, может быть достаточно большим (например, при отладке программ).

Примечание

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

Чтобы включить режим автоматической выгрузки DLL-файлов, необходимо в разделе реестра HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionExplorer создать строковый параметр AlwaysUnloadDLL и присвоить ему значение 1. Соответствующий REG-файл будет выглядеть следующим образом (расположение на компакт-диске – Файлы реестраРежимыAlwaysUnloadDLL.reg):

Windows Registry Editor Version 5.00

После внесения в реестр указанных изменений и последующей перезагрузки компьютера будет включен режим автоматической выгрузки всех DLL-файлов (в том числе и неиспользуемых).

Данный текст является ознакомительным фрагментом.

Продолжение на ЛитРес

9.3. Автоматическая нумерация и списки

9.3. Автоматическая нумерация и списки В CSS существует два свойства для управления нумерацией: counter-increment и counter-reset. Счетчики, которые определены данными свойствами, используются функциями counter() и counters() свойства content. Рассмотрим подробно свойства для управления

11.4. Автоматическая установка Windows Vista

11.4. Автоматическая установка Windows Vista Пакет Windows AIKЕсли вам приходилось устанавливать одну и ту же операционную систему на несколько компьютеров, то вам, вероятно, надоедало постоянно отвечать на одни и те же вопросы. Windows Vista имеет специальные средства для установки

OID, их автоматическая генерация и перезагрузки

OID, их автоматическая генерация и перезагрузки При создании конфигурационного файла, и при добавлении юнитов вручную oid можно не указывать. При этом значение oid генерируется автоматически, так что если набрать show config, значения oid выставятся каким–то случайным образом.

9.1.2 Выгрузка процессов

9.1.2 Выгрузка процессов Ядро выгружает процесс, если испытывает потребность в свободной памяти, которая может возникнуть в следующих случаях:1. Произведено обращение к системной функции fork, которая должна выделить место в памяти для процесса-потомка.2. Произведено

9.1.2.1 Выгрузка при выполнении системной функции fork

9.1.2.1 Выгрузка при выполнении системной функции fork В описании системной функции fork (раздел 7.1) предполагалось, что процесс-родитель получил в свое распоряжение память, достаточную для создания контекста потомка. Если это условие не выполняется, ядро выгружает процесс из

9.1.2.2 Выгрузка с расширением

9.1.2.2 Выгрузка с расширением Если процесс испытывает потребность в дополнительной физической памяти, либо в результате расширения стека, либо в результате запуска функции brk, и если эта потребность превышает доступные резервы памяти, ядро выполняет операцию выгрузки

Автоматическая дефрагментация

Автоматическая дефрагментация Одним из нововведений Windows XP является автоматическая дефрагментация файловой системы при простое компьютера в течение определенного промежутка времени (10-30 минут). При этом по умолчанию также выполняется дефрагментация загрузочного

Выгрузка библиотек при выходе из программы

Выгрузка библиотек при выходе из программы По умолчанию система при завершении работы программы оставляет в памяти ее библиотеки на случай, если через некоторое время она снова будет запущена. С одной стороны, плюс этого метода очевиден — следующий запуск будет

Загрузка и выгрузка файлов посредством FTP

Загрузка и выгрузка файлов посредством FTP Поговорим о том, как можно выгрузить свои файлы на удаленный сервер Интернета, чтобы их потом могли загружать другие, а также рассмотрим еще один способ загрузки файлов на свой компьютер, не связанный с использованием браузеров и

Автоматическая проверка

Автоматическая проверка При автоматическом контроле орфографии и грамматики Word проверяет в тексте ошибки непосредственно при наборе. В таком случае сразу после того, как слово или предложение набрано, видно, допущена ошибка или нет: программа подчеркивает слова,

Программная выгрузка доменов приложения

II. Автоматическая корректировка текста

II. Автоматическая корректировка текста Для корректировки текста можно использовать либо интерактивные инструменты (см. ниже), либо – автоматические. Рассмотрим их подробнее.

2.3.6. Динамическая загрузка и выгрузка

2.3.6. Динамическая загрузка и выгрузка Иногда на этапе выполнения программы требуется загрузить некоторый код без явной компоновки. Рассмотрим приложение, поддерживающее подключаемые модули: Web-броузер. Архитектура броузера позволяет сторонним разработчикам создавать

Автоматическая смена обоев

Автоматическая смена обоев Украсить Рабочий стол, не устанавливая специальные программы для визуализации интерфейса, можно стандартными обоями. Используя для этого высококачественные изображения, можно фактически каждый день изменять внешний вид своего Рабочего

Загрузка и выгрузка фильмов Shockwave/Flash и изображений в формате JPEG

Загрузка и выгрузка фильмов Shockwave/Flash и изображений в формате JPEG Проще всего загрузить в основной фильм, воспроизводящийся в окне проигрывателя Flash, другой фильм Shockwave/Flash или изображение в формате JPEG из внешнего файла (загружаемого фильма). Такое часто делается, особенно


13.4k 12 12 золотых знаков 55 55 серебряных знаков 121 121 бронзовый знак

Система не настолько умна.. Будучи загруженной библиотека остается загруженной пока счетчик ссылок не обнулится. Кроме FreeLibrary может быть вызывается какой-нить CloseHandle. P.S. Чудес не бывает. Нужно внимательно и критично изучить все, что происходит с хендлом, возвращаемым из LoadLibrary

В самом деле, минусовать непонятные вопроы (даже если автор заблуждается) нехорошо. @Asen, то что Вы описали это интересно (плюсую). Реально многие пользователи отмечают странности в поведении разных программ в Windows при выходе из сна (на notebook). С чем это связано - не знаю. Могу предположить, что наблюдаемое @Asen "пропадание" dll из той же оперы. Места в комментарии как всегда мало

-продолжение- Это только предположение, основано на умозрительных построениях. -- Допустим, система при работе изредка "теряет" некоторые страницы. Т.е. они остаются в таблицах адресного пространства процесса, но исчезают из списка размещенных физических страниц и не попадают в список занятых. (Список условное название, реализация м.б. совсем другой). Тогда, при засыпании системы, их содержимое не будет выгружено на диск. Соответственно не будет восстановлено при пробуждении, и что реально будет в ОЗУ непредсказуемо. -- Еще раз по поводу вопроса - требуется тщательное изучение.

@avp Слабо верится, что при реализации Hibernate в Microsoft допустили возможность непредсказуемого состояния ОЗУ в момент восстановления из этого самого Hibernate . Недетерминированное состояние в этом случае в 204904590 раз критичнее, скажем, обычной утечки памяти.

@karmadro4, рад за Вас. Конфигуратор от HP проделывает эти фокусы регулярно. @Котик, охотно верю, что объяснение неработоспособности после Hibernate м.б. другим. Кстати, "фокусы" в Windows возникают и просто при интенсивной работе, явно вызывающей "тяжелый" свопинг.

Читайте также: