Entender a função auxiliar de atrasar carregamento
A função auxiliar para carregamento atrasado com suporte de vinculador é o que realmente carrega a DLL em tempo de execução. Você pode modificar a função auxiliar para personalizar seu comportamento. Em vez de usar a função auxiliar fornecida em delayimp.lib
, escreva sua própria função e vincule-a ao seu programa. Uma função auxiliar atende a todas as DLLs carregadas com atraso.
Você pode fornecer sua própria versão da função auxiliar se desejar fazer um processamento específico com base nos nomes da DLL ou importações.
A função auxiliar executa estas ações:
Verifica o identificador armazenado para a biblioteca para ver se já foi carregado
Chama
LoadLibrary
para tentar carregar a DLLChama
GetProcAddress
para tentar obter o endereço do procedimentoRetorna à conversão de carregamento de importação de atraso para chamar o ponto de entrada agora carregado
A função auxiliar pode chamar de volta para um gancho de notificação em seu programa após cada uma das seguintes ações:
Quando a função auxiliar inicia
Pouco antes
LoadLibrary
é chamado na função auxiliarPouco antes
GetProcAddress
é chamado na função auxiliarSe a chamada para
LoadLibrary
na função auxiliar falharSe a chamada para
GetProcAddress
na função auxiliar falharDepois que a função auxiliar terminar de processar
Cada um desses pontos de gancho pode retornar um valor que altera o processamento normal da rotina auxiliar de alguma maneira, exceto o retorno à conversão de carregamento de importação de atraso.
O código auxiliar padrão pode ser encontrado em delayhlp.cpp
e delayimp.h
no diretório MSVC include
. Ele é compilado em delayimp.lib
no diretório MSVC lib
para sua arquitetura de destino. Você precisará incluir essa biblioteca em suas compilações, a menos que escreva sua própria função auxiliar.
Convenções de chamada do auxiliar de carregamento de atraso, parâmetros e tipo de retorno
O protótipo para a rotina auxiliar de carregamento de atraso é:
FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd,
FARPROC * ppfnIATEntry
);
Parâmetros
pidd
Um ponteiro const
para um ImgDelayDescr
que contém os deslocamentos de vários dados relacionados à importação, um carimbo de data/hora para informações de vinculação e um conjunto de atributos que fornecem informações adicionais sobre o conteúdo do descritor. Atualmente há apenas um atributo, dlattrRva
, que indica que os endereços no descritor são endereços virtuais relativos. Para obter mais informações, consulte as declarações em delayimp.h
.
Os ponteiros no descritor de atraso (ImgDelayDescr
em delayimp.h
) usam endereços virtuais relativos (RVAs) para funcionar conforme o esperado em programas de 32 bits e 64 bits. Para usá-los, converta esses RVAs de volta em ponteiros usando a função PFromRva
, encontrada em delayhlp.cpp
. Você pode usar essa função em cada um dos campos no descritor para convertê-los de volta em ponteiros de 32 ou 64 bits. A função auxiliar de carregamento de atraso padrão é um bom modelo para usar como exemplo.
Para a definição da estrutura PCImgDelayDescr
, consulte Definições de estrutura e constante.
ppfnIATEntry
Um ponteiro para um slot na tabela de endereços de importação de carregamento de atraso (IAT). É o slot que é atualizado com o endereço da função importada. A rotina auxiliar precisa armazenar o mesmo valor que retorna neste local.
Valores de retorno esperados
Se a função auxiliar for bem-sucedida, ela retornará o endereço da função importada.
Se a função falhar, ela gera uma exceção estruturada e retorna 0. Há três tipos de exceções possíveis:
Parâmetro inválido, que acontece quando os parâmetros presentes em
pidd
não são especificados corretamente. Trate isso como um erro irrecuperável.LoadLibrary
falhou na DLL (biblioteca de vínculo dinâmico) especificada.Falha de
GetProcAddress
.
Lidar com essas exceções é sua responsabilidade. Para obter mais informações, consulte Tratamento e notificação de erros.
Comentários
A convenção de chamada da função do auxiliar é __stdcall
. O tipo de valor retornado não é relevante, portanto FARPROC
é usado. Essa função tem vinculação C, o que significa que ela precisa ser encapsulada por extern "C"
quando declarada no código C++. A macro ExternC
cuida desse wrapper para você.
Para usar a rotina auxiliar como um gancho de notificação, seu código deverá especificar o ponteiro de função apropriado a ser retornado. O código de conversão gerado pelo vinculador usa o valor retornado como destino real da importação e pula diretamente para ele. Se não quiser usar sua rotina auxiliar como um gancho de notificação, armazene o valor retornado da função auxiliar em ppfnIATEntry
, no local do ponteiro da função passada.
Exemplo de função de gancho
O código a seguir mostra como implementar uma função de gancho 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;
*/
Definições de estrutura atrasar carga e constantes
A rotina de auxiliar de atrasar carga padrão usa várias estruturas para se comunicar com as funções de gancho e durante exceções. Essas estruturas são definidas em delayimp.h
. Seguem as macros, typedefs, valores de notificação e falha, estruturas de informações e o tipo de função ponteiro a gancho passado para os ganchos:
#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 os valores necessários para o carregamento de atraso
A rotina auxiliar de carga de atraso precisa calcular duas informações críticas. Para ajudar, há duas funções embutidas em delayhlp.cpp
para calcular essas informações.
A primeira,
IndexFromPImgThunkData
, calcula o índice da importação atual para as três tabelas diferentes – IAT (tabela de endereços de importação), BIAT (tabela de endereços de importação associada) e UIAT (tabela de endereços de importação não associada).A segunda,
CountOfImports
, conta o número de importações em uma IAT válida.
// 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;
}
Dar suporte ao descarregamento de uma DLL carregada por atraso
Quando uma DLL carregada com atraso é carregada, o auxiliar de carregamento com atraso padrão verifica se os descritores de carregamento com atraso têm um ponteiro e uma cópia da tabela de endereços de importação original (IAT) no campo pUnloadIAT
. Nesse caso, o auxiliar salva um ponteiro em uma lista para o descritor de atraso de importação. Essa entrada permite que a função auxiliar encontre a DLL pelo nome, para ajudar a descarregar essa DLL explicitamente.
Aqui estão as estruturas e funções associadas para descarregar explicitamente uma DLL carregada com atraso:
//
// 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;
A estrutura UnloadInfo
é implementada usando uma classe C++ que usa implementações LocalAlloc
e LocalFree
como seus operator new
e operator delete
, respectivamente. Essas opções são mantidas em uma lista vinculada padrão que usa __puiHead
como o início da lista.
Quando você chama __FUnloadDelayLoadedDLL
, ele tenta localizar o nome fornecido na lista de DLLs carregadas. (Uma correspondência exata é necessária). Se encontrado, a cópia do IAT in pUnloadIAT
é copiada sobre a parte superior da IAT em execução para restaurar os ponteiros de thunk. Em seguida, a biblioteca é liberada usando FreeLibrary
, o registro UnloadInfo
correspondente é desvinculado da lista e excluído e TRUE
é retornado.
O argumento para a função __FUnloadDelayLoadedDLL2
diferencia maiúsculas de minúsculas. Por exemplo, você pode especificar:
__FUnloadDelayLoadedDLL2("user32.dll");
e não:
__FUnloadDelayLoadedDLL2("User32.DLL");
Para obter um exemplo de descarregamento de uma DLL carregada com atraso, consulte Descarregar uma DLL carregada com atraso explicitamente.
Desenvolver sua própria função auxiliar de carga com atraso
Talvez você queira fornecer sua própria versão da rotina auxiliar de carga com atraso. Em sua própria rotina, você pode fazer um processamento específico baseado nos nomes das DLLs ou importações. Existem duas maneiras de inserir seu próprio código: codifique sua própria função auxiliar, possivelmente com base no código fornecido. Ou, ligue o auxiliar fornecido para chamar sua própria função usando os ganchos de notificação.
Codifique seu próprio auxiliar
Criar sua própria rotina auxiliar é simples. Você pode usar o código existente como um guia para sua nova função. Sua função deve usar as mesmas convenções de chamada que o auxiliar existente. E, se ele retornar às conversões geradas pelo vinculador, ele deverá retornar um ponteiro de função adequado. Depois de criar seu código, você pode atender à chamada ou sair da chamada, como preferir.
Use o gancho de notificação de início de processamento
Provavelmente será mais fácil fornecer um novo ponteiro para uma função de gancho de notificação fornecida pelo usuário que tenha os mesmos valores que o auxiliar padrão para a notificação dliStartProcessing
. Nesse ponto, a função de gancho pode, essencialmente, se tornar a nova função auxiliar, porque um retorno bem-sucedido ao auxiliar padrão ignora todo o processamento adicional no auxiliar padrão.