Compartir a través de


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 DLL

  • Llama a GetProcAddress para intentar obtener la dirección del procedimiento

  • Vuelve 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 auxiliar

  • Justo antes de llamar a GetProcAddress en la función auxiliar

  • Si se produce un error en la llamada a LoadLibrary en la función auxiliar

  • Si se produce un error en la llamada a GetProcAddress en la función auxiliar

  • Despué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