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 GetProcAddress
kullanmaktı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 DllMain
gecikmeli 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ı MessageBox
yaptığı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.dll
işlevini fnMyDll
iç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
__FUnloadDelayLoadedDLL2
delayhlp.cpp
uygulamasını dosyasında, MSVCinclude
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.) kullanarakDUMPBIN /DEPENDENTS
iç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.cpp
tanı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 CheckDelayException
kullanı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 /IMPORTS
bı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) veGetProcAddress
kullanarakGetModuleHandle
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ığıylaLoadLibrary
yüklendiğinde işlenmez. ,TlsFree
TlsGetValue
, veTlsSetValue
kullananTlsAlloc
dinamik 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.lib
sağ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