Memahami fungsi pembantu beban penundaan
Fungsi pembantu untuk pemuatan tertunda yang didukung linker adalah apa yang sebenarnya memuat DLL saat runtime. Anda dapat mengubah fungsi pembantu untuk menyesuaikan perilakunya. Alih-alih menggunakan fungsi pembantu yang disediakan di delayimp.lib
, tulis fungsi Anda sendiri dan tautkan ke program Anda. Satu fungsi pembantu melayani semua DLL yang dimuat keterlambatan.
Anda dapat menyediakan versi fungsi pembantu Anda sendiri jika Anda ingin melakukan pemrosesan tertentu berdasarkan nama DLL atau impor.
Fungsi pembantu mengambil tindakan ini:
Memeriksa handel tersimpan ke pustaka untuk melihat apakah handel tersebut telah dimuat
LoadLibrary
Panggilan untuk mencoba memuat DLLGetProcAddress
Panggilan untuk mencoba mendapatkan alamat prosedurKembali ke thunk muatan impor penundaan untuk memanggil titik masuk yang sekarang dimuat
Fungsi pembantu dapat memanggil kembali ke hook pemberitahuan di program Anda setelah setiap tindakan berikut:
Ketika fungsi pembantu dimulai
Tepat sebelum
LoadLibrary
dipanggil dalam fungsi pembantuTepat sebelum
GetProcAddress
dipanggil dalam fungsi pembantuJika panggilan ke
LoadLibrary
dalam fungsi pembantu gagalJika panggilan ke
GetProcAddress
dalam fungsi pembantu gagalSetelah fungsi pembantu selesai diproses
Masing-masing titik kait ini dapat mengembalikan nilai yang mengubah pemrosesan normal rutinitas pembantu dengan cara tertentu, kecuali kembali ke thunk beban impor penundaan.
Kode pembantu default dapat ditemukan di delayhlp.cpp
dan delayimp.h
di direktori MSVC include
. Ini dikompilasi ke delayimp.lib
dalam direktori MSVC lib
untuk arsitektur target Anda. Anda harus menyertakan pustaka ini dalam kompilasi Anda kecuali Anda menulis fungsi pembantu Anda sendiri.
Tunda konvensi panggilan pembantu beban, parameter, dan jenis pengembalian
Prototipe untuk rutinitas pembantu beban penundaan adalah:
FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd,
FARPROC * ppfnIATEntry
);
Parameter
pidd
const
Penunjuk ke ImgDelayDescr
yang berisi offset berbagai data terkait impor, tanda waktu untuk informasi pengikatan, dan sekumpulan atribut yang memberikan informasi lebih lanjut tentang konten deskriptor. Saat ini hanya ada satu atribut, dlattrRva
, yang menunjukkan bahwa alamat dalam deskriptor adalah alamat virtual relatif. Untuk informasi selengkapnya, lihat deklarasi di delayimp.h
.
Penunjuk dalam deskriptor penundaan (ImgDelayDescr
dalam delayimp.h
) menggunakan alamat virtual relatif (RVA) untuk bekerja seperti yang diharapkan dalam program 32-bit dan 64-bit. Untuk menggunakannya, konversikan RVA ini kembali ke penunjuk dengan menggunakan fungsi PFromRva
, ditemukan di delayhlp.cpp
. Anda dapat menggunakan fungsi ini pada setiap bidang dalam deskriptor untuk mengonversinya kembali ke pointer 32-bit atau 64-bit. Fungsi pembantu beban penundaan default adalah templat yang baik untuk digunakan sebagai contoh.
Untuk definisi PCImgDelayDescr
struktur, lihat Struktur dan definisi konstanta.
ppfnIATEntry
Penunjuk ke slot dalam tabel alamat impor beban penundaan (IAT). Ini adalah slot yang diperbarui dengan alamat fungsi yang diimpor. Rutinitas pembantu perlu menyimpan nilai yang sama dengan yang dikembalikan ke lokasi ini.
Nilai pengembalian yang diharapkan
Jika fungsi pembantu berhasil, fungsi tersebut mengembalikan alamat fungsi yang diimpor.
Jika fungsi gagal, fungsi akan memunculkan pengecualian terstruktur dan mengembalikan 0. Tiga jenis pengecualian dapat dinaikkan:
Parameter tidak valid, yang terjadi jika atribut di
pidd
tidak ditentukan dengan benar. Perlakukan ini sebagai kesalahan yang tidak dapat dipulihkan.LoadLibrary
gagal pada DLL yang ditentukan.GetProcAddress
Kegagalan .
Anda bertanggung jawab untuk menangani pengecualian ini. Untuk informasi selengkapnya, lihat Penanganan kesalahan dan pemberitahuan.
Keterangan
Konvensi panggilan untuk fungsi pembantu adalah __stdcall
. Jenis nilai yang dikembalikan tidak relevan, jadi FARPROC
digunakan. Fungsi ini memiliki tautan C, yang berarti perlu dibungkus oleh extern "C"
ketika dideklarasikan dalam kode C++. ExternC
Makro mengurus pembungkus ini untuk Anda.
Untuk menggunakan rutinitas pembantu Anda sebagai kait pemberitahuan, kode Anda harus menentukan penunjuk fungsi yang sesuai untuk dikembalikan. Kode thunk yang dihasilkan linker kemudian mengambil nilai pengembalian tersebut sebagai target nyata dari impor dan melompat langsung ke dalamnya. Jika Anda tidak ingin menggunakan rutinitas pembantu Anda sebagai kait pemberitahuan, simpan nilai pengembalian fungsi pembantu di ppfnIATEntry
, lokasi penunjuk fungsi yang diteruskan.
Contoh fungsi kait
Kode berikut menunjukkan cara mengimplementasikan fungsi kait dasar.
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;
*/
Menunda struktur beban dan definisi konstanta
Rutinitas pembantu beban penundaan default menggunakan beberapa struktur untuk berkomunikasi dengan fungsi kait dan selama pengecualian apa pun. Struktur ini didefinisikan dalam delayimp.h
. Berikut adalah makro, typedefs, nilai pemberitahuan dan kegagalan, struktur informasi, dan jenis pointer-to-hook-function yang diteruskan ke 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
);
Menghitung nilai yang diperlukan untuk pemuatan penundaan
Rutinitas pembantu beban penundaan perlu menghitung dua informasi penting. Untuk membantu, ada dua fungsi sebaris untuk delayhlp.cpp
menghitung informasi ini.
Yang pertama,
IndexFromPImgThunkData
, menghitung indeks impor saat ini ke dalam tiga tabel berbeda (tabel alamat impor (IAT), tabel alamat impor terikat (BIAT), dan tabel alamat impor tidak terikat (UIAT)).Yang kedua,
CountOfImports
, menghitung jumlah impor dalam IAT yang valid.
// 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;
}
Mendukung pembongkaran DLL yang dimuat keterlambatan
Ketika DLL yang dimuat keterlambatan dimuat, pembantu beban penundaan default memeriksa untuk melihat apakah deskriptor beban penundaan memiliki penunjuk dan salinan tabel alamat impor asli (IAT) di pUnloadIAT
bidang . Jika demikian, pembantu menyimpan penunjuk dalam daftar ke deskriptor penundaan impor. Entri ini memungkinkan fungsi pembantu menemukan DLL berdasarkan nama, untuk mendukung pembongkaran DLL tersebut secara eksplisit.
Berikut adalah struktur dan fungsi terkait untuk secara eksplisit membongkar DLL yang dimuat keterlambatan:
//
// 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;
Struktur diimplementasikan UnloadInfo
menggunakan kelas C++ yang menggunakan LocalAlloc
dan LocalFree
implementasi sebagai operator new
dan operator delete
, masing-masing. Opsi ini disimpan dalam daftar tertaut standar yang menggunakan __puiHead
sebagai kepala daftar.
Saat Anda memanggil __FUnloadDelayLoadedDLL
, ini mencoba menemukan nama yang Anda berikan dalam daftar DLL yang dimuat. (Diperlukan kecocokan yang tepat.) Jika ditemukan, salinan IAT di disalin di pUnloadIAT
atas IAT yang sedang berjalan untuk memulihkan pointer thunk. Kemudian, pustaka dikosongkan dengan menggunakan FreeLibrary
, rekaman yang UnloadInfo
cocok dilepas tautannya dari daftar dan dihapus, dan TRUE
dikembalikan.
Argumen untuk fungsi __FUnloadDelayLoadedDLL2
peka huruf besar/kecil. Misalnya, Anda akan menentukan:
__FUnloadDelayLoadedDLL2("user32.dll");
dan bukan:
__FUnloadDelayLoadedDLL2("User32.DLL");
Untuk contoh membongkar DLL yang dimuat keterlambatan, lihat Secara eksplisit membongkar DLL yang dimuat keterlambatan.
Mengembangkan fungsi pembantu beban penundaan Anda sendiri
Anda mungkin ingin memberikan versi rutinitas pembantu beban penundaan Anda sendiri. Dalam rutinitas Anda sendiri, Anda dapat melakukan pemrosesan tertentu berdasarkan nama DLL atau impor. Ada dua cara untuk memasukkan kode Anda sendiri: Kode fungsi pembantu Anda sendiri, mungkin berdasarkan kode yang disediakan. Atau, kaitkan pembantu yang disediakan untuk memanggil fungsi Anda sendiri dengan menggunakan kait pemberitahuan.
Kode pembantu Anda sendiri
Membuat rutinitas pembantu Anda sendiri sangat mudah. Anda dapat menggunakan kode yang ada sebagai panduan untuk fungsi baru Anda. Fungsi Anda harus menggunakan konvensi panggilan yang sama dengan pembantu yang ada. Dan, jika kembali ke thunk yang dihasilkan linker, itu harus mengembalikan penunjuk fungsi yang tepat. Setelah membuat kode, Anda dapat memenuhi panggilan atau keluar dari panggilan, sesuka Anda.
Gunakan kait pemberitahuan mulai pemrosesan
Mungkin paling mudah untuk memberikan penunjuk baru ke fungsi kait pemberitahuan yang disediakan pengguna yang mengambil nilai yang sama dengan pembantu default untuk dliStartProcessing
pemberitahuan. Pada saat itu, fungsi kait pada dasarnya dapat menjadi fungsi pembantu baru, karena pengembalian yang berhasil ke pembantu default melewati semua pemrosesan lebih lanjut di pembantu default.
Baca juga
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk