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 DLLScreenCap
açı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) veMFCSxxD.LIB
(statik).Yayın:
MFCxx.DLL
(birleşik) veMFCSxx.LIB
(statik).Unicode Hata Ayıklama:
MFCxxUD.DLL
(birleşik) veMFCSxxD.LIB
(statik).Unicode Sürümü:
MFCxxU.DLL
(birleşik) veMFCSxxU.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
CWinApp
yoktur.MFC uzantı DLL'sinin özel
DllMain
bir sağlaması gerekir. AppWizard, değiştirebileceğiniz birDllMain
işlev sağlar.MFC uzantı DLL'sinin türleri veya kaynakları uygulamaya dışarı aktarması
CRuntimeClass
durumunda MFC uzantı DLL'leri normalde birCDynLinkLibrary
baş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 DLLHUSK
de 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.DLL
kullanmalı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 CDialog
sı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_DATA
ile 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.H
içindeki A.H
bazı sınıfları dışarı aktarır. B.DLL
, sınıflarını A.DLL
kullanı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_IMPL
oluşturulur. Her DLL için ayrı simgeler kullanılarak dışarı CExampleB
aktarılır ve CExampleA
oluşturulurken B.DLL
iç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 FreeLibrary
yerine) 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 CDynLinkLibrary
iç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_SERIAL
olan 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.DLL
paylaşı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ı AfxInitExtensionModule
yaptı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.DLL
kullanı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 CWinApp
yoktur. Bunun yerine, istemci uygulamasının CWinApp
tü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.
- 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.LIB
değ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ı /R
ile 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.DLL
kalmaz, 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.DEF
MFCxx.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 birVERSIONINFO
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.DLL
sağ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 (, , realloc
free
ve 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.