Aracılığıyla paylaş


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

MSVC bağlayıcısı DLL'lerin gecikmeli yüklenmesini destekler. Bu özellik, Windows SDK işlevlerini LoadLibrary kullanma ve GetProcAddress DLL'nin gecikmeli yüklenmesini uygulama gereksinimini giderir.

Gecikmeli yükleme olmadan, çalışma zamanında BIR DLL'yi yüklemenin tek yolu ve GetProcAddresskullanmaktırLoadLibrary; kullanan yürütülebilir dosya veya DLL yüklendiğinde işletim sistemi DLL'yi yükler.

Gecikmeli yüklemede, bir DLL'yi örtük olarak bağladığınızda bağlayıcı, program bu DLL'deki bir işlevi çağırana kadar DLL yüklemesini geciktirmeye yönelik seçenekler sağlar.

Bir uygulama, yardımcı işleviyle (Yüklemeyi içeri aktarmayı /DELAYLOAD geciktir) bağlayıcı seçeneğini kullanarak DLL'nin yüklenmesini geciktirebilir. (Varsayılan bir yardımcı işlev uygulaması Microsoft tarafından sağlanır.) Yardımcı işlevi, sizin için ve GetProcAddress çağrısı LoadLibrary yaparak DLL'yi çalışma zamanında isteğe bağlı olarak yükler.

Aşağıdakiler durumunda DLL'nin yüklenmesini geciktirebilirsiniz:

  • Programınız DLL'de bir işlev çağırmayabilir.

  • DLL'deki bir işlev, programınızın yürütülmesinde geç olana kadar çağrılmayabilir.

DLL'nin gecikmeli yüklenmesi, EXE veya DLL projesinin derlemesi sırasında belirtilebilir. Bir veya daha fazla DLL'nin yüklenmesini geciktiren bir DLL projesi, içinde DllMaingecikmeli yüklenen bir giriş noktasını çağırmamalıdır.

Yüklemeyi geciktirmek için DLL'leri belirtme

Bağlayıcı seçeneğini kullanarak yüklemeyi geciktirecek DLL'leri /delayload:dllname belirtebilirsiniz. Yardımcı işlevin kendi sürümünü kullanmayı planlamıyorsanız, programınızı (masaüstü uygulamaları için) veya dloadhelper.lib (UWP uygulamaları için) ile delayimp.lib de bağlamanız gerekir.

Dll yüklemesinde gecikmeye örnek olarak şunlar gösterelim:

// cl t.cpp user32.lib delayimp.lib  /link /DELAYLOAD:user32.dll
#include <windows.h>
// uncomment these lines to remove .libs from command line
// #pragma comment(lib, "delayimp")
// #pragma comment(lib, "user32")

int main() {
   // user32.dll will load at this point
   MessageBox(NULL, "Hello", "Hello", MB_OK);
}

Projenin DEBUG sürümünü oluşturun. Hata ayıklayıcısını kullanarak kodda adım adım ilerlediğinizde, bunun user32.dll yalnızca çağrısı MessageBoxyaptığınızda yüklendiğini fark edersiniz.

Gecikmeli yüklenen DLL'leri açıkça kaldırma

/delay:unload Bağlayıcı seçeneği, kodunuzun gecikmeli yüklenen bir DLL'yi açıkça kaldırmasını sağlar. Varsayılan olarak, gecikmeli yüklenen içeri aktarmalar içeri aktarma adresi tablosunda (IAT) kalır. Ancak bağlayıcı komut satırında kullanırsanız /delay:unload , yardımcı işlevi DLL'nin bir __FUnloadDelayLoadedDLL2 çağrıyla açıkça kaldırılmasını destekler ve IAT'yi özgün biçimine sıfırlar. Artık geçersiz olan işaretçilerin üzerine yazılır. IAT, yapıda ImgDelayDescr varsa özgün IAT'nin bir kopyasının adresini içeren bir alandır.

Gecikmeli yüklenen DLL'yi kaldırma örneği

Bu örnekte, MyDll.dllişlevini fnMyDlliçeren bir DLL'nin nasıl açıkça kaldırıldığı gösterilmektedir:

// link with /link /DELAYLOAD:MyDLL.dll /DELAY:UNLOAD
#include <windows.h>
#include <delayimp.h>
#include "MyDll.h"
#include <stdio.h>

#pragma comment(lib, "delayimp")
#pragma comment(lib, "MyDll")
int main()
{
    BOOL TestReturn;
    // MyDLL.DLL will load at this point
    fnMyDll();

    //MyDLL.dll will unload at this point
    TestReturn = __FUnloadDelayLoadedDLL2("MyDll.dll");

    if (TestReturn)
        printf_s("\nDLL was unloaded");
    else
        printf_s("\nDLL was not unloaded");
}

Gecikmeli yüklenen DLL'yi kaldırmayla ilgili önemli notlar:

  • işlevinin __FUnloadDelayLoadedDLL2delayhlp.cppuygulamasını dosyasında, MSVC include dizininde bulabilirsiniz. Daha fazla bilgi için bkz . Gecikme yükü yardımcı işlevini anlama.

  • name İşlevin parametresi, içeri aktarma kitaplığının __FUnloadDelayLoadedDLL2 içerdiğiyle tam olarak eşleşmelidir (büyük/küçük harf dahil). (Bu dize, görüntüdeki içeri aktarma tablosunda da bulunur.) kullanarak DUMPBIN /DEPENDENTSiçeri aktarma kitaplığının içeriğini görüntüleyebilirsiniz. Büyük/küçük harfe duyarlı olmayan bir dize eşleşmesini tercih ediyorsanız, büyük/küçük harfe duyarlı olmayan CRT dize işlevlerinden birini veya bir Windows API çağrısını kullanacak şekilde güncelleştirebilirsiniz __FUnloadDelayLoadedDLL2 .

Gecikmeli yüklenen içeri aktarmaları bağlama

Varsayılan bağlayıcı davranışı, gecikmeli yüklenen DLL için bağlanabilir bir içeri aktarma adresi tablosu (IAT) oluşturmaktır. DLL bağlıysa, yardımcı işlevi başvuruda bulunan içeri aktarmaların her birinde çağırmak GetProcAddress yerine ilişkili bilgileri kullanmayı dener. Zaman damgası veya tercih edilen adres yüklenen DLL'deki adresle eşleşmiyorsa, yardımcı işlevi ilişkili içeri aktarma adresi tablosunun güncel olmadığını varsayar. IAT yok gibi devam eder.

Bir DLL'nin gecikmeli olarak yüklenen içeri aktarmalarını hiçbir zaman bağlamayı düşünmüyorsanız bağlayıcı komut satırında belirtin /delay:nobind . Bağlayıcı, görüntü dosyasında yer tasarrufu sağlayan ilişkili içeri aktarma adresi tablosunu oluşturmaz.

Gecikmeli yüklenen DLL için tüm içeri aktarmaları yükleme

__HrLoadAllImportsForDll içinde delayhlp.cpptanımlanan işlev, bağlayıcıya bağlayıcı seçeneğiyle belirtilen bir DLL'den tüm içeri aktarmaları yüklemesini /delayload söyler.

Tüm içeri aktarma işlemlerini aynı anda yüklediğinizde, hata işlemeyi tek bir yerde merkezileştirebilirsiniz. İçeri aktarmalara yapılan tüm gerçek çağrıların etrafında yapılandırılmış özel durum işlemekten kaçınabilirsiniz. Ayrıca, uygulamanızın bir süreç boyunca kısmen başarısız olması durumundan da kaçınılır: Örneğin, yardımcı kod içeri aktarmayı yükleyemezse, diğerlerini başarıyla yükledikten sonra.

Çağrı __HrLoadAllImportsForDll , kancaların ve hata işlemenin davranışını değiştirmez. Daha fazla bilgi için bkz . Hata işleme ve bildirim.

__HrLoadAllImportsForDll DLL'nin içinde depolanan adla büyük/küçük harfe duyarlı bir karşılaştırma yapar.

Adlandırılmış DLL'yi yüklemeye çalışmak için adlı TryDelayLoadAllImports bir işlevde kullanan __HrLoadAllImportsForDll bir örnek aşağıda verilmiştır. Özel durum davranışını belirlemek için işlevini CheckDelayExceptionkullanır.

int CheckDelayException(int exception_value)
{
    if (exception_value == VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND) ||
        exception_value == VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND))
    {
        // This example just executes the handler.
        return EXCEPTION_EXECUTE_HANDLER;
    }
    // Don't attempt to handle other errors
    return EXCEPTION_CONTINUE_SEARCH;
}

bool TryDelayLoadAllImports(LPCSTR szDll)
{
    __try
    {
        HRESULT hr = __HrLoadAllImportsForDll(szDll);
        if (FAILED(hr))
        {
            // printf_s("Failed to delay load functions from %s\n", szDll);
            return false;
        }
    }
    __except (CheckDelayException(GetExceptionCode()))
    {
        // printf_s("Delay load exception for %s\n", szDll);
        return false;
    }
    // printf_s("Delay load completed for %s\n", szDll);
    return true;
}

İçeri aktarma işlevlerini çağırıp çağırmayabileceğinizi denetlemek için sonucunu TryDelayLoadAllImports kullanabilirsiniz.

Hata işleme ve bildirme

Programınız gecikmeli yüklenen DLL'ler kullanıyorsa hataları sağlam bir şekilde işlemesi gerekir. Program çalışırken oluşan hatalar işlenmeyen özel durumlara neden olur. DLL gecikme yükü hata işleme ve bildirimi hakkında daha fazla bilgi için bkz . Hata işleme ve bildirim.

Gecikmeli yüklemeli içeri aktarmaların dökümünü alma

Gecikmeli yüklenen içeri aktarmalar kullanılarak DUMPBIN /IMPORTSbırakılabilir. Bu içeri aktarmalar, standart içeri aktarmalardan biraz farklı bilgilerle gösterilir. Bunlar listenin kendi bölümlerine /imports ayrılır ve açıkça gecikmeli içeri aktarmalar olarak etiketlenir. Görüntüde boş bilgi varsa, bu not edilir. Bağlama bilgileri varsa, hedef DLL'nin saat ve tarih damgası, içeri aktarmaların ilişkili adresleriyle birlikte not edilir.

Gecikmeli yük DLL'leri üzerindeki kısıtlamalar

DLL içeri aktarmalarının gecikmeli yüklenmesiyle ilgili çeşitli kısıtlamalar vardır.

  • Verilerin içeri aktarılması desteklenemez. Geçici çözüm, verileri içeri aktarma işlemini açıkça kullanarak LoadLibrary (veya gecikme yükü yardımcısının DLL'yi yüklediğini biliyorsanız) ve GetProcAddresskullanarak GetModuleHandle işlemektir.

  • Yükleme Kernel32.dll gecikmesi desteklenmez. Gecikme yükü yardımcı yordamlarının çalışması için bu DLL yüklenmelidir.

  • İletilen giriş noktalarının bağlanması desteklenmez.

  • Bir DLL başlatmaya yüklenmek yerine gecikmeli yüklenirse işlem farklı davranışlara sahip olabilir. Gecikmeli yüklenen DLL'nin giriş noktasında işlem başına başlatmalar olup olmadığı görülebilir. Diğer durumlar arasında kullanılarak bildirilen __declspec(thread)statik TLS (iş parçacığı yerel depolama alanı), DLL aracılığıyla LoadLibraryyüklendiğinde işlenmez. , TlsFreeTlsGetValue, ve TlsSetValuekullanan TlsAllocdinamik TLS, statik veya gecikmeli DLL'lerde kullanılmaya devam eder.

  • Her işlevin ilk çağrısından sonra içeri aktarılan işlevlere yönelik statik genel işlev işaretçilerini yeniden başlatma. Bunun nedeni, bir işlev işaretçisinin ilk kullanımı yüklenen işlevi değil, thunk'a işaret ettiğinden gereklidir.

  • Şu anda normal içeri aktarma mekanizmasını kullanırken DLL'den yalnızca belirli yordamların yüklenmesini geciktirmek için bir yol yoktur.

  • Özel çağrı kuralları (x86 mimarilerinde koşul kodlarını kullanma gibi) desteklenmez. Ayrıca kayan nokta yazmaçları herhangi bir platforma kaydedilmez. Özel yardımcı yordamınızın veya kanca yordamlarınızın kayan nokta türleri kullanıp kullanmadığını dikkate alın: Yordamların kayan nokta parametreleriyle yazmaç çağrı kurallarını kullanan makinelerde tam kayan nokta durumunu kaydetmesi ve geri yüklemesi gerekir. Özellikle yardım işlevindeki sayısal bir veri işlemcisi (NDP) yığınında kayan nokta parametrelerini alan CRT işlevlerini çağırırsanız, CRT DLL'sinin gecikmeli yüklenmesi konusunda dikkatli olun.

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. Daha fazla bilgi için bkz. Gecikme yükü yardımcı işlevini anlama ve Kendi yardımcı işlevinizi geliştirme.

Ayrıca bkz.

Visual Studio'da C/C++ DLL'leri oluşturma
MSVC bağlayıcı başvurusu