Gecikme yükü yardımcı işlevini anlama

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 LoadLibrary

  • Yordamı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ır

  • Yardımcı işlevinde hemen önce GetProcAddress çağrılır

  • Yardımcı işlevinde çağrısı LoadLibrary başarısız olursa

  • Yardımcı işlevinde çağrısı GetProcAddress başarısız olursa

  • Yardı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 pidd doğru belirtilmezse oluşan geçersiz parametre. Bunu kurtarılamaz bir hata olarak değerlendirin.

  • LoadLibrary belirtilen 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.

Ayrıca bkz.

Gecikmeli yüklenen DLL'ler için bağlayıcı desteği