Informazioni sulla funzione helper di caricamento ritardato
La funzione helper per il caricamento ritardato supportato dal linker è ciò che carica effettivamente la DLL in fase di esecuzione. È possibile modificare la funzione helper per personalizzarne il comportamento. Anziché usare la funzione helper fornita in delayimp.lib
, scrivere la propria funzione e collegarla al programma. Una funzione helper serve tutte le DLL caricate in ritardo.
È possibile specificare la propria versione della funzione helper se si vuole eseguire un'elaborazione specifica in base ai nomi della DLL o delle importazioni.
La funzione helper esegue queste azioni:
Controlla l'handle archiviato nella libreria per verificare se è già stato caricato
Chiamate
LoadLibrary
per tentare di caricare la DLLChiamate
GetProcAddress
per tentare di ottenere l'indirizzo della proceduraTorna al caricamento di caricamento ritardato di importazione per chiamare il punto di ingresso ora caricato
La funzione helper può richiamare un hook di notifica nel programma dopo ognuna delle azioni seguenti:
All'avvio della funzione helper
Appena prima
LoadLibrary
viene chiamato nella funzione helperAppena prima
GetProcAddress
viene chiamato nella funzione helperSe la chiamata a
LoadLibrary
nella funzione helper ha esito negativoSe la chiamata a
GetProcAddress
nella funzione helper ha esito negativoDopo aver completato l'elaborazione della funzione helper
Ognuno di questi punti hook può restituire un valore che modifica in qualche modo la normale elaborazione della routine helper, ad eccezione del ritorno al caricamento di caricamento ritardato di importazione.
Il codice helper predefinito è disponibile in delayhlp.cpp
e delayimp.h
nella directory MSVC include
. Viene compilato delayimp.lib
nella directory MSVC lib
per l'architettura di destinazione. Sarà necessario includere questa libreria nelle compilazioni, a meno che non si scriva la propria funzione helper.
Ritardare le convenzioni di chiamata dell'helper di caricamento, i parametri e il tipo restituito
Il prototipo per la routine helper di caricamento ritardato è:
FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd,
FARPROC * ppfnIATEntry
);
Parametri
pidd
Puntatore const
a un ImgDelayDescr
oggetto che contiene gli offset di vari dati correlati all'importazione, un timestamp per le informazioni di associazione e un set di attributi che forniscono ulteriori informazioni sul contenuto del descrittore. Attualmente è presente un solo attributo, dlattrRva
, che indica che gli indirizzi nel descrittore sono indirizzi virtuali relativi. Per altre informazioni, vedere le dichiarazioni in delayimp.h
.
I puntatori nel descrittore di ritardo (ImgDelayDescr
in delayimp.h
) usano indirizzi virtuali relativi (RVA) per funzionare come previsto nei programmi a 32 bit e a 64 bit. Per usarli, convertire di nuovo questi RVA in puntatori usando la funzione PFromRva
, disponibile in delayhlp.cpp
. È possibile usare questa funzione in ognuno dei campi del descrittore per convertirli di nuovo in puntatori a 32 bit o a 64 bit. La funzione helper di caricamento ritardo predefinita è un modello valido da usare come esempio.
Per la definizione della PCImgDelayDescr
struttura, vedere Struttura e definizioni costanti.
ppfnIATEntry
Puntatore a uno slot nella tabella degli indirizzi di importazione del caricamento ritardato (IAT). Si tratta dello slot aggiornato con l'indirizzo della funzione importata. La routine helper deve archiviare lo stesso valore restituito in questa posizione.
Valori restituiti previsti
Se la funzione helper ha esito positivo, restituisce l'indirizzo della funzione importata.
Se la funzione ha esito negativo, genera un'eccezione strutturata e restituisce 0. Possono essere generati tre tipi di eccezioni:
Parametro non valido, se gli attributi in
pidd
non sono specificati correttamente. Considera questa operazione come un errore irreversibile.LoadLibrary
: impossibile leggere la DLL specificata.Errore di
GetProcAddress
.
È responsabilità dell'utente gestire queste eccezioni. Per altre informazioni, vedere Gestione degli errori e notifica.
Osservazioni:
La convenzione di chiamata per la funzione helper è __stdcall
. Il tipo del valore restituito non è rilevante, quindi FARPROC
viene usato. Questa funzione ha un collegamento C, il che significa che deve essere sottoposto a wrapping da extern "C"
quando dichiarato nel codice C++. La ExternC
macro si occupa automaticamente di questo wrapper.
Per usare la routine helper come hook di notifica, il codice deve specificare il puntatore alla funzione appropriato da restituire. Il codice thunk generato dal linker accetta quindi quel valore restituito come destinazione effettiva dell'importazione e passa direttamente ad essa. Se non si vuole usare la routine helper come hook di notifica, archiviare il valore restituito della funzione helper in ppfnIATEntry
, la posizione del puntatore a funzione passata.
Funzione hook di esempio
Il codice seguente illustra come implementare una funzione hook di base.
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;
*/
Struttura di caricamento ritardata e definizioni costanti
La routine helper di caricamento ritardo predefinita usa diverse strutture per comunicare con le funzioni hook e durante eventuali eccezioni. Queste strutture sono definite in delayimp.h
. Ecco le macro, i typedef, i valori di notifica e di errore, le strutture informative e il tipo di funzione puntatore a hook passati agli hook:
#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
);
Calcolare i valori necessari per il caricamento ritardato
La routine helper di caricamento ritardato deve calcolare due informazioni critiche. Per semplificare, sono disponibili due funzioni inline in delayhlp.cpp
per calcolare queste informazioni.
Il primo,
IndexFromPImgThunkData
, calcola l'indice dell'importazione corrente nelle tre diverse tabelle (tabella degli indirizzi di importazione (IAT), tabella degli indirizzi di importazione associata (BIAT) e tabella degli indirizzi di importazione non associata (UIAT)).Il secondo,
CountOfImports
, conta il numero di importazioni in un IAT valido.
// 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;
}
Supporto del caricamento di una DLL caricata in ritardo
Quando viene caricata una DLL con caricamento ritardato, l'helper di caricamento ritardato predefinito verifica se i descrittori di caricamento ritardato hanno un puntatore e una copia della tabella degli indirizzi di importazione originale (IAT) nel pUnloadIAT
campo. In tal caso, l'helper salva un puntatore in un elenco nel descrittore di ritardo di importazione. Questa voce consente alla funzione helper di trovare la DLL in base al nome, per supportare lo scaricamento esplicito della DLL.
Ecco le strutture e le funzioni associate per scaricare in modo esplicito una DLL con caricamento ritardato:
//
// 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 UnloadInfo
struttura viene implementata usando una classe C++ che usa LocalAlloc
e LocalFree
le implementazioni rispettivamente come operator new
e operator delete
. Queste opzioni vengono mantenute in un elenco collegato standard che usa __puiHead
come capo dell'elenco.
Quando si chiama __FUnloadDelayLoadedDLL
, tenta di trovare il nome specificato nell'elenco delle DLL caricate. È necessaria una corrispondenza esatta. Se viene trovata, la copia dell'IAT in pUnloadIAT
viene copiata sopra la parte superiore dell'IAT in esecuzione per ripristinare i puntatori di thunk. La libreria viene quindi liberata usando FreeLibrary
, il record corrispondente UnloadInfo
viene scollegato dall'elenco ed eliminato e TRUE
viene restituito.
L'argomento della funzione __FUnloadDelayLoadedDLL2
fa distinzione tra maiuscole e minuscole. Ad esempio, è necessario specificare:
__FUnloadDelayLoadedDLL2("user32.dll");
e non:
__FUnloadDelayLoadedDLL2("User32.DLL");
Per un esempio di scaricamento di una DLL con caricamento ritardato, vedere Scaricare in modo esplicito una DLL con caricamento ritardato.
Sviluppare una funzione helper di caricamento ritardata personalizzata
È possibile specificare la propria versione della routine helper di caricamento ritardato. Nella tua routine puoi eseguire un'elaborazione specifica in base ai nomi della DLL o delle importazioni. Esistono due modi per inserire il proprio codice: scrivere il codice della propria funzione helper, possibilmente in base al codice fornito. In alternativa, associare l'helper fornito per chiamare la propria funzione usando gli hook di notifica.
Codificare il proprio helper
La creazione di una routine helper personalizzata è semplice. È possibile usare il codice esistente come guida per la nuova funzione. La funzione deve usare le stesse convenzioni di chiamata dell'helper esistente. E, se torna ai batch generati dal linker, deve restituire un puntatore a funzione corretto. Dopo aver creato il codice, puoi soddisfare la chiamata o uscire dalla chiamata, ma ti piace.
Usare l'hook di notifica di avvio dell'elaborazione
È probabilmente più semplice fornire un nuovo puntatore a una funzione hook di notifica fornita dall'utente che accetta gli stessi valori dell'helper predefinito per la dliStartProcessing
notifica. A questo punto, la funzione hook può essenzialmente diventare la nuova funzione helper, perché un ritorno corretto all'helper predefinito ignora tutta l'ulteriore elaborazione nell'helper predefinito.