Share via


Bir yürütülebilir dosyayı DLL’ye bağlama

Yürütülebilir dosya bir DLL'ye iki yoldan biriyle bağlanır (veya yükler):

  • Örtük bağlantı; burada işletim sistemi DLL'yi kullanan yürütülebilir dosyayla aynı anda yükler. İstemci yürütülebilir dosyası, DLL'nin dışarı aktarılan işlevlerini, işlevlerin statik olarak bağlanmış ve yürütülebilir dosyanın içinde yer alan gibi çağırır. Örtük bağlama bazen statik yük veya yük süresi dinamik bağlama olarak adlandırılır.

  • İşletim sisteminin DLL'yi çalışma zamanında isteğe bağlı olarak yüklediği açık bağlama. Açık bağlama yoluyla DLL kullanan yürütülebilir dosyanın DLL'yi açıkça yüklemesi ve kaldırması gerekir. Ayrıca DLL'den kullandığı her işleve erişmek için bir işlev işaretçisi ayarlaması gerekir. Statik bağlantılı bir kitaplıktaki veya örtük olarak bağlı DLL'deki işlevlere yapılan çağrılardan farklı olarak, istemci yürütülebilir dosyasının işlev işaretçileri aracılığıyla açıkça bağlı DLL'de dışarı aktarılan işlevleri çağırması gerekir. Açık bağlama bazen dinamik yük veya çalışma zamanı dinamik bağlama olarak adlandırılır.

Yürütülebilir dosya, aynı DLL'ye bağlanmak için bağlama yönteminden birini kullanabilir. Ayrıca, bu yöntemler birbirini dışlamaz; Yürütülebilir dosyalardan biri örtük olarak bir DLL'ye bağlanabilir ve başka bir yürütülebilir dosyaya açıkça bağlanabilir.

Hangi bağlama yönteminin kullanılacağını belirleme

Örtük bağlamanın mı yoksa açık bağlamanın mı kullanılacağı, uygulamanız için yapmanız gereken mimari bir karardır. Her yöntemin avantajları ve dezavantajları vardır.

Örtük Bağlama

Örtük bağlama, bir uygulamanın kodu dışarı aktarılan DLL işlevini çağırdığında gerçekleşir. Çağıran yürütülebilir dosyanın kaynak kodu derlendiğinde veya bir araya getirildiğinde, DLL işlev çağrısı nesne kodunda bir dış işlev başvurusu oluşturur. Bu dış başvuruyu çözmek için uygulamanın DLL'nin oluşturucusunun sağladığı içeri aktarma kitaplığı (.lib dosyası) ile bağlantısı olmalıdır.

İçeri aktarma kitaplığı yalnızca DLL'yi yüklemek ve DLL'deki işlevlere çağrılar uygulamak için kod içerir. İçeri aktarma kitaplığında dış işlev bulmak, bağlayıcıya bu işlevin kodunun DLL içinde olduğunu bildirir. DLL'lere yönelik dış başvuruları çözümlemek için bağlayıcı, işlem başladığında sisteme DLL kodunu nerede bulacağını bildiren bilgileri yürütülebilir dosyaya ekler.

Sistem dinamik olarak bağlı başvurular içeren bir program başlattığında, gerekli DLL'leri bulmak için programın yürütülebilir dosyasındaki bilgileri kullanır. DLL'yi bulamazsa sistem işlemi sonlandırır ve hatayı bildiren bir iletişim kutusu görüntüler. Aksi takdirde, sistem DLL modüllerini işlem adres alanına eşler.

DLL'lerden herhangi birinin başlatma ve sonlandırma kodu gibi DllMainbir giriş noktası işlevi varsa, işletim sistemi işlevi çağırır. Giriş noktası işlevine geçirilen parametrelerden biri, DLL'nin işleme eklendiğini gösteren bir kod belirtir. Giriş noktası işlevi TRUE döndürmezse sistem işlemi sonlandırır ve hatayı bildirir.

Son olarak, sistem DLL işlevleri için başlangıç adresleri sağlamak üzere işlemin yürütülebilir kodunu değiştirir.

Bir programın kodunun geri kalanında olduğu gibi yükleyici de DLL kodunu işlem başladığında işlemin adres alanına eşler. İşletim sistemi bunu yalnızca gerektiğinde belleğe yükler. Sonuç olarak, Windows'un PRELOAD önceki sürümlerinde yüklemeyi denetlemek için .def dosyaları tarafından kullanılan ve LOADONCALL kod özniteliklerinin artık anlamı yoktur.

Açık Bağlama

En kolay bağlama yöntemi olduğundan çoğu uygulama örtük bağlama kullanır. Ancak, açık bağlamanın gerekli olduğu zamanlar vardır. Açık bağlamayı kullanmanın bazı yaygın nedenleri şunlardır:

  • Uygulama, çalışma zamanına kadar yüklediği DLL'nin adını bilmez. Örneğin, uygulama başlangıçta bir yapılandırma dosyasından DLL'nin adını ve dışarı aktarılan işlevleri alabilir.

  • DLL işlem başlangıcında bulunamazsa, örtük bağlantı kullanan bir işlem işletim sistemi tarafından sonlandırılır. Açık bağlama kullanan bir işlem bu durumda sonlandırılmamıştır ve hatadan kurtarma girişiminde bulunabilir. Örneğin, işlem kullanıcıya hatayı bildirebilir ve kullanıcının DLL'nin başka bir yolunu belirtmesini sağlayabilir.

  • Bağlı olduğu DLL'lerden herhangi birinin başarısız olan bir işlevi varsa, örtük bağlama kullanan bir DllMain işlem de sonlandırılır. Açık bağlama kullanan bir işlem bu durumda sonlandırılmamıştır.

  • Windows, uygulama yüklendiğinde tüm DLL'leri yüklediğinden, birçok DLL'ye örtük olarak bağlanan bir uygulama yavaş başlatılabilir. Bir uygulama, başlangıç performansını geliştirmek için yalnızca yüklendikten hemen sonra gereken DLL'ler için örtük bağlama kullanabilir. Yalnızca gerekli olduğunda diğer DLL'leri yüklemek için açık bağlama kullanabilir.

  • Açık bağlama, içeri aktarma kitaplığı kullanarak uygulamayı bağlama gereksinimini ortadan kaldırır. DLL'deki değişiklikler dışarı aktarma sıralarının değişmesine neden olursa, uygulamaların bir sıra değeri değil işlevin adını kullanarak çağırmaları GetProcAddress durumunda yeniden bağlanmaları gerekmez. Örtük bağlama kullanan uygulamaların değiştirilen içeri aktarma kitaplığına yeniden bağlanması gerekir.

Aşağıda, farkında olmanız gereken iki açık bağlantı tehlikesi verilmiştir:

  • DLL'nin bir DllMain giriş noktası işlevi varsa, işletim sistemi işlevini adlı iş parçacığı bağlamında çağırır LoadLibrary. DLL işlevi, işleve karşılık gelen çağrısı olmayan önceki bir çağrı LoadLibrary nedeniyle işleme zaten eklenmişse giriş noktası işlevi çağrılmaz FreeLibrary . (veya AfxLoadLibrary) çağrıldığında LoadLibrary zaten var olan iş parçacıkları başlatılmadığından, DLL bir işlemin her iş parçacığını başlatmak için bir işlev kullanırsaDllMain, açık bağlama sorunlara neden olabilir.

  • DLL statik kapsam verilerini olarak __declspec(thread)bildirirse, açıkça bağlıysa bir koruma hatasına neden olabilir. DLL çağrısı tarafından LoadLibraryyüklendikten sonra, kod bu verilere her başvursa bir koruma hatasına neden olur. (Statik kapsam verileri hem genel hem de yerel statik öğeleri içerir.) Bu nedenle, dll oluşturduğunuzda iş parçacığı yerel depolama kullanmaktan kaçınmanız gerekir. Bunu yapamazsanız DLL kullanıcılarınızı DLL'nizi dinamik olarak yüklemenin olası tuzakları hakkında bilgilendirin. Daha fazla bilgi için bkz . Dinamik bağlantı kitaplığında (Windows SDK) iş parçacığı yerel depolamasını kullanma.

Örtük bağlamayı kullanma

Dll'yi örtük bağlantı yoluyla kullanmak için istemci yürütülebilir dosyalarının DLL sağlayıcısından bu dosyaları alması gerekir:

  • DLL'de dışarı aktarılan verilerin, işlevlerin ve C++ sınıflarının bildirimlerini içeren bir veya daha fazla üst bilgi dosyası (.h dosyaları). DLL tarafından dışarı aktarılan sınıfların, işlevlerin ve verilerin tümü üst bilgi dosyasında işaretlenmelidir __declspec(dllimport) . Daha fazla bilgi için bkz . dllexport, dllimport.

  • Yürütülebilir dosyanıza bağlanmak için bir içeri aktarma kitaplığı. Bağlayıcı, DLL oluşturulduğunda içeri aktarma kitaplığını oluşturur. Daha fazla bilgi için bkz . Bağlayıcı girişi olarak LIB dosyaları.

  • Gerçek DLL dosyası.

Dll'deki verileri, işlevleri ve sınıfları örtük bağlantıyla kullanmak için, herhangi bir istemci kaynak dosyasının bunları bildiren üst bilgi dosyalarını içermesi gerekir. Kodlama açısından bakıldığında, dışarı aktarılan işlevlere yapılan çağrılar diğer işlev çağrıları gibidir.

İstemci yürütülebilir dosyasını oluşturmak için DLL'nin içeri aktarma kitaplığıyla bağlantı kurmanız gerekir. Dış derleme dosyası veya derleme sistemi kullanıyorsanız, içeri aktarma kitaplığını bağladığınız diğer nesne dosyaları veya kitaplıklarla birlikte belirtin.

çağıran yürütülebilir dosyayı yüklediğinde işletim sisteminin DLL dosyasını bulabilmesi gerekir. Bu, uygulamanızı yüklerken DLL'nin varlığını dağıtmanız veya doğrulamanız gerektiği anlamına gelir.

Açık bağlama yoluyla DLL kullanmak için, uygulamaların çalışma zamanında DLL'yi açıkça yüklemek için bir işlev çağrısı yapması gerekir. Bir DLL'ye açıkça bağlanmak için bir uygulamanın şunları yapması gerekir:

  • DLL'yi yüklemek ve modül tanıtıcısını almak için LoadLibraryEx veya benzer bir işlevi çağırın.

  • Uygulamanın çağırdığını dışarı aktarılan her işlev için bir işlev işaretçisi almak için GetProcAddress'i çağırın. Uygulamalar DLL işlevlerini bir işaretçi aracılığıyla çağırdığından, derleyici dış başvurular oluşturmaz, bu nedenle içeri aktarma kitaplığıyla bağlantı oluşturmanız gerekmez. Ancak, çağırdığınız dışarı aktarılan işlevlerin çağrı imzasını tanımlayan bir typedef veya using deyiminiz olmalıdır.

  • DLL ile işiniz bittiğinde FreeLibrary'yi çağırın.

Örneğin, bu örnek işlev "MyDLL" adlı bir DLL'yi yüklemek için çağırır LoadLibrary , "DLLFunc1" adlı bir işleve işaretçi almak için çağırır GetProcAddress , işlevi çağırır ve sonucu kaydeder ve ardından DLL'yi kaldırmak için çağırır FreeLibrary .

#include "windows.h"

typedef HRESULT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT*);

HRESULT LoadAndCallSomeFunction(DWORD dwParam1, UINT * puParam2)
{
    HINSTANCE hDLL;               // Handle to DLL
    LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer
    HRESULT hrReturnVal;

    hDLL = LoadLibrary("MyDLL");
    if (NULL != hDLL)
    {
        lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL, "DLLFunc1");
        if (NULL != lpfnDllFunc1)
        {
            // call the function
            hrReturnVal = lpfnDllFunc1(dwParam1, puParam2);
        }
        else
        {
            // report the error
            hrReturnVal = ERROR_DELAY_LOAD_FAILED;
        }
        FreeLibrary(hDLL);
    }
    else
    {
        hrReturnVal = ERROR_DELAY_LOAD_FAILED;
    }
    return hrReturnVal;
}

Bu örnekten farklı olarak, çoğu durumda belirli bir DLL için uygulamanızda yalnızca bir kez aramanız LoadLibraryFreeLibrary gerekir. Özellikle DLL'de birden çok işlevi çağıracaksanız veya DLL işlevlerini tekrar tekrar çağıracaksanız bu durum geçerlidir.

Ne hakkında daha fazla bilgi edinmek istiyorsunuz?

Ayrıca bkz.

Visual Studio'da C/C++ DLL'leri oluşturma