Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bağlayıcı tarafından desteklenen gecikmeli yükleme için yardımcı işlevi, DLL'yi çalışma zamanında yükler. Davranışını özelleştirmek için yardımcı işlevini değiştirebilirsiniz. içinde delayimp.libsağlanan yardımcı işlevini kullanmak yerine kendi işlevinizi yazın ve programınıza bağlayın. Bir yardımcı işlevi tüm gecikmeli yüklenen DLL'lere hizmet eder.
DLL veya içeri aktarma adlarına göre belirli bir işlem yapmak istiyorsanız yardımcı işlevin kendi sürümünü sağlayabilirsiniz.
Yardımcı işlevi şu eylemleri gerçekleştirir:
Depolanmış tanıtıcıyı kitaplığın zaten yüklenip yüklenmediğini görmek için denetler
DLL'yi yüklemeye çalışmak için çağrılar
LoadLibraryYordamın adresini almayı denemeye yönelik çağrılar
GetProcAddressŞimdi yüklenen giriş noktasını çağırmak için gecikmeli içeri aktarma yüküne döner
Yardımcı işlevi, aşağıdaki eylemlerin her biri sonrasında programınızdaki bir bildirim kancasına geri çağrı yapabilir:
Yardımcı işlevi başlatıldığında
Yardımcı işlevinde hemen önce
LoadLibraryçağrılırYardımcı işlevinde hemen önce
GetProcAddressçağrılırYardımcı işlevinde çağrısı
LoadLibrarybaşarısız olursaYardımcı işlevinde çağrısı
GetProcAddressbaşarısız olursaYardımcı işlevi işleme tamamlandıktan sonra
Bu kanca noktalarının her biri, gecikmeli içeri aktarma yüküne dönüş dışında yardımcı yordamın normal işlemesini bir şekilde değiştiren bir değer döndürebilir.
Varsayılan yardımcı kod, MSVC include dizininde delayhlp.cpp ve delayimp.h içinde bulunabilir. Hedef mimariniz için MSVC lib dizininde derlenmiştirdelayimp.lib. Kendi yardımcı işlevinizi yazmadığınız sürece bu kitaplığı derlemelerinize eklemeniz gerekir.
Yük yardımcı çağırma kurallarını, parametrelerini ve dönüş türünü geciktirme
Gecikme yükü yardımcı yordamının prototipi:
FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd,
FARPROC * ppfnIATEntry
);
Parametreler
pidd
İçeri const aktarmayla ilgili çeşitli verilerin uzaklıklarını, bağlama bilgileri için bir zaman damgasını ve tanımlayıcı içeriği hakkında daha fazla bilgi sağlayan öznitelik kümesini içeren bir işaretçi ImgDelayDescr . Şu anda tanımlayıcıdaki dlattrRvaadreslerin göreli sanal adresler olduğunu gösteren tek bir özniteliği vardır. Daha fazla bilgi için içindeki bildirimlere delayimp.hbakın.
Gecikme tanımlayıcısında (ImgDelayDescr içinde delayimp.h) işaretçiler, hem 32 bit hem de 64 bit programlarda beklendiği gibi çalışmak için göreli sanal adresleri (RVA' lar) kullanır. Bunları kullanmak için, içinde delayhlp.cppbulunan işlevini PFromRvakullanarak bu RVA'ları işaretçilere geri dönüştürün. Tanımlayıcıdaki alanların her birinde bu işlevi kullanarak bunları 32 bit veya 64 bit işaretçilere dönüştürebilirsiniz. Varsayılan gecikme yükü yardımcı işlevi, örnek olarak kullanmak için iyi bir şablondur.
Yapının tanımı PCImgDelayDescr için bkz . Yapı ve sabit tanımlar.
ppfnIATEntry
Gecikme yükü içeri aktarma adresi tablosundaki (IAT) yuvaya yönelik bir işaretçi. İçeri aktarılan işlevin adresiyle güncelleştirilen yuvadır. Yardımcı yordamının bu konuma döndürdüğü değeri depolaması gerekir.
Beklenen dönüş değerleri
Yardımcı işlev başarılı olursa, içeri aktarılan işlevin adresini döndürür.
İşlev başarısız olursa, yapılandırılmış bir özel durum oluşturur ve 0 döndürür. Üç tür özel durum oluşturulabilir:
içindeki öznitelikler
pidddoğru belirtilmezse oluşan geçersiz parametre. Bunu kurtarılamaz bir hata olarak değerlendirin.LoadLibrarybelirtilen DLL üzerinde başarısız oldu.hatası.
GetProcAddress
Bu özel durumları işlemek sizin sorumluluğunuzdadır. Daha fazla bilgi için bkz . Hata işleme ve bildirim.
Açıklamalar
Yardımcı işlevi için çağırma kuralı: __stdcall. Dönüş değerinin türü ilgili FARPROC olmadığından kullanılır. Bu işlevin C bağlantısı vardır, yani C++ kodunda bildirildiğinde tarafından extern "C" sarmalanması gerekir. Makro ExternC bu sarmalayıcıyı sizin için halleder.
Yardımcı yordamınızı bildirim kancası olarak kullanmak için kodunuzun döndürülecek uygun işlev işaretçisini belirtmesi gerekir. Bağlayıcının oluşturduğu thunk kodu, içeri aktarmanın gerçek hedefi olarak bu dönüş değerini alır ve doğrudan buna atlar. Yardımcı yordamınızı bildirim kancası olarak kullanmak istemiyorsanız, yardımcı işlevin ppfnIATEntrydönüş değerini geçirilen işlev işaretçisi konumunda depolayın.
Örnek kanca işlevi
Aşağıdaki kodda temel bir kanca işlevinin nasıl uygulandığı gösterilmektedir.
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;
*/
Yük yapısını ve sabit tanımları geciktirme
Varsayılan gecikme yükü yardımcı yordamı, kanca işlevleriyle ve özel durumlar sırasında iletişim kurmak için birkaç yapı kullanır. Bu yapılar içinde delayimp.htanımlanır. Burada makrolar, tür tanımları, bildirim ve hata değerleri, bilgi yapıları ve kancalara geçirilen işaretçiden kancaya işlev türü yer alır:
#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
);
Yükleme gecikmesi için gerekli değerleri hesaplama
Gecikme yükü yardımcı yordamının iki kritik bilgi parçasını hesaplaması gerekir. Yardımcı olmak için, içinde bu bilgileri hesaplamak için iki satır içi işlev delayhlp.cpp vardır.
birincisi,
IndexFromPImgThunkDatageçerli içeri aktarmanın dizinini üç farklı tabloya (içeri aktarma adresi tablosu (IAT), ilişkili içeri aktarma adresi tablosu (BIAT) ve ilişkisiz içeri aktarma adres tablosuna (UIAT) hesaplar.İkincisi,
CountOfImportsgeçerli bir IAT'deki içeri aktarma sayısını sayar.
// 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;
}
Gecikmeli yüklenen DLL'nin kaldırılmasını destekleme
Gecikmeli yüklenen DLL yüklendiğinde, varsayılan gecikme yükü yardımcı programı, gecikme yükü tanımlayıcılarının alanda bir işaretçisi ve özgün içeri aktarma adresi tablosunun (IAT) pUnloadIAT bir kopyası olup olmadığını denetler. Bu durumda, yardımcı listedeki bir işaretçiyi içeri aktarma gecikmesi tanımlayıcısına kaydeder. Bu girdi, yardımcı işlevin DLL'nin açıkça kaldırılmasını desteklemek için DLL'yi ada göre bulmasını sağlar.
Gecikmeli yüklenen DLL'yi açıkça kaldırmaya yönelik ilişkili yapılar ve işlevler şunlardır:
//
// 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;
YapıUnloadInfo, ve uygulamalarını sırasıyla ve LocalFree operator deleteolarak operator new kullanan LocalAlloc bir C++ sınıfı kullanılarak uygulanır. Bu seçenekler, listenin başı olarak kullanılan __puiHead standart bağlantılı bir listede tutulur.
çağırdığınızda __FUnloadDelayLoadedDLL, yüklenen DLL'ler listesinde sağladığınız adı bulmaya çalışır. (Tam eşleşme gereklidir.) Bulunursa, içindeki IAT pUnloadIAT kopyası, thunk işaretçilerini geri yüklemek için çalışan IAT'nin üst kısmına kopyalanır. Ardından, kitaplığı kullanılarak FreeLibraryserbest bırakılır, eşleşen UnloadInfo kayıt listeden çıkarılır ve silinir ve TRUE döndürülür.
İşlevin __FUnloadDelayLoadedDLL2 bağımsız değişkeni büyük/küçük harfe duyarlıdır. Örneğin şunları belirtebilirsiniz:
__FUnloadDelayLoadedDLL2("user32.dll");
ve değil:
__FUnloadDelayLoadedDLL2("User32.DLL");
Gecikmeli yüklenen DLL'yi kaldırma örneği için bkz . Gecikmeli yüklenen DLL'yi açıkça kaldırma.
Kendi gecikme yükü yardımcı işlevinizi geliştirme
Gecikme yükü yardımcı yordamının kendi sürümünü sağlamak isteyebilirsiniz. Kendi yordamınızda, DLL veya içeri aktarmaların adlarına göre belirli işlemler yapabilirsiniz. Kendi kodunuzu eklemenin iki yolu vardır: Sağlanan kodu temel alarak kendi yardımcı işlevinizi kodlayın. Alternatif olarak, bildirim kancalarını kullanarak sağlanan yardımcıyı kendi işlevinizi çağırmak için bağlayabilirsiniz.
Kendi yardımcınızı kodlayın
Kendi yardımcı rutininizi oluşturmak basittir. Mevcut kodu yeni işleviniz için kılavuz olarak kullanabilirsiniz. İşleviniz, mevcut yardımcıyla aynı çağrı kurallarını kullanmalıdır. Bağlayıcı tarafından oluşturulan thunk'lara geri dönerse, uygun bir işlev işaretçisi döndürmelidir. Kodunuzu oluşturduktan sonra, aramayı karşılar veya istediğiniz gibi aramadan çıkarsınız.
Bildirim kancasını işlemeye başlamayı kullanma
Bildirim için varsayılan yardımcı ile aynı değerleri alan, kullanıcı tarafından sağlanan bildirim kancası işlevine yeni bir işaretçi sağlamak muhtemelen en kolay seçenektir dliStartProcessing . Bu noktada, varsayılan yardımcıya başarılı bir dönüş, varsayılan yardımcıdaki diğer tüm işlemleri atladığı için kanca işlevi temelde yeni yardımcı işlevi haline gelebilir.