Aracılığıyla paylaş


TN033: MFC'nin DLL Sürümü

Bu not, MFC uygulamaları ve MFC uzantı DLL'leri ile ve MFCxxD.DLL (burada xx MFC sürüm numarasıdır) paylaşılan dinamik bağlantı kitaplıklarını nasıl kullanabileceğinizi MFCxx.DLL açıklar. Normal MFC DLL'leri hakkında daha fazla bilgi için bkz . MFC'yi DLL'nin Parçası Olarak Kullanma.

Bu teknik not, DLL'lerin üç yönünü kapsar. Son ikisi daha ileri düzey kullanıcılar içindir:

MFC olmayan uygulamalarla (normal MFC DLL olarak bilinir) kullanılabilecek MFC kullanarak bir DLL oluşturmak istiyorsanız Teknik Not 11'e bakın.

MFCxx.DLL Desteğine Genel Bakış: Terminoloji ve Dosyalar

Normal MFC DLL:Bazı MFC sınıflarını kullanarak tek başına DLL oluşturmak için normal bir MFC DLL kullanırsınız. Uygulama/DLL sınırındaki arabirimler "C" arabirimleridir ve istemci uygulamasının bir MFC uygulaması olması gerekmez.

Normal MFC DLL'leri, MFC 1.0'da desteklenen DLL'lerin sürümüdür. Bunlar Teknik Not 11 ve MFC Gelişmiş Kavramlar örneğinde DLLScreenCapaçıklanmıştır.

Dekont

Visual C++ sürüm 4.0 itibarıyla USRDLL terimi eskidir ve MFC'ye statik olarak bağlanan normal bir MFC DLL'si ile değiştirilmiştir. MFC'ye dinamik olarak bağlanan normal bir MFC DLL de oluşturabilirsiniz.

MFC 3.0 (ve üzeri), OLE ve Veritabanı sınıfları da dahil olmak üzere tüm yeni işlevlerle normal MFC DLL'lerini destekler.

AFXDLL: MFC kitaplıklarının paylaşılan sürümü olarak da adlandırılır. MFC 2.0'a eklenen yeni DLL desteğidir. MFC kitaplığının kendisi bir dizi DLL içindedir (aşağıda açıklanmıştır). İstemci uygulaması veya DLL, gerektirdiği DLL'leri dinamik olarak bağlar. Uygulama/DLL sınırındaki arabirimler C++/MFC sınıf arabirimleridir. İstemci uygulaması bir MFC uygulaması OLMALıDıR. Bu DLL tüm MFC 3.0 işlevselliğini destekler (özel durum: VERITABANı sınıfları için UNICODE desteklenmez).

Dekont

Visual C++ sürüm 4.0 itibariyle, bu dll türüne "Uzantı DLL" adı verilir.

Bu not, MFC DLL kümesinin tamamına başvurmak için kullanılır MFCxx.DLL ve bunlar şunlardır:

  • Hata ayıklama: MFCxxD.DLL (birleşik) ve MFCSxxD.LIB (statik).

  • Yayın: MFCxx.DLL (birleşik) ve MFCSxx.LIB (statik).

  • Unicode Hata Ayıklama: MFCxxUD.DLL (birleşik) ve MFCSxxD.LIB (statik).

  • Unicode Sürümü: MFCxxU.DLL (birleşik) ve MFCSxxU.LIB (statik).

Dekont

Kitaplıklar MFCSxx[U][D].LIB MFC paylaşılan DLL'leri ile birlikte kullanılır. Bu kitaplıklar, uygulamaya veya DLL'ye statik olarak bağlanması gereken kod içerir.

Uygulama ilgili içeri aktarma kitaplıklarına bağlanır:

  • Hata ayıklama: MFCxxD.LIB

  • Sürüm: MFCxx.LIB

  • Unicode Hata Ayıklama: MFCxxUD.LIB

  • Unicode Sürümü: MFCxxU.LIB

MFC uzantı DLL'i, genişleten MFCxx.DLL bir DLL'dir (veya diğer MFC paylaşılan DLL'leri). Burada MFC bileşen mimarisi devreye giriyor. MFC sınıfından yararlı bir sınıf türetirseniz veya başka bir MFC benzeri araç seti oluşturursanız, bunu bir DLL'ye yerleştirebilirsiniz. DLL'niz, nihai istemci uygulaması gibi kullanır MFCxx.DLL. MFC uzantısı DLL'i yeniden kullanılabilir yaprak sınıflarına, yeniden kullanılabilir temel sınıflara ve yeniden kullanılabilir görünüm ve belge sınıflarına izin verir.

Artılar ve Dezavantajlar

MFC'nin paylaşılan sürümünü neden kullanmalısınız?

  • Paylaşılan kitaplığın kullanılması daha küçük uygulamalara neden olabilir. (MFC kitaplığının çoğunu kullanan en düşük uygulama 10.000'den küçüktür).

  • MFC'nin paylaşılan sürümü MFC uzantısı DLL'lerini ve normal MFC DLL'lerini destekler.

  • Paylaşılan MFC kitaplıklarını kullanan bir uygulama oluşturmak, statik olarak bağlı bir MFC uygulamasına göre daha hızlıdır. Bunun nedeni MFC'nin kendisini bağlamanın gerekli olmadığındandır. Özellikle bağlayıcının hata ayıklama bilgilerini sıkıştırması gereken derlemelerde DEBUG geçerlidir. Uygulamanız zaten hata ayıklama bilgilerini içeren bir DLL'ye bağlandığında, sıkıştıracak daha az hata ayıklama bilgisi olur.

MFC'nin paylaşılan sürümünü neden kullanmamalısınız:

  • Paylaşılan kitaplığı kullanan bir uygulamayı göndermek için programınızla birlikte ve diğer kitaplıkları göndermeniz MFCxx.DLL gerekir. MFCxx.DLL birçok DLL gibi serbestçe yeniden dağıtılabilir, ancak YINE de DLL'yi KURULUM programınıza yüklemeniz gerekir. Ayrıca, hem programınız hem de MFC DLL'leri tarafından kullanılan diğer yeniden dağıtılabilir kitaplıkları göndermeniz gerekir.

MFC uzantı DLL'sini yazma

MFC uzantı DLL'i, MFC sınıflarının işlevselliğini genişletmek için sınıfları ve işlevleri içeren bir DLL'dir. MFC uzantısı DLL'si, paylaşılan MFC DLL'lerini uygulamanın kullandığı gibi kullanır ve dikkat edilmesi gereken birkaç nokta daha vardır:

  • Derleme işlemi, birkaç ek derleyici ve bağlayıcı seçeneğiyle paylaşılan MFC kitaplıklarını kullanan bir uygulama oluşturmaya benzer.

  • MFC uzantısı DLL'sinde türetilmiş sınıf CWinAppyoktur.

  • MFC uzantı DLL'sinin özel DllMainbir sağlaması gerekir. AppWizard, değiştirebileceğiniz bir DllMain işlev sağlar.

  • MFC uzantı DLL'sinin türleri veya kaynakları uygulamaya dışarı aktarması CRuntimeClass durumunda MFC uzantı DLL'leri normalde bir CDynLinkLibrarybaşlatma yordamı sağlar. Türetilmiş bir sınıfı CDynLinkLibrary , uygulama başına verilerin MFC uzantı DLL'sinin korunması gerekiyorsa kullanılabilir.

Bu konular aşağıda daha ayrıntılı olarak açıklanmıştır. Ayrıca MFC Gelişmiş Kavramlar örneğine DLLHUSKde bakın. Şunların nasıl yapılacağını gösterir:

  • Paylaşılan kitaplıkları kullanarak bir uygulama oluşturun. (DLLHUSK.EXE MFC kitaplıklarına ve diğer DLL'lere dinamik olarak bağlanan bir MFC uygulamasıdır.)

  • Bir MFC uzantısı DLL'i oluşturun. (MFC uzantı DLL'si oluştururken nasıl kullanıldığı gibi _AFXEXT özel bayrakları gösterir.)

  • MFC uzantısı DLL'lerine iki örnek oluşturun. Biri sınırlı dışarı aktarmaları (TESTDLL1) olan bir MFC uzantısı DLL'sinin temel yapısını, diğeri ise tüm sınıf arabirimini (TESTDLL2) dışarı aktarmayı gösterir.

hem istemci uygulaması hem de tüm MFC uzantısı DLL'leri aynı sürümünü MFCxx.DLLkullanmalıdır. MFC DLL'lerinin kurallarını izleyin ve MFC uzantı DLL'nizin hem hata ayıklama hem de sürüm (/release) sürümünü sağlayın. Bu uygulama, istemci programlarının uygulamalarının hem hata ayıklama hem de sürüm sürümlerini oluşturmasına ve bunları tüm DLL'lerin uygun hata ayıklama veya sürüm sürümüyle bağlamasına izin verir.

Dekont

C++ ad düzenleme ve dışarı aktarma sorunları nedeniyle, MFC uzantısı DLL'sinden dışarı aktarma listesi aynı DLL'nin hata ayıklama ve sürüm sürümleri ile farklı platformlar için DLL'ler arasında farklı olabilir. Yayında MFCxx.DLL yaklaşık 2000 dışarı aktarılan giriş noktası vardır; hata ayıklamada MFCxxD.DLL yaklaşık 3000 dışarı aktarılan giriş noktası vardır.

Bellek Yönetimi hakkında Hızlı Not

Bu teknik notun sonuna yakın olan "Bellek Yönetimi" başlıklı bölüm, MFC'nin MFCxx.DLL paylaşılan sürümüyle uygulamasını açıklar. Yalnızca bir MFC uzantı DLL'sini uygulamak için bilmeniz gereken bilgiler burada açıklanmıştır.

MFCxx.DLL ve bir istemci uygulamasının adres alanına yüklenen tüm MFC uzantısı DLL'leri aynı uygulamadaymış gibi aynı bellek ayırıcısını, kaynak yüklemesini ve diğer MFC "genel" durumlarını kullanır. MFC olmayan DLL kitaplıkları ve MFC'ye statik olarak bağlanan normal MFC DLL'leri tam tersini yaptığı için önemlidir: her DLL kendi bellek havuzundan ayrılır.

MFC uzantısı DLL'i bellek ayırırsa, bu bellek uygulama tarafından ayrılan diğer tüm nesnelerle serbestçe kesişebilir. Ayrıca, paylaşılan MFC kitaplıklarını kullanan bir uygulama kilitlenirse, işletim sistemi DLL'yi paylaşan diğer tüm MFC uygulamalarının bütünlüğünü korur.

Benzer şekilde, kaynakları yüklenecek geçerli yürütülebilir dosya gibi diğer "genel" MFC durumları da istemci uygulaması, tüm MFC uzantısı DLL'leri ve MFCxx.DLL kendisi arasında paylaşılır.

MFC uzantısı DLL'i oluşturma

AppWizard'ı kullanarak bir MFC uzantısı DLL projesi oluşturabilirsiniz ve uygun derleyici ve bağlayıcı ayarlarını otomatik olarak oluşturur. Ayrıca değiştirebileceğiniz bir DllMain işlev oluşturur.

Mevcut bir projeyi MFC uzantı DLL'sine dönüştürüyorsanız, MFC'nin paylaşılan sürümünü kullanarak derleyen standart ayarlarla başlayın. Ardından aşağıdaki değişiklikleri yapın:

  • Derleyici bayraklarına ekleyin /D_AFXEXT . Proje Özellikleri iletişim kutusunda C/C++>Önişlemci kategorisini seçin. Öğelerin her birini noktalı virgülle ayırarak MakroLarı Tanımla alanına ekleyin_AFXEXT.

  • /Gy Derleyici anahtarını kaldırın. Proje Özellikleri iletişim kutusunda C/C++>Kod Oluşturma kategorisini seçin. İşlev Düzeyi Bağlamayı Etkinleştir özelliğinin etkinleştirilmediğinden emin olun. Bağlayıcı başvurulmamış işlevleri kaldırmayacağından bu ayar sınıfları dışarı aktarmayı kolaylaştırır. Özgün proje, MFC'ye statik olarak bağlı normal bir MFC DLL'sini derlediyse, (veya ) derleyici seçeneğini (veya /MTd/MDd) olarak /MD değiştirin /MT .

  • BAĞLANDIR seçeneğiyle /DLL bir dışarı aktarma kitaplığı oluşturun. Yeni bir hedef oluşturduğunuzda ve hedef türü olarak Win32 Dinamik Bağlantı Kitaplığı'nı belirttiğinizde bu seçenek ayarlanır.

Üst Bilgi Dosyalarınızı Değiştirme

MFC uzantı DLL'sinin her zamanki hedefi, bazı yaygın işlevleri bu işlevselliği kullanabilen bir veya daha fazla uygulamaya aktarmaktır. Temelde DLL, istemci uygulamalarınız tarafından kullanılmak üzere sınıfları ve genel işlevleri dışarı aktarır.

Her üye işlevinin uygun şekilde içeri veya dışarı aktarma için işaretlendiğinden emin olmak için ve __declspec(dllimport)özel bildirimlerini __declspec(dllexport) kullanın. İstemci uygulamaları sınıflarınızı kullandığında, bunların olarak __declspec(dllimport)bildirilmesi gerekir. MFC uzantısı DLL'sinin kendisi derlendiğinde, işlevler olarak __declspec(dllexport)bildirilmelidir. İstemci programlarının yükleme zamanında bağlanabilmesi için, yerleşik DLL'nin işlevleri de dışarı aktarması gerekir.

Sınıfınızın tamamını dışarı aktarmak için sınıf tanımında kullanın AFX_EXT_CLASS . Çerçeve bu makroyu ne zaman _AFXDLL ve tanımlandığı olarak __declspec(dllexport) tanımlar, ancak tanımlanmadığı zaman _AFXEXT olarak __declspec(dllimport) _AFXEXT tanımlar. _AFXEXT yalnızca MFC uzantı DLL'nizi oluştururken tanımlanır. Örnek:

class AFX_EXT_CLASS CExampleExport : public CObject
{ /* ... class definition ... */ };

Sınıfın Tamamı Dışarı Aktarılmıyor

Bazen yalnızca sınıfınızın tek tek gerekli üyelerini dışarı aktarmak isteyebilirsiniz. Örneğin, türetilmiş bir CDialogsınıfı dışarı aktarırsanız, yalnızca oluşturucuyu ve çağrıyı DoModal dışarı aktarmanız gerekebilir. DLL'nin DEF dosyasını kullanarak bu üyeleri dışarı aktarabilirsiniz, ancak dışarı aktarmanız gereken tek tek üyeler üzerinde de aynı şekilde kullanabilirsiniz AFX_EXT_CLASS .

Örnek:

class CExampleDialog : public CDialog
{
public:
    AFX_EXT_CLASS CExampleDialog();
    AFX_EXT_CLASS int DoModal();
    // rest of class definition
    // ...
};

Bunu yaptığınızda, sınıfın tüm üyelerini dışarı aktarmadığınız için ek bir sorunla karşılaşabilirsiniz. Sorun, MFC makrolarının çalışma biçimindedir. MFC'nin yardımcı makrolarından birkaçı aslında veri üyelerini bildirir veya tanımlar. DLL'nizin de bu veri üyelerini dışarı aktarması gerekir.

Örneğin, DECLARE_DYNAMIC makro MFC uzantısı DLL'sini oluştururken aşağıdaki gibi tanımlanır:

#define DECLARE_DYNAMIC(class_name) \
protected: \
    static CRuntimeClass* PASCAL _GetBaseClass(); \
    public: \
    static AFX_DATA CRuntimeClass class##class_name; \
    virtual CRuntimeClass* GetRuntimeClass() const; \

Başlayan static AFX_DATA satır, sınıfınızın içinde statik bir nesne bildirir. Bu sınıfı doğru bir şekilde dışarı aktarmak ve bir istemci EXE'sinden çalışma zamanı bilgilerine erişmek için bu statik nesneyi dışarı aktarmanız gerekir. Statik nesne değiştirici AFX_DATAile bildirildiğinden, yalnızca DLL'nizi oluştururken olarak __declspec(dllexport) tanımlamanız AFX_DATA gerekir. İstemci yürütülebilir dosyanızı oluştururken olduğu gibi __declspec(dllimport) tanımlayın.

Yukarıda açıklandığı gibi, AFX_EXT_CLASS bu şekilde zaten tanımlanmıştır. Yalnızca sınıf tanımınızla aynı olacak şekilde AFX_EXT_CLASS yeniden AFX_DATA tanımlamanız gerekir.

Örnek:

#undef  AFX_DATA
#define AFX_DATA AFX_EXT_CLASS
class CExampleView : public CView
{
    DECLARE_DYNAMIC()
    // ... class definition ...
};
#undef  AFX_DATA
#define AFX_DATA

MFC, makroları içinde tanımladığı veri öğelerinde her zaman sembolünü kullanır AFX_DATA , bu nedenle bu teknik bu tür senaryolarda çalışır. Örneğin, DECLARE_MESSAGE_MAP için çalışır.

Dekont

Sınıfın seçili üyeleri yerine tüm sınıfı dışarı aktarıyorsanız, statik veri üyeleri otomatik olarak dışarı aktarılır.

DECLARE_SERIAL ve IMPLEMENT_SERIAL makrolarını kullanan sınıflar için ayıklama işlecini otomatik olarak dışarı aktarmak CArchive için aynı tekniği kullanabilirsiniz. Sınıf bildirimlerini (üst bilgi dosyasında bulunur) aşağıdaki kodla köşeli ayraç içine alarak arşiv işlecini dışarı aktarın:

#undef AFX_API
#define AFX_API AFX_EXT_CLASS

/* your class declarations here */

#undef AFX_API
#define AFX_API

_AFXEXT sınırlamaları

MFC uzantısı DLL'lerinin birden çok katmanı olmadığı sürece MFC uzantı DLL'leriniz için ön işlemci simgesini kullanabilirsiniz _AFXEXT . Kendi MFC uzantı DLL'lerinizdeki sınıflara çağrı yapan veya sınıflardan türetilen MFC uzantı DLL'leriniz varsa ve MFC sınıflarından türetilirseniz, belirsizliği önlemek için kendi ön işlemci sembolünüzü kullanmanız gerekir.

Sorun, Win32'de dll'den dışarı aktarmak ve __declspec(dllimport) DLL'den içeri aktarmak için olarak açıkça veri __declspec(dllexport) bildirmeniz gerektiğidir. tanımladığınızda _AFXEXT, MFC üst bilgileri doğru tanımlandığından AFX_EXT_CLASS emin olur.

Birden çok katmanınız olduğunda, örneğin AFX_EXT_CLASS bir simge yeterli değildir: MFC uzantı DLL'i kendi sınıflarını dışarı aktarabilir ve ayrıca başka bir MFC uzantısı DLL'sinden diğer sınıfları içeri aktarabilir. Bu sorunla başa çıkmak için, DLL'yi kullanmak yerine DLL'nin kendisini oluşturduğunuzu gösteren özel bir ön işlemci simgesi kullanın. Örneğin, iki MFC uzantısı DLL'sini A.DLL( ve B.DLL) düşünün. Her biri sırasıyla ve B.Hiçindeki A.H bazı sınıfları dışarı aktarır. B.DLL , sınıflarını A.DLLkullanır. Üst bilgi dosyaları şuna benzer olacaktır:

/* A.H */
#ifdef A_IMPL
    #define CLASS_DECL_A   __declspec(dllexport)
#else
    #define CLASS_DECL_A   __declspec(dllimport)
#endif

class CLASS_DECL_A CExampleA : public CObject
{ /* ... class definition ... */ };

/* B.H */
#ifdef B_IMPL
    #define CLASS_DECL_B   __declspec(dllexport)
#else
    #define CLASS_DECL_B   __declspec(dllimport)
#endif

class CLASS_DECL_B CExampleB : public CExampleA
{ /* ... class definition ... */ };

Derlendiğinde A.DLL ile /DA_IMPL oluşturulur ve ne zaman B.DLL oluşturulursa ile /DB_IMPLoluşturulur. Her DLL için ayrı simgeler kullanılarak dışarı CExampleB aktarılır ve CExampleA oluşturulurken B.DLLiçeri aktarılır. CExampleA, veya başka bir istemci tarafından B.DLL kullanıldığında oluşturulurken A.DLL ve içeri aktarılırken dışarı aktarılır.

Bu katmanlama türü, yerleşik AFX_EXT_CLASS ve _AFXEXT önişlemci sembolleri kullanılırken yapılamaz. Yukarıda açıklanan teknik bu sorunu MFC ile aynı şekilde çözer. MFC, OLE, Veritabanı ve Ağ MFC uzantısı DLL'lerini oluştururken bu tekniği kullanır.

Sınıfın Tamamı Yine Dışarı Aktarılmıyor

Sınıfın tamamını dışarı aktarmadığınız zamanlarda da özel bir özen göstermeniz gerekir. MFC makroları tarafından oluşturulan gerekli veri öğelerinin doğru şekilde dışarı aktarıldığından emin olun. Bunu, belirli bir sınıfınızın makrosna yeniden tanımlayarak AFX_DATA yapabilirsiniz. Sınıfın tamamını dışarı aktarmadığınız her durumda yeniden tanımlayın.

Örnek:

// A.H
#ifdef A_IMPL
    #define CLASS_DECL_A  _declspec(dllexport)
#else
    #define CLASS_DECL_A  _declspec(dllimport)
#endif

#undef  AFX_DATA
#define AFX_DATA CLASS_DECL_A

class CExampleA : public CObject
{
    DECLARE_DYNAMIC()
    CLASS_DECL_A int SomeFunction();
    // class definition
    // ...
};

#undef AFX_DATA
#define AFX_DATA

Dllmain

MFC uzantı DLL'niz için ana kaynak dosyanıza yerleştirmeniz gereken kod aşağıdadır. Standart dahil olduktan sonra gelmelidir. Bir MFC uzantı DLL'si için başlangıç dosyaları oluşturmak için AppWizard kullandığınızda, sizin için bir DllMain sağlar.

#include "afxdllx.h"

static AFX_EXTENSION_MODULE extensionDLL;

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
{
   if (dwReason == DLL_PROCESS_ATTACH)
   {
      // MFC extension DLL one-time initialization
      if (!AfxInitExtensionModule(
             extensionDLL, hInstance))
         return 0;

      // TODO: perform other initialization tasks here
   }
   else if (dwReason == DLL_PROCESS_DETACH)
   {
      // MFC extension DLL per-process termination
      AfxTermExtensionModule(extensionDLL);

      // TODO: perform other cleanup tasks here
   }
   return 1;   // ok
}

çağrısı AfxInitExtensionModule , modülün çalışma zamanı sınıflarını (CRuntimeClass yapıları) ve nesne fabrikalarını (COleObjectFactory nesneleri) daha sonra CDynLinkLibrary nesne oluşturulduğunda kullanmak üzere yakalar. (isteğe bağlı) çağrısı AfxTermExtensionModule , her işlem MFC uzantı DLL'sinden ayrılırken (işlem çıktığında veya DLL bir FreeLibrary çağrı tarafından kaldırıldığında gerçekleşir) MFC uzantısı DLL'sini temizlemesine olanak tanır. MFC uzantı DLL'lerinin çoğu dinamik olarak yüklenmediğinden (normalde, içeri aktarma kitaplıkları aracılığıyla bağlandığı için), çağrısı AfxTermExtensionModule genellikle gerekli değildir.

Uygulamanız MFC uzantısı DLL'lerini dinamik olarak yükleyip serbest bırakırsa, yukarıda gösterildiği gibi çağırın AfxTermExtensionModule . Ayrıca, uygulamanız birden çok iş parçacığı kullanıyorsa veya dinamik olarak bir MFC uzantı DLL'sini yüklüyorsa ve AfxFreeLibrary (Win32 işlevleri LoadLibrary ve FreeLibraryyerine) kullandığınızdan AfxLoadLibrary emin olun. AfxFreeLibrary MFC uzantısı DLL yüklendiğinde ve kaldırıldığında yürütülen başlatma ve kapatma kodunun kullanılması AfxLoadLibrary ve kullanılması genel MFC durumunu bozmaz.

Üst bilgi dosyasıAFXDLLX.H, ve CDynLinkLibraryiçin tanımı gibi MFC uzantısı DLL'lerinde kullanılan yapıların özel tanımlarını AFX_EXTENSION_MODULE içerir.

Genel extensionDLL gösterildiği gibi bildirilmelidir. MFC'nin 16 bit sürümünden farklı olarak, bu süre boyunca bellek ayırabilir ve MFC işlevlerini çağırabilirsiniz.MFCxx.DLL DllMain

Kaynakları ve Sınıfları Paylaşma

Basit MFC uzantısı DLL'lerinin istemci uygulamasına yalnızca birkaç düşük bant genişliği işlevini dışarı aktarması gerekir ve başka bir şey yoktur. Daha fazla kullanıcı arabirimi kullanan DLL'ler, kaynakları ve C++ sınıflarını istemci uygulamasına aktarmak isteyebilir.

Kaynakları dışarı aktarma işlemi bir kaynak listesi aracılığıyla yapılır. Her uygulamada tek tek bağlantılı nesnelerin listesi bulunur CDynLinkLibrary . Bir kaynak ararken, kaynakları yükleyen standart MFC uygulamalarının çoğu önce geçerli kaynak modülüne (AfxGetResourceHandle) bakar ve bulunamazsa istenen kaynağı yüklemeye çalışan nesnelerin listesi CDynLinkLibrary görüntülenir.

C++ sınıf adı verilen C++ nesnelerinin dinamik olarak oluşturulması benzerdir. MFC nesne seri durumdan çıkarma mekanizmasının CRuntimeClass tüm nesnelerin kayıtlı olması gerekir, böylece daha önce depolananlara göre gerekli türde C++ nesnesini dinamik olarak oluşturarak yeniden yapılandırabilir.

İstemci uygulamasının MFC uzantı DLL'nizde DECLARE_SERIALolan sınıfları kullanmasını istiyorsanız, bunları istemci uygulamasına görünür hale getirmek için sınıflarınızı dışarı aktarmanız gerekir. Ayrıca listeden yürüyerek CDynLinkLibrary de yapılır.

MFC Gelişmiş Kavramlar örneğinde DLLHUSK, liste şuna benzer:

head ->   DLLHUSK.EXE   - or - DLLHUSK.EXE
               |                    |
          TESTDLL2.DLL         TESTDLL2.DLL
               |                    |
          TESTDLL1.DLL         TESTDLL1.DLL
               |                    |
               |                    |
           MFC90D.DLL           MFC90.DLL

Girdi MFCxx.DLL genellikle kaynak ve sınıf listesinde en son gelir. MFCxx.DLL tüm standart komut kimlikleri için istem dizeleri dahil olmak üzere tüm standart MFC kaynaklarını içerir. Listenin sonuna yerleştirmek, hem DLL'lerin hem de istemci uygulamasının kendi kopyalarına sahip olmak yerine içindeki MFCxx.DLLpaylaşılan kaynaklara güvenmesini sağlar.

Tüm DLL'lerin kaynaklarını ve sınıf adlarını istemci uygulamasının ad alanıyla birleştirmek, hangi kimlikleri veya adları seçtiğinize dikkat etmek zorunda olmanıza neden olur. Kaynaklarınızı veya bir CDynLinkLibrary nesneyi istemci uygulamasına dışarı aktarmayarak bu özelliği devre dışı bırakabilirsiniz. Örnek, DLLHUSK paylaşılan kaynak adı alanını birden çok üst bilgi dosyası kullanarak yönetir. Paylaşılan kaynak dosyalarını kullanma hakkında daha fazla ipucu için bkz . Teknik Not 35 .

DLL'yi başlatma

Yukarıda belirtildiği gibi, genellikle kaynaklarınızı ve sınıflarınızı istemci uygulamasına dışarı aktarmak için bir CDynLinkLibrary nesne oluşturmak istersiniz. DLL'yi başlatmak için dışarı aktarılan bir giriş noktası sağlamanız gerekir. En düşük düzeyde, bağımsız değişken içermeyen ve hiçbir şey döndüren bir rutindir, ancak istediğiniz herhangi bir void şey olabilir.

Bu yaklaşımı kullanıyorsanız, DLL'nizi kullanmak isteyen her istemci uygulamasının bu başlatma yordamını çağırması gerekir. Bu CDynLinkLibrary nesneyi çağrısı AfxInitExtensionModuleyaptıktan hemen sonra içinde DllMain de ayırabilirsiniz.

Başlatma yordamının geçerli uygulamanın yığınında MFC uzantı DLL'nize bağlı bir CDynLinkLibrary nesne oluşturması gerekir. Bunu yapmak için aşağıdakine benzer bir işlev tanımlayabilirsiniz:

extern "C" extern void WINAPI InitXxxDLL()
{
    new CDynLinkLibrary(extensionDLL);
}

Bu örnekteki initXxxDLL yordam adı istediğiniz her şey olabilir. olması gerekmez extern "C", ancak dışarı aktarma listesinin korunmasını kolaylaştırır.

Dekont

Normal bir MFC DLL'sinden MFC uzantı DLL'nizi kullanıyorsanız, bu başlatma işlevini dışarı aktarmanız gerekir. Bu işlev, MFC uzantısı DLL sınıfları veya kaynakları kullanmadan önce normal MFC DLL'sinden çağrılmalıdır.

Girdileri Dışarı Aktarma

Sınıflarınızı dışarı aktarmanın basit yolu, __declspec(dllexport) dışarı aktarmak istediğiniz her sınıf ve genel işlevde ve kullanmaktır__declspec(dllimport). Çok daha kolaydır, ancak aşağıda açıklandığı gibi bir DEF dosyasındaki her giriş noktasını adlandırmaktan daha az verimlidir. Bunun nedeni, dışarı aktarılan işlevler üzerinde daha az denetim sahibi olmanızdır. Ayrıca işlevleri sıralı olarak dışarı aktaramazsınız. TESTDLL1 ve TESTDLL2 girdilerini dışarı aktarmak için bu yöntemi kullanır.

Daha verimli bir yöntem, her girdiyi DEF dosyasında adlandırarak dışarı aktarmaktır. Bu yöntem tarafından MFCxx.DLLkullanılır. DLL'mizden seçmeli olarak dışarı aktardığımız için, hangi arabirimleri dışarı aktarmak istediğimize karar vermemiz gerekir. Bağlayıcıya mangled adlarını DEF dosyasındaki girdiler biçiminde belirtmeniz gerektiğinden bu zordur. Gerçekten sembolik bir bağlantınız olması gerekmediği sürece C++ sınıfını dışarı aktarmayın.

C++ sınıflarını daha önce bir DEF dosyasıyla dışarı aktarmayı denediyseniz, bu listeyi otomatik olarak oluşturmak için bir araç geliştirmek isteyebilirsiniz. İki aşamalı bağlantı işlemi kullanılarak yapılabilir. DLL'nizi dışarı aktarma olmadan bir kez bağlayın ve bağlayıcının bir MAP dosyası oluşturmasına izin verin. MAP dosyası dışarı aktarılacak işlevlerin listesini içerir. Bazı yeniden düzenlemelerle, DEF dosyanız için EXPORT girdilerini oluşturmak için kullanabilirsiniz. ve ole ve veritabanı MFC uzantısı DLL'leri için MFCxx.DLL dışarı aktarma listesi, sayı olarak birkaç bin, bu tür bir işlemle oluşturulmuştur (tam otomatik olmasa da ve arada bir el ayarlaması gerektirir).

CWinApp ve CDynLinkLibrary karşılaştırması

MFC uzantı DLL'sinin kendi türetilmiş nesnesi CWinAppyoktur. Bunun yerine, istemci uygulamasının CWinApptüretilmiş nesnesiyle çalışması gerekir. Bu, istemci uygulamasının ana ileti pompasına, boşta döngüsüne vb. sahip olduğu anlamına gelir.

MFC uzantı DLL'nizin her uygulama için ek veri tutması gerekiyorsa, öğesinden CDynLinkLibrary yeni bir sınıf türetebilir ve yukarıda açıklanan yordamda InitXxxDLL oluşturabilirsiniz. DLL çalıştırılırken, geçerli uygulamanın nesne listesini CDynLinkLibrary denetleyerek söz konusu MFC uzantı DLL'sine ait olanı bulabilir.

DLL Uygulamanızda Kaynakları Kullanma

Yukarıda belirtildiği gibi, varsayılan kaynak yükü istenen kaynağı içeren ilk EXE veya DLL'yi arayan nesnelerin listesini CDynLinkLibrary gösterir. Tüm MFC API'leri ve tüm iç kodlar, nerede bulunursa bulun herhangi bir kaynağı bulmak için kaynak listesinde gezinmek için kullanır AfxFindResourceHandle .

Kaynakları yalnızca belirli bir yerden yüklemek istiyorsanız API'leri AfxGetResourceHandle kullanın ve AfxSetResourceHandle eski tanıtıcıyı kaydedip yeni tanıtıcıyı ayarlayın. İstemci uygulamasına dönmeden önce eski kaynak tanıtıcısını geri yüklemeyi unutmayın. Örnek TESTDLL2, menüyü açıkça yüklemek için bu yaklaşımı kullanır.

Listede yürümenin bazı dezavantajları vardır: biraz daha yavaştır ve kaynak kimliği aralıklarının yönetilmesini gerektirir. Birkaç MFC uzantısı DLL'sine bağlanan bir istemci uygulamasının DLL örneği tanıtıcısını belirtmek zorunda kalmadan DLL tarafından sağlanan herhangi bir kaynağı kullanabilmesi avantajına sahiptir. AfxFindResourceHandle , belirli bir eşleşmeyi aramak için kaynak listesinde yürümek için kullanılan bir API'dir. Kaynağın adını ve türünü alır ve kaynağı ilk bulduğu kaynak tutamacını veya NULL değerini döndürür.

DLL Sürümünü Kullanan Bir Uygulama Yazma

Uygulama Gereksinimleri

MFC'nin paylaşılan sürümünü kullanan bir uygulama birkaç temel kurala uymalıdır:

  • Bir nesnesi olmalıdır CWinApp ve ileti pompası için standart kurallara uymalıdır.

  • Gerekli derleyici bayrakları kümesiyle derlenmelidir (aşağıya bakın).

  • MFCxx içeri aktarma kitaplıklarıyla bağlantı oluşturması gerekir. Gerekli derleyici bayraklarını ayarlayarak, MFC üst bilgileri bağlantı zamanında uygulamanın hangi kitaplığa bağlanması gerektiğini belirler.

  • Yürütülebilir dosyayı MFCxx.DLL çalıştırmak için yolda veya Windows sistem dizininde olmalıdır.

Geliştirme Ortamı ile Oluşturma

Standart varsayılanların çoğuyla iç derleme dosyasını kullanıyorsanız, DLL sürümünü derlemek için projeyi kolayca değiştirebilirsiniz.

Aşağıdaki adım, (hata ayıklama için) ve (yayın için) ile NAFXCWD.LIB bağlantılı doğru işlevli bir MFC uygulamanız olduğunu ve NAFXCW.LIB bunu MFC kitaplığının paylaşılan sürümünü kullanacak şekilde dönüştürmek istediğinizi varsayar. Visual Studio ortamını çalıştırıyorsunuz ve bir iç proje dosyanız var.

  1. Projeler menüsünde Özellikler'i seçin. Project Defaults altındaki Genel sayfasında, Microsoft Foundation Sınıflarını Paylaşılan DLL'de (MFCxx(d.dll) MFC Kullanacak şekilde ayarlayın.

NMAKE ile derleme

Derleyicinin dış derleme dosyası özelliğini kullanıyorsanız veya doğrudan NMAKE kullanıyorsanız, gerekli derleyici ve bağlayıcı seçeneklerini desteklemek için derleme dosyanızı düzenlemeniz gerekir.

Gerekli derleyici bayrakları:

  • /D_AFXDLL /MD /D_AFXDLL

Standart MFC üst bilgilerinin _AFXDLL tanımlanması için simge gerekir.

  • /MD Uygulamanın C çalışma zamanı kitaplığının DLL sürümünü kullanması gerekir.

Diğer tüm derleyici bayrakları MFC varsayılanlarını izler (örneğin, _DEBUG hata ayıklama için).

Bağlayıcı kitaplık listesini düzenleyin. olarak MFCxxD.LIB değiştirin NAFXCWD.LIB ve olarak MFCxx.LIBdeğiştirinNAFXCW.LIB. LIBC.LIB öğesini MSVCRT.LIB ile değiştirin. Diğer MFC kitaplıkları gibi, C çalışma zamanı kitaplıklarının önüne yerleştirilmesi önemlidirMFCxxD.LIB.

İsteğe bağlı olarak hem sürüm hem de hata ayıklama kaynak derleyicisi seçeneklerinize ekleyin /D_AFXDLL (kaynakları /Rile derleyen seçenek). Bu seçenek, MFC DLL'lerinde bulunan kaynakları paylaşarak son yürütülebilir dosyanızı küçültür.

Bu değişiklikler yapıldıktan sonra tam yeniden derleme gerekir.

Örnekleri Oluşturma

MFC örnek programlarının çoğu Visual C++ veya komut satırından paylaşılan NMAKE uyumlu MAKEFILE'dan oluşturulabilir.

Bu örneklerden herhangi birini kullanacak MFCxx.DLLşekilde dönüştürmek için, MAK dosyasını Visual C++ içine yükleyebilir ve yukarıda açıklandığı gibi Proje seçeneklerini ayarlayabilirsiniz. NMAKE derlemesini kullanıyorsanız, NMAKE komut satırında belirtebilirsiniz AFXDLL=1 ve bu, paylaşılan MFC kitaplıklarını kullanarak örneği oluşturur.

MFC Gelişmiş Kavramlar örneği DLLHUSK , MFC'nin DLL sürümüyle oluşturulur. Bu örnek, ile bağlantılı bir uygulamanın nasıl derlenmiş olduğunu göstermekle MFCxx.DLLkalmaz, aynı zamanda bu teknik notun devamında açıklanan MFC uzantı DLL'leri gibi MFC DLL paketleme seçeneğinin diğer özelliklerini de gösterir.

Paketleme Notları

DLL'lerin (MFCxx.DLL ve MFCxxU.DLL) yayın sürümleri serbestçe yeniden dağıtılabilir. DLL'lerin hata ayıklama sürümleri serbestçe yeniden dağıtılamaz ve yalnızca uygulamanızın geliştirilmesi sırasında kullanılmalıdır.

Hata ayıklama DLL'leri hata ayıklama bilgileriyle birlikte sağlanır. Visual C++ hata ayıklayıcısını kullanarak hem uygulamanızın hem de DLL'nin yürütülmesini izleyebilirsiniz. Yayın DLL'leri (MFCxx.DLL ve MFCxxU.DLL) hata ayıklama bilgilerini içermez.

DLL'leri özelleştirir veya yeniden oluşturursanız, bunlara "MFCxx" dışında bir ad vermelisiniz. MFC SRC dosyası MFCDLL.MAK derleme seçeneklerini açıklar ve DLL'yi yeniden adlandırma mantığını içerir. Bu DLL'ler büyük olasılıkla birçok MFC uygulaması tarafından paylaşıldığından, dosyaların yeniden adlandırılması gerekir. MFC DLL'lerinin özel sürümünün sistemde yüklü olanlarla değiştirilmesi, paylaşılan MFC DLL'lerini kullanarak başka bir MFC uygulamasını bozabilir.

MFC DLL'lerinin yeniden oluşturulması önerilmez.

MFCxx.DLL Nasıl Uygulanır?

Aşağıdaki bölümde MFC DLL'sinin (MFCxx.DLL ve MFCxxD.DLL) nasıl uygulandığı açıklanmaktadır. Tek yapmak istediğiniz MFC DLL'yi uygulamanızla kullanmaksa buradaki ayrıntıları anlamak da önemli değildir. Buradaki ayrıntılar MFC uzantısı DLL'sinin nasıl yazılması gerektiğini anlamak için gerekli değildir, ancak bu uygulamayı anlamak kendi DLL'nizi yazmanıza yardımcı olabilir.

Uygulamaya Genel Bakış

MFC DLL, yukarıda açıklandığı gibi MFC uzantı DLL'sinin gerçekten özel bir örneğidir. Çok sayıda sınıf için çok sayıda dışarı aktarma işlemi vardır. MFC DLL'sinde yaptığımız ve normal MFC uzantı DLL'sinden daha da özel hale getiren birkaç ek işlem vardır.

Win32 İşin Çoğunu Yapar

MFC'nin 16 bit sürümü yığın kesimindeki uygulama başına veriler, bazı 80x86 derleme kodları tarafından oluşturulan özel segmentler, işlem başına özel durum bağlamları ve diğer teknikler dahil olmak üzere bir dizi özel teknik gerekiyordu. Win32, çoğu zaman istediğiniz dll'deki işlem başına verileri doğrudan destekler. Çoğunlukla MFCxx.DLL yalnızca NAFXCW.LIB bir DLL içinde paketlenmiş. MFC kaynak koduna bakarsanız, yapılması gereken çok fazla özel durum olmadığından birkaç #ifdef _AFXDLL servis talebi görürsünüz. Windows 3.1'de Win32 ile ilgilenmek için özel durumlar vardır (diğer adıyla Win32s). Win32'ler işlem başına DLL verilerini doğrudan desteklemez. MFC DLL'sinin, işlem yerel verilerini almak için iş parçacığı yerel depolama (TLS) Win32 API'lerini kullanması gerekir.

Kitaplık Kaynakları, Ek Dosyalar Üzerindeki Etkisi

Sürümün _AFXDLL normal MFC sınıf kitaplığı kaynakları ve üst bilgileri üzerindeki etkisi nispeten küçük. Ana AFXWIN.H üst bilgi tarafından eklenen özel bir sürüm dosyası (AFXV_DLL.H) ve ek bir üst bilgi dosyası (AFXDLL_.H) vardır. Üst bilgi, AFXDLL_.H hem uygulamaların hem de _AFXDLL MFC uzantısı DLL'lerinin sınıfını ve diğer uygulama ayrıntılarını içerirCDynLinkLibrary. AFXDLLX.H Üst bilgi MFC uzantısı DLL'leri oluşturmak için sağlanır (ayrıntılar için yukarıya bakın).

MFC SRC'deki MFC kitaplığının normal kaynaklarının #ifdef altında _AFXDLL bazı ek koşullu kodlar vardır. Ek bir kaynak dosya (DLLINIT.CPP), MFC'nin paylaşılan sürümü için ek DLL başlatma kodunu ve diğer tutkalı içerir.

MFC'nin paylaşılan sürümünü oluşturmak için ek dosyalar sağlanır. (DLL'yi derleme hakkında ayrıntılı bilgi için aşağıya bakın.)

  • DLL'nin hata ayıklama () ve sürüm (MFCxxD.DEFMFCxx.DEF) sürümleri için MFC DLL giriş noktalarını dışarı aktarmak için iki DEF dosyası kullanılır.

  • RC dosyası (MFCDLL.RC) tüm standart MFC kaynaklarını ve DLL için bir VERSIONINFO kaynağı içerir.

  • ClassWizard kullanarak MFC sınıflarına göz atmaya izin vermek için bir CLW dosyası (MFCDLL.CLW) sağlanır. Bu özellik MFC'nin DLL sürümüne özel değildir.

Bellek Yönetimi

kullanan MFCxx.DLL bir uygulama, tarafından MSVCRTxx.DLLsağlanan ortak bir bellek ayırıcısı kullanır, paylaşılan C-runtime DLL'si. Uygulama, tüm MFC uzantı DLL'leri ve MFC DLL'leri bu paylaşılan bellek ayırıcısını kullanır. MFC DLL'leri, bellek ayırma için paylaşılan DLL kullanarak daha sonra uygulama tarafından boşaltılan veya tam tersi olan belleği ayırabilir. Hem uygulamanın hem de DLL'nin aynı ayırıcıyı kullanması gerektiğinden, C++ genel operator new veya operator deleteöğesini geçersiz kılmamalısınız. Aynı kurallar, C çalışma zamanı bellek ayırma yordamlarının (, , reallocfreeve diğerleri gibimalloc) geri kalanı için de geçerlidir.

Sıralar ve sınıf __declspec(dllexport) ve DLL adlandırma

C++ derleyicisinin işlevselliğini kullanmıyoruz class__declspec(dllexport) . Bunun yerine, dışarı aktarmaların listesi sınıf kitaplığı kaynaklarına (MFCxx.DEF ve MFCxxD.DEF) eklenir. Yalnızca belirli bir giriş noktası kümesi (işlevler ve veriler) dışarı aktarılır. MFC özel uygulama işlevleri veya sınıfları gibi diğer simgeler dışarı aktarılamaz. Tüm dışarı aktarmalar, yerleşik veya yerleşik olmayan ad tablosunda dize adı olmadan sıralı olarak yapılır.

Kullanmak class__declspec(dllexport) daha küçük DLL'ler oluşturmak için uygun bir alternatif olabilir, ancak MFC gibi büyük bir DLL'de varsayılan dışarı aktarma mekanizması verimlilik ve kapasite sınırlarına sahiptir.

Bunun anlamı, yayında MFCxx.DLL çok fazla yürütme veya yükleme hızından ödün vermeden yalnızca 800 KB'lık büyük miktarda işlev paketleyebiliriz. MFCxx.DLL bu teknik kullanılmamış olsaydı 100 KB daha büyük olurdu. Bu teknik, DEF dosyasının sonuna ek giriş noktaları eklemeyi mümkün kılar. Sıralı olarak dışarı aktarmanın hız ve boyut verimliliğinden ödün vermeden basit sürüm oluşturma olanağı sağlar. MFC sınıf kitaplığındaki ana sürüm düzeltmeleri kitaplık adını değiştirir. Yani, MFC30.DLL MFC sınıf kitaplığının 3.0 sürümünü içeren yeniden dağıtılabilir DLL'dir. Bu DLL'nin yükseltmesi, örneğin, varsayımsal bir MFC 3.1'de, bunun yerine DLL olarak adlandırılır MFC31.DLL . MFC kaynak kodunu MFC DLL'sinin özel bir sürümünü oluşturacak şekilde değiştirirseniz, farklı bir ad (ve tercihen adında "MFC" olmayan bir ad) kullanın.

Ayrıca bkz.

Sayıya Göre Teknik Notlar
Kategoriye Göre Teknik Notlar