Descripción de la función del asistente para retrasar cargas
La función auxiliar para la carga retrasada compatible con el enlazador es lo que realmente carga el archivo DLL en el runtime. Se puede modificar la función auxiliar para personalizar su comportamiento. En lugar de usar la función auxiliar proporcionada en delayimp.lib
, escriba su propia función y vincule al programa. Una función auxiliar sirve a todas las DLL cargadas con retraso.
Se puede proporcionar su propia versión de la función auxiliar si se desea realizar un procesamiento específico en función de los nombres de la DLL o las importaciones.
La función auxiliar realiza estas acciones:
Comprueba el manipulador almacenado en la biblioteca para ver si ya se ha cargado
Llama a
LoadLibrary
para intentar cargar la DLLLlama a
GetProcAddress
para intentar obtener la dirección del procedimientoVuelve al retraso de carga de la importación del código thunk para llamar al punto de entrada ahora cargado
La función auxiliar puede volver a llamar a un enlace de notificación en el programa después de cada una de las siguientes acciones:
Cuando se inicia la función auxiliar
Justo antes de llamar a
LoadLibrary
en la función auxiliarJusto antes de llamar a
GetProcAddress
en la función auxiliarSi se produce un error en la llamada a
LoadLibrary
en la función auxiliarSi se produce un error en la llamada a
GetProcAddress
en la función auxiliarDespués de que la función auxiliar se haya terminado de procesar
Cada uno de estos puntos de enlace puede devolver un valor que modifica el procesamiento normal de la rutina auxiliar de alguna manera, excepto el retorno a la carga de importación retrasada del código thunk.
El código auxiliar predeterminado se puede encontrar en delayhlp.cpp
y delayimp.h
en el directorio MSVC include
. Se compila en delayimp.lib
del directorio MSVC lib
para la arquitectura de destino. Deberá incluir esta biblioteca en las compilaciones a menos que se escriba su propia función auxiliar.
Convenciones de llamada, parámetros y tipo de valor devuelto del asistente de carga retrasada
El prototipo de la rutina del auxiliar de carga retrasada es:
FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd,
FARPROC * ppfnIATEntry
);
Parámetros
pidd
Un puntero const
a una ImgDelayDescr
que contiene los desplazamientos de diversos datos relacionados con la importación, una marca de tiempo para enlazar información y un conjunto de atributos que proporcionan más información sobre el contenido del descriptor. Actualmente, solo hay un atributo, dlattrRva
, que indica que las direcciones del descriptor son direcciones virtuales relativas. Para más información, consulte las declaraciones en delayimp.h
.
Los punteros del descriptor de retraso (ImgDelayDescr
en delayimp.h
) usan direcciones virtuales relativas (RVA) para funcionar según lo previsto en programas de 32 y 64 bits. Para usarlos, vuelva a convertir estas RVA en punteros mediante la función PFromRva
, que se encuentra en delayhlp.cpp
. Puede usar esta función en cada uno de los campos del descriptor para convertirlos nuevamente en punteros de 32 o 64 bits. La función auxiliar de carga con retraso predeterminada es una buena plantilla que se usará como ejemplo.
Para obtener la definición de la estructura PCImgDelayDescr
, consulte Definiciones de estructura y constantes.
ppfnIATEntry
Puntero a una ranura en la tabla de direcciones de importación de carga retrasada (IAT). Es la ranura que se actualiza con la dirección de la función importada. La rutina del asistente tiene que almacenar el mismo valor que se devuelve en esta ubicación.
Valores devueltos esperados
Si la función del asistente se ejecuta correctamente, devolverá la dirección de la función importada.
Si la función no se ejecuta correctamente, generará una excepción estructurada y devolverá 0. Se pueden generar tres tipos de excepciones:
Parámetro no válido, que se produce si los atributos de
pidd
no se especifican correctamente. Trate esto como un error irrecuperable.Error de
LoadLibrary
en la DLL especificada.Error de
GetProcAddress
.
Es su responsabilidad administrar estas excepciones. Para más información, consulte Control de errores y notificación.
Comentarios
La convención de llamada de la función del asistente es __stdcall
. El tipo del valor devuelto no es relevante, de modo que se usa FARPROC
. Esta función tiene vinculación de C, lo que significa que debe encapsularse mediante extern "C"
cuando se declara en código de C++. La macro ExternC
se encarga de este contenedor.
Para usar la rutina auxiliar como un enlace de notificación, el código debe especificar el puntero de función adecuado que va a devolverse. Entonces, el código thunk generado por el enlazador tomará ese valor devuelto como el destino real de la importación y saltará a él directamente. Si no se desea usar la rutina auxiliar como enlace de notificación, almacene el valor devuelto de la función auxiliar en ppfnIATEntry
, la ubicación del puntero de función pasada.
Ejemplo de función de enlace
El siguiente código muestra cómo implementar una función de enlace básica.
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;
*/
Estructura de carga retrasada y definiciones constantes
La rutina auxiliar de carga de retraso predeterminada usa varias estructuras para comunicarse con las funciones de enlace y durante las excepciones. Estas estructuras se describen en delayimp.h
. Estas son las macros, las definiciones de tipo, los valores de notificación y error, las estructuras de información y el tipo de función puntero a enlace que se pasa a los enlaces:
#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
);
Calcular los valores necesarios para retrasar la carga
La rutina del asistente de carga de retraso debe calcular dos fragmentos críticos de información. Para ayudar, hay dos funciones insertadas en delayhlp.cpp
para calcular esta información.
La primera,
IndexFromPImgThunkData
, calcula el índice de la importación actual en tres tablas diferentes (tabla de direcciones de importación (IAT), tabla de direcciones de importación enlazada (BIAT) y tabla de direcciones de importación sin enlazar (UIAT)).La segunda,
CountOfImports
, cuenta el número de importaciones en un IAT válido.
// 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;
}
Compatibilidad con la descarga de una DLL cargada con retraso
Cuando se carga una DLL cargada con retraso, el asistente de carga retrasada predeterminado comprueba si los descriptores de carga retrasada tienen un puntero y una copia de la tabla de direcciones de importación original (IAT) en el campo pUnloadIAT
. Si es así, el asistente guarda un puntero en una lista en el descriptor de retraso de importación. Esta entrada permite que la función auxiliar busque la DLL por nombre para admitir la descarga explícita de esa DLL.
Estas son las estructuras y funciones asociadas para descargar explícitamente una DLL cargada con retraso:
//
// 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;
La estructura UnloadInfo
se implementa mediante una clase de C++ que usa las implementaciones LocalAlloc
y LocalFree
como sus operator new
y operator delete
, respectivamente. Estas opciones se mantienen en una lista de vínculo estándar que usa __puiHead
como encabezado de la lista.
Cuando se llama a __FUnloadDelayLoadedDLL
, se intenta encontrar el nombre que se proporciona en la lista de las DLL cargadas. (Se requiere una coincidencia exacta). Si se encuentra, la copia del IAT en pUnloadIAT
se copia sobre la parte superior de la IAT en ejecución para restaurar los punteros de código thunk. A continuación, la biblioteca se libera mediante FreeLibrary
, el registro coincidente UnloadInfo
se desvincula de la lista y se elimina y se devuelve TRUE
.
El argumento de la función __FUnloadDelayLoadedDLL2
distingue mayúsculas de minúsculas. Por ejemplo, se podría especificar:
__FUnloadDelayLoadedDLL2("user32.dll");
y no:
__FUnloadDelayLoadedDLL2("User32.DLL");
Para obtener un ejemplo de descarga de una DLL cargada con retraso, consulte Descarga explícita de una DLL cargada con retraso.
Desarrolle su propia función del asistente de carga retrasada
Es posible que desee proporcionar su propia versión de la rutina del asistente de carga retrasada. En su propia rutina, puede realizar un procesamiento específico en función de los nombres de la DLL o las importaciones. Hay dos maneras de insertar su propio código: codifique su propia función auxiliar, posiblemente en función del código proporcionado. O bien, enlace el asistente proporcionado para llamar a su propia función mediante los enlaces de notificación.
Codificar su propio asistente
El crear su propia rutina auxiliar es algo sencillo. Puede usar el código existente como una guía para la nueva función. La función debe usar las mismas convenciones de llamada que el asistente existente. Y, si vuelve a los códigos thunks generados por el enlazador, debe devolver un puntero de función adecuado. Una vez que haya creado el código, puede satisfacer la llamada o salir de ella, como guste.
Uso del enlace de notificación de inicio del procesamiento
Probablemente sea más fácil proporcionar un nuevo puntero a una función de enlace de notificación proporcionada por el usuario que toma los mismos valores que el asistente predeterminado para la notificación dliStartProcessing
. En ese momento, la función de enlace puede convertirse esencialmente en la nueva función auxiliar, ya que un retorno correcto al asistente predeterminado omite todo el procesamiento adicional en el asistente predeterminado.
Consulte también
Compatibilidad del vinculador con archivos DLL cargados con retraso