Вспомогательная функция для отложенной загрузки
Вспомогательной функцией для отложенной загрузки, поддерживаемой компоновщиком, является то, что фактически загружает библиотеку DLL во время выполнения. Вы можете изменить вспомогательную функцию, чтобы настроить его поведение. Вместо использования предоставленной вспомогательной функции, напишите собственную функцию delayimp.lib
и свяжите ее с программой. Одна вспомогающая функция обслуживает все загруженные библиотеки DLL с задержкой.
Вы можете указать собственную версию вспомогательной функции, если вы хотите выполнить определенную обработку на основе имен библиотеки DLL или импорта.
Вспомогательная функция выполняет следующие действия:
Проверяет хранимый дескриптор библиотеки, чтобы узнать, загружен ли он уже
Вызовы
LoadLibrary
для попытки загрузить библиотеку DLLВызовы
GetProcAddress
для получения адреса процедурыВозвращается в точку импорта задержки импорта, чтобы вызвать загруженную точку входа
Вспомогательная функция может вернуться к перехватчику уведомлений в программе после каждого из следующих действий:
При запуске вспомогательной функции
LoadLibrary
Перед вызовом в вспомогательной функцииGetProcAddress
Перед вызовом в вспомогательной функцииЕсли вызов в вспомогательной функции завершается
LoadLibrary
сбоемЕсли вызов в вспомогательной функции завершается
GetProcAddress
сбоемПосле завершения обработки вспомогательной функции
Каждая из этих точек перехватчика может возвращать значение, которое изменяет обычную обработку вспомогательной подпрограммы каким-то образом, за исключением возврата к задержке импорта нагрузки.
Вспомогательный код по умолчанию можно найти в delayhlp.cpp
каталоге MSVC include
и delayimp.h
в ней. Он компилируется delayimp.lib
в каталог MSVC lib
для целевой архитектуры. Вам потребуется включить эту библиотеку в компиляции, если вы не напишете собственную вспомогательные функции.
Задержка вспомогательных соглашений о вызовах, параметров и возвращаемых типов
Прототип вспомогательной подпрограммы задержки загрузки:
FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd,
FARPROC * ppfnIATEntry
);
Параметры
pidd
Указатель const
на ImgDelayDescr
объект, содержащий смещения различных данных, связанных с импортом, метку времени для сведений о привязке и набор атрибутов, которые предоставляют дополнительные сведения о содержимом дескриптора. В настоящее время существует только один атрибут, dlattrRva
который указывает, что адреса в дескрипторе являются относительными виртуальными адресами. Дополнительные сведения см. в объявлениях.delayimp.h
Указатели дескриптора задержки (ImgDelayDescr
в delayimp.h
) используют относительные виртуальные адреса (RVAs) для работы как ожидалось в 32-разрядных и 64-разрядных программах. Чтобы использовать их, преобразуйте эти RVA обратно в указатели с помощью функции PFromRva
, найденной в delayhlp.cpp
. Эту функцию можно использовать для каждого поля дескриптора, чтобы преобразовать их обратно в 32-разрядные или 64-разрядные указатели. Вспомогательной функцией задержки загрузки по умолчанию является хороший шаблон, используемый в качестве примера.
Определение структуры см. в PCImgDelayDescr
разделе "Структура" и "Константные определения".
ppfnIATEntry
Указатель на слот в таблице адресов импорта задержки загрузки (IAT). Это слот, который обновляется с адресом импортированной функции. Вспомогательный подпрограмма должна хранить то же значение, что и в этом расположении.
Ожидаемые возвращаемые значения
Если вспомогающая функция выполнена успешно, он возвращает адрес импортированной функции.
Если функция завершается ошибкой, она вызывает структурированное исключение и возвращает значение 0. Возможны три типа исключений.
Недопустимый параметр. Это исключение создается, если атрибуты в
pidd
указаны некорректно. Эта ошибка рассматривается как неустранимая ошибка.Функции
LoadLibrary
не удалось загрузить указанную библиотеку DLL.Ошибка в функции
GetProcAddress
.
Это ваша ответственность за обработку этих исключений. Дополнительные сведения см. в разделе об обработке ошибок и уведомлениях.
Замечания
Для вспомогательной функции используется соглашение о вызовах __stdcall
. Тип возвращаемого значения не имеет значения, поэтому FARPROC
используется. Эта функция имеет компоновку C, что означает, что она должна быть упакована extern "C"
при объявлении в коде C++. Макрос ExternC
заботится об этом оболочке.
Чтобы использовать вспомогательный подпрограмму в качестве перехватчика уведомлений, код должен указать соответствующий указатель функции для возврата. Затем код преобразователя, создаваемый компоновщиком, принимает это возвращаемое значение в качестве действительной цели импорта и переходит непосредственно к ней. Если вы не хотите использовать вспомогательскую подпрограмму в качестве перехватчика уведомлений, сохраните возвращаемое значение вспомогательной функции в ppfnIATEntry
расположении указателя функции, передаваемой функции.
Пример функции перехватчика
В следующем коде показано, как реализовать базовую функцию перехватчика.
FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
switch (dliNotify) {
case dliStartProcessing :
// If you want to return control to the helper, return 0.
// Otherwise, return a pointer to a FARPROC helper function
// that will be used instead, thereby bypassing the rest
// of the helper.
break;
case dliNotePreLoadLibrary :
// If you want to return control to the helper, return 0.
// Otherwise, return your own HMODULE to be used by the
// helper instead of having it call LoadLibrary itself.
break;
case dliNotePreGetProcAddress :
// If you want to return control to the helper, return 0.
// If you choose you may supply your own FARPROC function
// address and bypass the helper's call to GetProcAddress.
break;
case dliFailLoadLib :
// LoadLibrary failed.
// If you don't want to handle this failure yourself, return 0.
// In this case the helper will raise an exception
// (ERROR_MOD_NOT_FOUND) and exit.
// If you want to handle the failure by loading an alternate
// DLL (for example), then return the HMODULE for
// the alternate DLL. The helper will continue execution with
// this alternate DLL and attempt to find the
// requested entrypoint via GetProcAddress.
break;
case dliFailGetProc :
// GetProcAddress failed.
// If you don't want to handle this failure yourself, return 0.
// In this case the helper will raise an exception
// (ERROR_PROC_NOT_FOUND) and exit.
// If you choose, you may handle the failure by returning
// an alternate FARPROC function address.
break;
case dliNoteEndProcessing :
// This notification is called after all processing is done.
// There is no opportunity for modifying the helper's behavior
// at this point except by longjmp()/throw()/RaiseException.
// No return value is processed.
break;
default :
return NULL;
}
return NULL;
}
/*
and then at global scope somewhere:
ExternC const PfnDliHook __pfnDliNotifyHook2 = delayHook;
ExternC const PfnDliHook __pfnDliFailureHook2 = delayHook;
*/
Задержка структуры нагрузки и определений констант
В вспомогательной подпрограмме задержки загрузки по умолчанию используется несколько структур для взаимодействия с функциями перехватчика и во время любых исключений. Эти структуры определены в delayimp.h
. Ниже приведены макросы, typedefs, значения уведомлений и сбоев, информационные структуры и тип функции указателя на перехватчик:
#define _DELAY_IMP_VER 2
#if defined(__cplusplus)
#define ExternC extern "C"
#else
#define ExternC extern
#endif
typedef IMAGE_THUNK_DATA * PImgThunkData;
typedef const IMAGE_THUNK_DATA * PCImgThunkData;
typedef DWORD RVA;
typedef struct ImgDelayDescr {
DWORD grAttrs; // attributes
RVA rvaDLLName; // RVA to dll name
RVA rvaHmod; // RVA of module handle
RVA rvaIAT; // RVA of the IAT
RVA rvaINT; // RVA of the INT
RVA rvaBoundIAT; // RVA of the optional bound IAT
RVA rvaUnloadIAT; // RVA of optional copy of original IAT
DWORD dwTimeStamp; // 0 if not bound,
// O.W. date/time stamp of DLL bound to (Old BIND)
} ImgDelayDescr, * PImgDelayDescr;
typedef const ImgDelayDescr * PCImgDelayDescr;
enum DLAttr { // Delay Load Attributes
dlattrRva = 0x1, // RVAs are used instead of pointers
// Having this set indicates a VC7.0
// and above delay load descriptor.
};
//
// Delay load import hook notifications
//
enum {
dliStartProcessing, // used to bypass or note helper only
dliNoteStartProcessing = dliStartProcessing,
dliNotePreLoadLibrary, // called just before LoadLibrary, can
// override w/ new HMODULE return val
dliNotePreGetProcAddress, // called just before GetProcAddress, can
// override w/ new FARPROC return value
dliFailLoadLib, // failed to load library, fix it by
// returning a valid HMODULE
dliFailGetProc, // failed to get proc address, fix it by
// returning a valid FARPROC
dliNoteEndProcessing, // called after all processing is done, no
// bypass possible at this point except
// by longjmp()/throw()/RaiseException.
};
typedef struct DelayLoadProc {
BOOL fImportByName;
union {
LPCSTR szProcName;
DWORD dwOrdinal;
};
} DelayLoadProc;
typedef struct DelayLoadInfo {
DWORD cb; // size of structure
PCImgDelayDescr pidd; // raw form of data (everything is there)
FARPROC * ppfn; // points to address of function to load
LPCSTR szDll; // name of dll
DelayLoadProc dlp; // name or ordinal of procedure
HMODULE hmodCur; // the hInstance of the library we have loaded
FARPROC pfnCur; // the actual function that will be called
DWORD dwLastError;// error received (if an error notification)
} DelayLoadInfo, * PDelayLoadInfo;
typedef FARPROC (WINAPI *PfnDliHook)(
unsigned dliNotify,
PDelayLoadInfo pdli
);
Вычисление необходимых значений для задержки загрузки
Вспомогательный подпрограмма задержки загрузки должна вычислить две критически важные части информации. Чтобы помочь, есть две встроенные функции delayhlp.cpp
для вычисления этих сведений.
Во-первых,
IndexFromPImgThunkData
вычисляет индекс текущего импорта в три разные таблицы (импорт таблицы адресов (IAT), привязанную таблицу адресов импорта (BIAT) и таблицу адресов импорта (UIAT)).Во-вторых,
CountOfImports
подсчитывает количество импорта в допустимом IAT.
// utility function for calculating the index of the current import
// for all the tables (INT, BIAT, UIAT, and IAT).
__inline unsigned
IndexFromPImgThunkData(PCImgThunkData pitdCur, PCImgThunkData pitdBase) {
return pitdCur - pitdBase;
}
// utility function for calculating the count of imports given the base
// of the IAT. NB: this only works on a valid IAT!
__inline unsigned
CountOfImports(PCImgThunkData pitdBase) {
unsigned cRet = 0;
PCImgThunkData pitd = pitdBase;
while (pitd->u1.Function) {
pitd++;
cRet++;
}
return cRet;
}
Поддержка выгрузки библиотеки DLL с задержкой
При загрузке загруженной библиотеки DLL по умолчанию вспомогательный помощник по задержке загрузки проверяет, есть ли дескрипторы задержки и копия исходной таблицы адресов импорта (IAT) в pUnloadIAT
поле. В этом случае вспомогательный элемент сохраняет указатель в списке дескриптор задержки импорта. Эта запись позволяет вспомогательной функции найти библиотеку DLL по имени, чтобы поддерживать выгрузку библиотеки DLL явным образом.
Ниже приведены связанные структуры и функции для явной выгрузки библиотеки DLL с задержкой.
//
// Unload support from delayimp.h
//
// routine definition; takes a pointer to a name to unload
ExternC
BOOL WINAPI
__FUnloadDelayLoadedDLL2(LPCSTR szDll);
// structure definitions for the list of unload records
typedef struct UnloadInfo * PUnloadInfo;
typedef struct UnloadInfo {
PUnloadInfo puiNext;
PCImgDelayDescr pidd;
} UnloadInfo;
// from delayhlp.cpp
// the default delay load helper places the unloadinfo records in the
// list headed by the following pointer.
ExternC
PUnloadInfo __puiHead;
Структура UnloadInfo
реализуется с помощью класса C++, который использует LocalAlloc
и LocalFree
реализуется в качестве его operator new
и operator delete
соответственно. Эти параметры хранятся в стандартном связанном списке, который используется __puiHead
в качестве главы списка.
При вызове __FUnloadDelayLoadedDLL
он пытается найти имя, указанное в списке загруженных БИБЛИОТЕК DLL. (Требуется точное совпадение.) При обнаружении копия IAT копируется в верхней части запущенного IAT pUnloadIAT
для восстановления указателей на thunk. Затем библиотека освобождается с помощью FreeLibrary
, соответствующая UnloadInfo
запись не связана со списком и удаляется и TRUE
возвращается.
Аргумент функции __FUnloadDelayLoadedDLL2
учитывает регистр. Например, можно указать:
__FUnloadDelayLoadedDLL2("user32.dll");
и нет:
__FUnloadDelayLoadedDLL2("User32.DLL");
Пример выгрузки библиотеки DLL, загруженной с задержкой, см. в статье Явно выгрузить библиотеку DLL с задержкой.
Разработка собственной вспомогательной функции задержки загрузки
Возможно, потребуется предоставить собственную версию вспомогательной подпрограммы задержки загрузки. В собственной подпрограмме можно выполнять конкретную обработку на основе имен библиотеки DLL или импорта. Существует два способа вставки собственного кода: код собственной вспомогательной функции, возможно, на основе предоставленного кода. Или перехватчик предоставленной вспомогательной функции, чтобы вызвать собственную функцию с помощью перехватчиков уведомлений.
Код собственного вспомогательного помощника
Создание собственной вспомогательной подпрограммы просто. Существующий код можно использовать в качестве руководства для новой функции. Функция должна использовать те же соглашения о вызовах, что и существующий вспомогательный элемент. И, если он возвращается в созданные компоновщиком thunks, он должен вернуть правильный указатель функции. После создания кода можно либо выполнить вызов, либо выйти из вызова, но вам нравится.
Использование перехватчика уведомлений о начале обработки
Вероятно, проще всего указать новый указатель на функцию перехватчика уведомлений, предоставляемую пользователем, которая принимает те же значения, что и вспомогательный элемент по умолчанию для dliStartProcessing
уведомления. На этом этапе функция перехватчика может по сути стать новой вспомогательной функцией, так как успешное возвращение вспомогательной функции по умолчанию проходит все дальнейшие обработки в вспомогательном приложении по умолчанию.