Bagikan melalui


TN033: Versi DLL MFC

Catatan ini menjelaskan bagaimana Anda dapat menggunakan MFCxx.DLL dan MFCxxD.DLL (di mana xx adalah nomor versi MFC) pustaka tautan dinamis bersama dengan aplikasi MFC dan DLL ekstensi MFC. Untuk informasi selengkapnya tentang DLL MFC reguler, lihat Menggunakan MFC sebagai Bagian dari DLL.

Catatan teknis ini mencakup tiga aspek DLL. Dua terakhir adalah untuk pengguna yang lebih canggih:

Jika Anda tertarik untuk membangun DLL menggunakan MFC yang dapat digunakan dengan aplikasi non-MFC (dikenal sebagai DLL MFC biasa), lihat Catatan Teknis 11.

Gambaran Umum Dukungan MFCxx.DLL: Terminologi dan File

DLL MFC reguler: Anda menggunakan DLL MFC reguler untuk membangun DLL mandiri menggunakan beberapa kelas MFC. Antarmuka di seluruh batas Aplikasi/DLL adalah antarmuka "C", dan aplikasi klien tidak harus menjadi aplikasi MFC.

DLL MFC reguler adalah versi DLL yang didukung di MFC 1.0. Mereka dijelaskan dalam Catatan Teknis 11 dan sampel DLLScreenCapKonsep Lanjutan MFC .

Catatan

Pada Visual C++ versi 4.0, istilah USRDLL usang dan telah digantikan oleh DLL MFC reguler yang secara statis ditautkan ke MFC. Anda juga dapat membangun DLL MFC reguler yang secara dinamis menautkan ke MFC.

MFC 3.0 (ke atas) mendukung DLL MFC reguler dengan semua fungsionalitas baru termasuk kelas OLE dan Database.

AFXDLL: Juga disebut sebagai versi bersama pustaka MFC. Ini adalah dukungan DLL baru yang ditambahkan di MFC 2.0. Pustaka MFC itu sendiri berada di sejumlah DLL (dijelaskan di bawah). Aplikasi klien atau DLL secara dinamis menautkan DLL yang diperlukan. Antarmuka di seluruh batas aplikasi/DLL adalah antarmuka kelas C++/MFC. Aplikasi klien HARUS merupakan aplikasi MFC. DLL ini mendukung semua fungsionalitas MFC 3.0 (pengecualian: UNICODE tidak didukung untuk kelas database).

Catatan

Pada Visual C++ versi 4.0, jenis DLL ini disebut sebagai "DLL Ekstensi."

Catatan ini akan digunakan MFCxx.DLL untuk merujuk ke seluruh set DLL MFC, yang meliputi:

  • Debug: MFCxxD.DLL (gabungan) dan MFCSxxD.LIB (statis).

  • Rilis: MFCxx.DLL (gabungan) dan MFCSxx.LIB (statis).

  • Unicode Debug: MFCxxUD.DLL (gabungan) dan MFCSxxD.LIB (statis).

  • Rilis Unicode: MFCxxU.DLL (gabungan) dan MFCSxxU.LIB (statis).

Catatan

MFCSxx[U][D].LIB Pustaka digunakan bersama dengan DLL bersama MFC. Pustaka ini berisi kode yang harus ditautkan secara statis ke aplikasi atau DLL.

Aplikasi menautkan ke pustaka impor yang sesuai:

  • Debug: MFCxxD.LIB

  • Rilis: MFCxx.LIB

  • Debug Unicode: MFCxxUD.LIB

  • Rilis Unicode: MFCxxU.LIB

DLL ekstensi MFC adalah DLL yang memperluas MFCxx.DLL (atau DLL bersama MFC lainnya). Di sini, arsitektur komponen MFC dimulai. Jika Anda mendapatkan kelas yang berguna dari kelas MFC, atau membangun toolkit seperti MFC lain, Anda dapat menempatkannya di DLL. DLL Anda menggunakan MFCxx.DLL, seperti halnya aplikasi klien utama. DLL ekstensi MFC mengizinkan kelas daun yang dapat digunakan kembali, kelas dasar yang dapat digunakan kembali, dan tampilan yang dapat digunakan kembali dan kelas dokumen.

Pro dan Kontra

Mengapa Anda harus menggunakan versi bersama MFC

  • Menggunakan pustaka bersama dapat menghasilkan aplikasi yang lebih kecil. (Aplikasi minimal yang menggunakan sebagian besar pustaka MFC kurang dari 10K).

  • Versi bersama MFC mendukung DLL ekstensi MFC dan DLL MFC reguler.

  • Lebih cepat untuk membangun aplikasi yang menggunakan pustaka MFC bersama daripada aplikasi MFC yang ditautkan secara statis. Itu karena tidak perlu menautkan MFC itu sendiri. Ini terutama berlaku dalam build di DEBUG mana linker harus memampatkan informasi debug. Saat aplikasi Anda ditautkan ke DLL yang sudah berisi informasi debug, ada lebih sedikit informasi debug untuk dikompilasi.

Mengapa Anda tidak menggunakan MFC versi bersama:

  • Mengirim aplikasi yang menggunakan pustaka bersama mengharuskan Anda mengirim MFCxx.DLL dan pustaka lain dengan program Anda. MFCxx.DLL dapat didistribusikan ulang secara bebas seperti banyak DLL, tetapi Anda masih harus menginstal DLL dalam program PENYIAPAN Anda. Selain itu, Anda harus mengirim pustaka lain yang dapat didistribusikan ulang yang digunakan oleh program Anda dan DLL MFC itu sendiri.

Cara Menulis DLL ekstensi MFC

DLL ekstensi MFC adalah DLL yang berisi kelas dan fungsi untuk memperluas fungsionalitas kelas MFC. DLL ekstensi MFC menggunakan DLL MFC bersama dengan cara yang sama seperti aplikasi menggunakannya, dengan beberapa pertimbangan tambahan:

  • Proses build mirip dengan membangun aplikasi yang menggunakan pustaka MFC bersama dengan beberapa opsi compiler dan linker tambahan.

  • DLL ekstensi MFC tidak memiliki CWinAppkelas -turunan.

  • DLL ekstensi MFC harus menyediakan khusus DllMain. AppWizard menyediakan DllMain fungsi yang dapat Anda ubah.

  • DLL ekstensi MFC biasanya menyediakan rutinitas inisialisasi CRuntimeClass untuk membuat CDynLinkLibrary, jika DLL ekstensi MFC mengekspor jenis atau sumber daya ke aplikasi. Kelas turunan CDynLinkLibrary dapat digunakan jika data per aplikasi harus dikelola oleh DLL ekstensi MFC.

Pertimbangan ini dijelaskan secara lebih rinci di bawah ini. Lihat juga sampel DLLHUSKKonsep Lanjutan MFC . Ini menunjukkan cara:

  • Buat aplikasi menggunakan pustaka bersama. (DLLHUSK.EXE adalah aplikasi MFC yang secara dinamis menautkan ke pustaka MFC dan DLL lainnya.)

  • Buat DLL ekstensi MFC. (Ini menunjukkan bagaimana bendera khusus seperti _AFXEXT digunakan saat membangun DLL ekstensi MFC.)

  • Buat dua contoh DLL ekstensi MFC. Satu menunjukkan struktur dasar DLL ekstensi MFC dengan ekspor terbatas (TESTDLL1) dan yang lain menunjukkan mengekspor seluruh antarmuka kelas (TESTDLL2).

Aplikasi klien dan DLL ekstensi MFC apa pun harus menggunakan versi yang sama dari MFCxx.DLL. Ikuti konvensi DLL MFC dan berikan versi debug dan rilis (/release) DLL ekstensi MFC Anda. Praktik ini memungkinkan program klien untuk membangun versi debug dan rilis aplikasi mereka dan menautkannya dengan versi debug atau rilis yang sesuai dari semua DLL.

Catatan

Karena masalah mangling dan ekspor nama C++, daftar ekspor dari DLL ekstensi MFC mungkin berbeda antara versi debug dan rilis DLL dan DLL yang sama untuk platform yang berbeda. Rilis MFCxx.DLL ini memiliki sekitar 2000 titik masuk yang diekspor; debug MFCxxD.DLL memiliki sekitar 3000 titik masuk yang diekspor.

Catatan Cepat tentang Manajemen Memori

Bagian berjudul "Manajemen Memori", di dekat akhir catatan teknis ini, menjelaskan implementasi MFCxx.DLL dengan versi bersama MFC. Informasi yang perlu Anda ketahui untuk mengimplementasikan hanya DLL ekstensi MFC dijelaskan di sini.

MFCxx.DLL dan semua DLL ekstensi MFC yang dimuat ke dalam ruang alamat aplikasi klien akan menggunakan alokator memori yang sama, pemuatan sumber daya, dan status "global" MFC lainnya seolah-olah mereka berada dalam aplikasi yang sama. Ini signifikan karena pustaka DLL non-MFC dan DLL MFC reguler yang secara statis ditautkan ke MFC melakukan kebalikannya: setiap DLL mengalokasikan keluar dari kumpulan memorinya sendiri.

Jika DLL ekstensi MFC mengalokasikan memori, maka memori tersebut dapat dengan bebas melakukan intermix dengan objek lain yang dialokasikan aplikasi. Selain itu, jika aplikasi yang menggunakan pustaka MFC bersama mengalami crash, sistem operasi mempertahankan integritas aplikasi MFC lain yang berbagi DLL.

Demikian pula, status MFC "global" lainnya seperti file yang dapat dieksekusi saat ini untuk memuat sumber daya, juga dibagikan antara aplikasi klien, semua DLL ekstensi MFC, dan MFCxx.DLL itu sendiri.

Membangun DLL ekstensi MFC

Anda dapat menggunakan AppWizard untuk membuat proyek DLL ekstensi MFC, dan secara otomatis menghasilkan pengaturan pengkompilasi dan pengtaut yang sesuai. Ini juga menghasilkan DllMain fungsi yang dapat Anda ubah.

Jika Anda mengonversi proyek yang ada ke DLL ekstensi MFC, mulailah dengan pengaturan standar yang dibuat dengan menggunakan versi bersama MFC. Kemudian, lakukan perubahan berikut:

  • Tambahkan /D_AFXEXT ke bendera pengkompilasi. Pada dialog Properti Proyek, pilih kategori C/C++>Preprocessor. Tambahkan _AFXEXT ke bidang Tentukan Makro , yang memisahkan setiap item dengan titik koma.

  • Hapus sakelar /Gy pengkompilasi. Pada dialog Properti Proyek, pilih kategori C/C++>Code Generation. Pastikan properti Aktifkan Penautan Tingkat Fungsi tidak diaktifkan. Pengaturan ini memudahkan untuk mengekspor kelas, karena linker tidak akan menghapus fungsi yang tidak direferensikan. Jika proyek asli membangun DLL MFC reguler yang secara statis ditautkan ke MFC, ubah /MT opsi pengkompilasi (atau /MTd) menjadi /MD (atau /MDd).

  • Buat pustaka ekspor dengan /DLL opsi ke LINK. Opsi ini akan diatur saat Anda membuat target baru dan menentukan Pustaka Win32 Dynamic-Link sebagai jenis target.

Mengubah File Header Anda

Tujuan biasa dari DLL ekstensi MFC adalah untuk mengekspor beberapa fungsionalitas umum ke satu atau beberapa aplikasi yang dapat menggunakan fungsionalitas tersebut. Pada dasarnya, DLL mengekspor kelas dan fungsi global untuk digunakan oleh aplikasi klien Anda.

Untuk memastikan bahwa setiap fungsi anggota ditandai untuk impor atau ekspor sebagaimana mewajibkan, gunakan deklarasi __declspec(dllexport) khusus dan __declspec(dllimport). Ketika aplikasi klien menggunakan kelas Anda, Anda ingin mereka dinyatakan sebagai __declspec(dllimport). Ketika DLL ekstensi MFC itu sendiri dibangun, fungsi harus dinyatakan sebagai __declspec(dllexport). DLL yang dibangun juga harus mengekspor fungsi, sehingga program klien dapat mengikatnya pada waktu pemuatan.

Untuk mengekspor seluruh kelas Anda, gunakan AFX_EXT_CLASS dalam definisi kelas. Kerangka kerja mendefinisikan makro ini sebagai __declspec(dllexport) kapan _AFXDLL dan _AFXEXT didefinisikan, tetapi mendefinisikannya sebagai __declspec(dllimport) kapan _AFXEXT tidak ditentukan. _AFXEXT hanya ditentukan saat membangun DLL ekstensi MFC Anda. Contohnya:

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

Tidak Mengekspor Seluruh Kelas

Terkadang Anda mungkin ingin mengekspor hanya anggota masing-masing yang diperlukan dari kelas Anda. Misalnya, jika Anda mengekspor CDialogkelas -turunan, Anda mungkin hanya perlu mengekspor konstruktor dan DoModal panggilan. Anda dapat mengekspor anggota ini menggunakan file DEF DLL, tetapi Anda juga dapat menggunakan AFX_EXT_CLASS dengan cara yang sama pada masing-masing anggota yang perlu Anda ekspor.

Contohnya:

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

Saat melakukannya, Anda mungkin mengalami masalah tambahan karena Anda tidak mengekspor semua anggota kelas. Masalahnya adalah cara kerja makro MFC. Beberapa makro pembantu MFC benar-benar mendeklarasikan atau menentukan anggota data. DLL Anda juga perlu mengekspor anggota data ini.

Misalnya, makro DECLARE_DYNAMIC didefinisikan sebagai berikut saat membangun DLL ekstensi MFC:

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

Garis yang dimulai static AFX_DATA mendeklarasikan objek statis di dalam kelas Anda. Untuk mengekspor kelas ini dengan benar dan mengakses informasi runtime dari EXE klien, Anda perlu mengekspor objek statis ini. Karena objek statis dideklarasikan dengan pengubah AFX_DATA, Anda hanya perlu menentukan AFX_DATA saat __declspec(dllexport) Anda membangun DLL Anda. Tentukan sebagai __declspec(dllimport) saat Anda membangun klien Anda yang dapat dieksekusi.

Seperti yang dibahas di atas, AFX_EXT_CLASS sudah didefinisikan dengan cara ini. Anda hanya perlu mendefinisikan AFX_DATA ulang agar sama seperti AFX_EXT_CLASS sekeliling definisi kelas Anda.

Contohnya:

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

MFC selalu menggunakan AFX_DATA simbol pada item data yang ditentukan dalam makronya, sehingga teknik ini akan berfungsi untuk semua skenario tersebut. Misalnya, ini akan berfungsi untuk DECLARE_MESSAGE_MAP.

Catatan

Jika Anda mengekspor seluruh kelas daripada anggota kelas yang dipilih, anggota data statis akan diekspor secara otomatis.

Anda dapat menggunakan teknik yang sama untuk mengekspor CArchive operator ekstraksi secara otomatis untuk kelas yang menggunakan makro DECLARE_SERIAL dan IMPLEMENT_SERIAL. Ekspor operator arsip dengan membungkus deklarasi kelas (terletak di file header) dengan kode berikut:

#undef AFX_API
#define AFX_API AFX_EXT_CLASS

/* your class declarations here */

#undef AFX_API
#define AFX_API

Batasan _AFXEXT

Anda dapat menggunakan _AFXEXT simbol pra-prosesor untuk DLL ekstensi MFC Anda selama Anda tidak memiliki beberapa lapisan DLL ekstensi MFC. Jika Anda memiliki DLL ekstensi MFC yang memanggil atau berasal dari kelas di DLL ekstensi MFC Anda sendiri, yang kemudian berasal dari kelas MFC, Anda harus menggunakan simbol praprosesor Anda sendiri untuk menghindari ambiguitas.

Masalahnya adalah bahwa di Win32, Anda harus secara eksplisit menyatakan data __declspec(dllexport) apa pun untuk mengekspornya dari DLL, dan __declspec(dllimport) untuk mengimpornya dari DLL. Saat Anda menentukan _AFXEXT, header MFC memastikan bahwa AFX_EXT_CLASS didefinisikan dengan benar.

Ketika Anda memiliki beberapa lapisan, satu simbol seperti AFX_EXT_CLASS tidak cukup: DLL ekstensi MFC dapat mengekspor kelasnya sendiri, dan juga mengimpor kelas lain dari DLL ekstensi MFC lainnya. Untuk menangani masalah ini, gunakan simbol prapemrosan khusus yang menunjukkan bahwa Anda membangun DLL itu sendiri, alih-alih menggunakan DLL. Misalnya, bayangkan dua DLL ekstensi MFC, , A.DLLdan B.DLL. Mereka masing-masing mengekspor beberapa kelas di A.H dan B.H, masing-masing. B.DLL menggunakan kelas dari A.DLL. File header akan terlihat seperti ini:

/* 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 ... */ };

Ketika A.DLL dibangun, dibangun dengan /DA_IMPL dan kapan B.DLL dibangun, dibangun dengan /DB_IMPL. Dengan menggunakan simbol terpisah untuk setiap DLL, CExampleB diekspor dan CExampleA diimpor saat membangun B.DLL. CExampleA diekspor saat membangun A.DLL dan mengimpor saat digunakan oleh B.DLL atau beberapa klien lain.

Jenis lapisan ini tidak dapat dilakukan saat menggunakan simbol bawaan AFX_EXT_CLASS dan _AFXEXT praprosesor. Teknik yang dijelaskan di atas memecahkan masalah ini dengan cara yang sama seperti yang dilakukan MFC. MFC menggunakan teknik ini saat membangun DLL ekstensi OLE, Database, dan Network MFC- nya.

Masih Belum Mengekspor Seluruh Kelas

Sekali lagi, Anda harus berhati-hati ketika Anda tidak mengekspor seluruh kelas. Pastikan bahwa item data yang diperlukan yang dibuat oleh makro MFC diekspor dengan benar. Anda dapat melakukannya dengan mendefinisikan AFX_DATA ulang ke makro kelas tertentu Anda. Tentukan ulang setiap kali Anda tidak mengekspor seluruh kelas.

Contohnya:

// 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

Berikut adalah kode yang harus Anda tempatkan di file sumber utama untuk DLL ekstensi MFC Anda. Itu harus datang setelah standar termasuk. Saat Anda menggunakan AppWizard untuk membuat file pemula untuk DLL ekstensi MFC, appWizard menyediakan DllMain untuk Anda.

#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
}

Panggilan untuk AfxInitExtensionModule mengambil kelas runtime modul (CRuntimeClass struktur) dan pabrik objeknya (COleObjectFactory objek) untuk digunakan nanti saat CDynLinkLibrary objek dibuat. Panggilan (opsional) untuk AfxTermExtensionModule memungkinkan MFC membersihkan DLL ekstensi MFC ketika setiap proses terlepas (yang terjadi ketika proses keluar, atau ketika DLL dibongkar oleh FreeLibrary panggilan) dari DLL ekstensi MFC. Karena sebagian besar DLL ekstensi MFC tidak dimuat secara dinamis (biasanya, MEREKA ditautkan melalui pustaka impor mereka), panggilan ke AfxTermExtensionModule biasanya tidak diperlukan.

Jika aplikasi Anda memuat dan membebaskan DLL ekstensi MFC secara dinamis, pastikan untuk memanggil AfxTermExtensionModule seperti yang ditunjukkan di atas. Pastikan juga untuk menggunakan AfxLoadLibrary dan AfxFreeLibrary (alih-alih fungsi LoadLibrary Win32 dan FreeLibrary) jika aplikasi Anda menggunakan beberapa utas atau jika secara dinamis memuat DLL ekstensi MFC. Menggunakan AfxLoadLibrary dan AfxFreeLibrary memastikan bahwa kode startup dan shutdown yang dijalankan ketika DLL ekstensi MFC dimuat dan dibongkar tidak merusak status MFC global.

File AFXDLLX.H header berisi definisi khusus untuk struktur yang digunakan dalam DLL ekstensi MFC, seperti definisi untuk AFX_EXTENSION_MODULE dan CDynLinkLibrary.

ExtensionDLL global harus dinyatakan seperti yang ditunjukkan. Tidak seperti MFC versi 16-bit, Anda dapat mengalokasikan memori dan memanggil fungsi MFC selama waktu ini, karena MFCxx.DLL sepenuhnya diinisialisasi pada saat Anda DllMain dipanggil.

Berbagi Sumber Daya dan Kelas

DLL ekstensi MFC sederhana hanya perlu mengekspor beberapa fungsi bandwidth rendah ke aplikasi klien dan tidak lebih. LEBIH banyak DLL intensif antarmuka pengguna mungkin ingin mengekspor sumber daya dan kelas C++ ke aplikasi klien.

Mengekspor sumber daya dilakukan melalui daftar sumber daya. Di setiap aplikasi adalah daftar objek yang ditautkan dengan nyanyian CDynLinkLibrary . Saat mencari sumber daya, sebagian besar implementasi MFC standar yang memuat sumber daya terlihat terlebih dahulu pada modul sumber daya saat ini (AfxGetResourceHandle) dan jika tidak ditemukan, jalankan daftar CDynLinkLibrary objek yang mencoba memuat sumber daya yang diminta.

Pembuatan dinamis objek C++ yang diberi nama kelas C++ serupa. Mekanisme deserialisasi objek MFC harus memiliki semua CRuntimeClass objek yang terdaftar sehingga dapat membangun kembali dengan membuat objek C++ secara dinamis dari jenis yang diperlukan berdasarkan apa yang disimpan sebelumnya.

Jika Anda ingin aplikasi klien menggunakan kelas di DLL ekstensi MFC Anda yaitu DECLARE_SERIAL, maka Anda harus mengekspor kelas Anda untuk membuatnya terlihat oleh aplikasi klien. Ini juga dilakukan dengan berjalan daftar CDynLinkLibrary .

Dalam sampel DLLHUSKKonsep Lanjutan MFC , daftar terlihat seperti:

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

Entri MFCxx.DLL biasanya muncul terakhir pada daftar sumber daya dan kelas. MFCxx.DLL mencakup semua sumber daya MFC standar, termasuk string prompt untuk semua ID perintah standar. Menempatkannya di akhir daftar memungkinkan DLL dan aplikasi klien itu sendiri untuk mengandalkan sumber daya bersama di MFCxx.DLL, alih-alih memiliki salinan mereka sendiri.

Menggabungkan sumber daya dan nama kelas semua DLL ke dalam ruang nama aplikasi klien memiliki kerugian yang Anda miliki untuk berhati-hati id atau nama apa yang Anda pilih. Anda dapat menonaktifkan fitur ini dengan tidak mengekspor sumber daya atau CDynLinkLibrary objek Anda ke aplikasi klien. Sampel DLLHUSK mengelola ruang nama sumber daya bersama dengan menggunakan beberapa file header. Lihat Catatan Teknis 35 untuk tips selengkapnya tentang menggunakan file sumber daya bersama.

Menginisialisasi DLL

Seperti disebutkan di atas, Anda biasanya ingin membuat CDynLinkLibrary objek untuk mengekspor sumber daya dan kelas Anda ke aplikasi klien. Anda harus menyediakan titik masuk yang diekspor untuk menginisialisasi DLL. Minimal, ini adalah void rutinitas yang tidak mengambil argumen dan tidak mengembalikan apa pun, tetapi bisa menjadi apa pun yang Anda suka.

Setiap aplikasi klien yang ingin menggunakan DLL Anda harus memanggil rutinitas inisialisasi ini, jika Anda menggunakan pendekatan ini. Anda juga dapat mengalokasikan objek ini CDynLinkLibrary di tepat DllMain setelah Anda memanggil AfxInitExtensionModule.

Rutinitas inisialisasi harus membuat CDynLinkLibrary objek di tumpukan aplikasi saat ini, yang terhubung ke informasi DLL ekstensi MFC Anda. Anda dapat melakukannya dengan menentukan fungsi seperti ini:

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

Nama rutin, InitXxxDLL dalam contoh ini, bisa menjadi apa pun yang Anda inginkan. Tidak perlu extern "C", tetapi membuat daftar ekspor lebih mudah dipertahankan.

Catatan

Jika Anda menggunakan DLL ekstensi MFC dari DLL MFC reguler, Anda harus mengekspor fungsi inisialisasi ini. Fungsi ini harus dipanggil dari DLL MFC reguler sebelum menggunakan kelas atau sumber daya DLL ekstensi MFC apa pun.

Mengekspor Entri

Cara sederhana untuk mengekspor kelas Anda adalah dengan menggunakan __declspec(dllimport) dan __declspec(dllexport) pada setiap kelas dan fungsi global yang ingin Anda ekspor. Ini jauh lebih mudah, tetapi kurang efisien daripada menamai setiap titik masuk dalam file DEF seperti yang dijelaskan di bawah ini. Itu karena Anda memiliki lebih sedikit kontrol atas fungsi apa yang diekspor. Dan, Anda tidak dapat mengekspor fungsi berdasarkan ordinal. TESTDLL1 dan TESTDLL2 menggunakan metode ini untuk mengekspor entri mereka.

Metode yang lebih efisien adalah mengekspor setiap entri dengan menamainya dalam file DEF. Metode ini digunakan oleh MFCxx.DLL. Karena kami mengekspor secara selektif dari DLL kami, kami harus memutuskan antarmuka tertentu mana yang ingin kami ekspor. Sulit, karena Anda harus menentukan nama mangled ke linker dalam bentuk entri dalam file DEF. Jangan ekspor kelas C++ apa pun kecuali Anda benar-benar perlu memiliki tautan simbolis untuk kelas tersebut.

Jika Anda telah mencoba mengekspor kelas C++ dengan file DEF sebelumnya, Anda mungkin ingin mengembangkan alat untuk menghasilkan daftar ini secara otomatis. Ini dapat dilakukan dengan menggunakan proses tautan dua tahap. Tautkan DLL Anda sekali tanpa ekspor, dan izinkan linker untuk membuat file MAP. File MAP berisi daftar fungsi yang harus diekspor. Dengan beberapa pengaturan ulang, Anda dapat menggunakannya untuk menghasilkan entri EKSPOR untuk file DEF Anda. Daftar ekspor untuk MFCxx.DLL dan DLL ekstensi OLE dan Database MFC, beberapa ribu dalam jumlah, dihasilkan dengan proses seperti itu (meskipun tidak sepenuhnya otomatis, dan memerlukan penyetelan tangan setiap sekali-sekali).

CWinApp vs. CDynLinkLibrary

DLL ekstensi MFC tidak memiliki CWinAppobjek -turunannya sendiri. Sebaliknya, ini harus bekerja dengan CWinAppobjek -turunan dari aplikasi klien. Ini berarti bahwa aplikasi klien memiliki pompa pesan utama, perulangan diam, dan sebagainya.

Jika DLL ekstensi MFC Anda perlu mempertahankan data tambahan untuk setiap aplikasi, Anda dapat memperoleh kelas baru dari CDynLinkLibrary dan membuatnya dalam rutinitas yang InitXxxDLL dijelaskan di atas. Saat berjalan, DLL dapat memeriksa daftar CDynLinkLibrary objek aplikasi saat ini untuk menemukan yang untuk DLL ekstensi MFC tertentu.

Menggunakan Sumber Daya dalam Implementasi DLL Anda

Seperti disebutkan di atas, beban sumber daya default akan memandu CDynLinkLibrary daftar objek yang mencari EXE atau DLL pertama yang memiliki sumber daya yang diminta. Semua API MFC dan semua kode internal menggunakan untuk memandu AfxFindResourceHandle daftar sumber daya untuk menemukan sumber daya apa pun, di mana pun lokasinya.

Jika Anda hanya ingin memuat sumber daya dari tempat tertentu, gunakan API AfxGetResourceHandle dan AfxSetResourceHandle untuk menyimpan handel lama dan mengatur handel baru. Pastikan untuk memulihkan handel sumber daya lama sebelum Anda kembali ke aplikasi klien. Sampel TESTDLL2 menggunakan pendekatan ini untuk memuat menu secara eksplisit.

Berjalan dalam daftar memiliki beberapa kelemahan: sedikit lebih lambat, dan memerlukan pengelolaan rentang ID sumber daya. Ini memiliki keuntungan bahwa aplikasi klien yang menautkan ke beberapa DLL ekstensi MFC dapat menggunakan sumber daya yang disediakan DLL tanpa harus menentukan handel instans DLL. AfxFindResourceHandle adalah API yang digunakan untuk berjalan di daftar sumber daya untuk mencari kecocokan tertentu. Dibutuhkan nama dan jenis sumber daya, dan mengembalikan handel sumber daya tempat pertama kali menemukan sumber daya, atau NULL.

Menulis Aplikasi yang Menggunakan Versi DLL

Persyaratan Aplikasi

Aplikasi yang menggunakan MFC versi bersama harus mengikuti beberapa aturan dasar:

  • Ini harus memiliki CWinApp objek dan mengikuti aturan standar untuk pompa pesan.

  • Ini harus dikompilasi dengan sekumpulan bendera pengkompilasi yang diperlukan (lihat di bawah).

  • Ini harus ditautkan dengan pustaka impor MFCxx. Dengan mengatur bendera kompilator yang diperlukan, header MFC menentukan pada waktu tautan pustaka mana yang harus ditautkan dengan aplikasi.

  • Untuk menjalankan executable, MFCxx.DLL harus berada di jalur atau di direktori sistem Windows.

Membangun dengan Lingkungan Pengembangan

Jika Anda menggunakan makefile internal dengan sebagian besar default standar, Anda dapat dengan mudah mengubah proyek untuk membangun versi DLL.

Langkah berikut mengasumsikan Anda memiliki aplikasi MFC yang berfungsi dengan benar yang ditautkan dengan NAFXCWD.LIB (untuk debug) dan NAFXCW.LIB (untuk rilis) dan Anda ingin mengonversinya untuk menggunakan versi bersama pustaka MFC. Anda menjalankan lingkungan Visual Studio dan memiliki file proyek internal.

  1. Pada menu Proyek , pilih Properti. Di halaman Umum di bawah Default Proyek, atur Kelas Microsoft Foundation untuk Menggunakan MFC di DLL Bersama (MFCxx(d).dll).

Membangun dengan NMAKE

Jika Anda menggunakan fitur makefile eksternal pengkompilasi, atau menggunakan NMAKE secara langsung, Anda harus mengedit makefile untuk mendukung opsi pengkompilasi dan linker yang diperlukan.

Bendera kompilator yang diperlukan:

  • /D_AFXDLL /MD /D_AFXDLL

Header MFC standar memerlukan _AFXDLL simbol untuk didefinisikan.

  • /MD Aplikasi harus menggunakan versi DLL dari pustaka run-time C.

Semua bendera pengkompilasi lainnya mengikuti default MFC (misalnya, _DEBUG untuk debug).

Edit daftar pustaka linker. Ubah NAFXCWD.LIB ke MFCxxD.LIB dan ubah NAFXCW.LIB menjadi MFCxx.LIB. Ganti LIBC.LIB dengan MSVCRT.LIB. Seperti halnya pustaka MFC lainnya, penting untuk MFCxxD.LIB ditempatkan sebelum pustaka runtime C apa pun.

Secara opsional tambahkan /D_AFXDLL ke opsi kompilator sumber daya rilis dan debug Anda (opsi yang benar-benar mengkompilasi sumber daya dengan /R). Opsi ini membuat eksekusi akhir Anda lebih kecil dengan berbagi sumber daya yang ada di DLL MFC.

Pembangunan ulang penuh diperlukan setelah perubahan ini dibuat.

Membangun Sampel

Sebagian besar program sampel MFC dapat dibangun dari Visual C++ atau dari MAKEFILE bersama yang kompatibel dengan NMAKE dari baris perintah.

Untuk mengonversi salah satu sampel ini untuk digunakan MFCxx.DLL, Anda dapat memuat file MAK ke Visual C++ dan mengatur opsi Proyek seperti yang dijelaskan di atas. Jika Anda menggunakan build NMAKE, Anda dapat menentukan AFXDLL=1 pada baris perintah NMAKE dan yang akan membangun sampel menggunakan pustaka MFC bersama.

Sampel Konsep Lanjutan MFC DLLHUSK dibangun dengan MFC versi DLL. Sampel ini tidak hanya menggambarkan cara membuat aplikasi yang ditautkan dengan MFCxx.DLL, tetapi juga menggambarkan fitur lain dari opsi pengemasan DLL MFC seperti DLL ekstensi MFC yang dijelaskan nanti dalam catatan teknis ini.

Catatan Kemasan

Versi rilis DLL (MFCxx.DLL dan MFCxxU.DLL) dapat didistribusikan ulang secara bebas. Versi debug DLL tidak dapat didistribusikan ulang secara bebas dan harus digunakan hanya selama pengembangan aplikasi Anda.

DLL debug disediakan dengan informasi penelusuran kesalahan. Dengan menggunakan debugger Visual C++, Anda dapat melacak eksekusi aplikasi dan DLL Anda. DLL Rilis (MFCxx.DLL dan MFCxxU.DLL) tidak berisi informasi penelusuran kesalahan.

Jika Anda menyesuaikan atau membangun kembali DLL, maka Anda harus memanggilnya sesuatu selain "MFCxx". File SRC MFCDLL.MAK MFC menjelaskan opsi build dan berisi logika untuk mengganti nama DLL. Mengganti nama file diperlukan, karena DLL ini berpotensi dibagikan oleh banyak aplikasi MFC. Memiliki versi kustom DLL MFC Anda menggantikan yang terinstal pada sistem dapat merusak aplikasi MFC lain menggunakan DLL MFC bersama.

Membangun kembali DLL MFC tidak disarankan.

Bagaimana MFCxx.DLL Diimplementasikan

Bagian berikut menjelaskan bagaimana DLL MFC (MFCxx.DLL dan MFCxxD.DLL) diimplementasikan. Memahami detail di sini juga tidak penting jika yang ingin Anda lakukan adalah menggunakan DLL MFC dengan aplikasi Anda. Detail di sini tidak penting untuk memahami cara menulis DLL ekstensi MFC, tetapi memahami implementasi ini dapat membantu Anda menulis DLL Anda sendiri.

Gambaran Umum Implementasi

DLL MFC benar-benar kasus khusus dari DLL ekstensi MFC seperti yang dijelaskan di atas. Ini memiliki sejumlah besar ekspor untuk sejumlah besar kelas. Ada beberapa hal tambahan yang kami lakukan di DLL MFC yang membuatnya lebih istimewa daripada DLL ekstensi MFC biasa.

Win32 Melakukan Sebagian Besar Pekerjaan

MFC versi 16-bit membutuhkan sejumlah teknik khusus termasuk data per aplikasi pada segmen tumpukan, segmen khusus yang dibuat oleh beberapa kode rakitan 80x86, konteks pengecualian per proses, dan teknik lainnya. Win32 secara langsung mendukung data per proses dalam DLL, yang merupakan apa yang Anda inginkan sebagian besar waktu. Untuk sebagian MFCxx.DLL besar hanya NAFXCW.LIB dibungkus dalam DLL. Jika Anda melihat kode sumber MFC, Anda akan menemukan beberapa #ifdef _AFXDLL kasus, karena tidak ada banyak kasus khusus yang perlu dibuat. Kasus khusus yang ada secara khusus untuk menangani Win32 di Windows 3.1 (atau dikenal sebagai Win32s). Win32s tidak mendukung data DLL per proses secara langsung. DLL MFC harus menggunakan API Win32 penyimpanan lokal utas (TLS) untuk mendapatkan data lokal proses.

Dampak pada Sumber Pustaka, File Tambahan

Dampak _AFXDLL versi pada sumber dan header pustaka kelas MFC normal relatif kecil. Ada file versi khusus (AFXV_DLL.H) dan file header tambahan (AFXDLL_.H) yang disertakan oleh header utama AFXWIN.H . Header AFXDLL_.H mencakup CDynLinkLibrary kelas dan detail implementasi lainnya dari _AFXDLL APLIKASI dan DLL ekstensi MFC. Header AFXDLLX.H disediakan untuk membangun DLL ekstensi MFC (lihat di atas untuk detailnya).

Sumber reguler ke pustaka MFC di MFC SRC memiliki beberapa kode kondisional tambahan di _AFXDLL bawah #ifdef. File sumber tambahan (DLLINIT.CPP) berisi kode inisialisasi DLL tambahan dan lem lainnya untuk versi bersama MFC.

Untuk membangun versi bersama MFC, file tambahan disediakan. (Lihat di bawah ini untuk detail tentang cara membangun DLL.)

  • Dua file DEF digunakan untuk mengekspor titik masuk DLL MFC untuk versi DLL debug (MFCxxD.DEF) dan rilis (MFCxx.DEF).

  • File RC (MFCDLL.RC) berisi semua sumber daya MFC standar dan VERSIONINFO sumber daya untuk DLL.

  • File CLW (MFCDLL.CLW) disediakan untuk memungkinkan penjelajahan kelas MFC menggunakan ClassWizard. Fitur ini tidak khusus untuk MFC versi DLL.

Manajemen Memori

Aplikasi yang menggunakan MFCxx.DLL alokator memori umum yang disediakan oleh MSVCRTxx.DLL, DLL runtime C bersama. Aplikasi, DLL ekstensi MFC apa pun, dan serta DLL MFC menggunakan alokator memori bersama ini. Dengan menggunakan DLL bersama untuk alokasi memori, DLL MFC dapat mengalokasikan memori yang kemudian dibebaskan oleh aplikasi atau sebaliknya. Karena aplikasi dan DLL harus menggunakan alokator yang sama, Anda tidak boleh mengambil alih C++ global operator new atau operator delete. Aturan yang sama berlaku untuk sisa rutinitas alokasi memori run-time C (seperti malloc, , reallocfree, dan lainnya).

Ordinal dan __declspec kelas (dllexport) dan penamaan DLL

Kami tidak menggunakan class__declspec(dllexport) fungsionalitas pengkompilasi C++. Sebagai gantinya, daftar ekspor disertakan dengan sumber pustaka kelas (MFCxx.DEF dan MFCxxD.DEF). Hanya sekumpulan titik masuk tertentu (fungsi dan data) yang diekspor. Simbol lain, seperti fungsi atau kelas implementasi privat MFC, tidak diekspor. Semua ekspor dilakukan secara ordinal tanpa nama string di tabel nama penduduk atau non-penduduk.

Menggunakan class__declspec(dllexport) mungkin alternatif yang layak untuk membangun DLL yang lebih kecil, tetapi dalam DLL besar seperti MFC, mekanisme ekspor default memiliki efisiensi dan batas kapasitas.

Apa artinya adalah bahwa kita dapat mengemas sejumlah besar fungsionalitas dalam rilis MFCxx.DLL yang hanya sekitar 800 KB tanpa mengorbankan banyak eksekusi atau kecepatan pemuatan. MFCxx.DLL akan menjadi 100 KB lebih besar jika teknik ini tidak digunakan. Teknik ini memungkinkan untuk menambahkan titik masuk tambahan di akhir file DEF. Ini memungkinkan penerapan versi sederhana tanpa mengorbankan kecepatan dan efisiensi ukuran ekspor dengan ordinal. Revisi versi utama di pustaka kelas MFC akan mengubah nama pustaka. Artinya, MFC30.DLL adalah DLL yang dapat didistribusikan ulang yang berisi versi 3.0 dari pustaka kelas MFC. Peningkatan DLL ini, misalnya, dalam MFC hipotetis 3.1, DLL akan diberi nama MFC31.DLL sebagai gantinya. Sekali lagi, jika Anda memodifikasi kode sumber MFC untuk menghasilkan versi kustom DLL MFC, gunakan nama yang berbeda (dan sebaiknya satu tanpa "MFC" dalam nama).

Baca juga

Catatan Teknis menurut Angka
Catatan Teknis menurut Kategori