TN033. Версия библиотеки DLL MFC
В этом примечании описывается, как использовать MFCxx.DLL
MFCxxD.DLL
библиотеки динамических ссылок ( где xx — номер версии MFC) с приложениями MFC и БИБЛИОТЕКАми DLL расширений MFC. Дополнительные сведения о обычных библиотеках DLL MFC см. в разделе "Использование MFC в составе библиотеки DLL".
Эта техническая заметка охватывает три аспекта библиотек DLL. Последние два — для более продвинутых пользователей:
Если вы заинтересованы в создании библиотеки DLL с помощью MFC, которая может использоваться с приложениями, отличными от MFC (обычной библиотекой DLL MFC), обратитесь к Техническому примечание 11.
Обзор поддержки MFCxx.DLL: терминология и файлы
Обычная библиотека DLL MFC: вы используете обычную библиотеку DLL MFC для создания автономной библиотеки DLL с помощью некоторых классов MFC. Интерфейсы через границу App/DLL — это интерфейсы C, а клиентское приложение не должно быть приложением MFC.
Обычные библиотеки DLL MFC — это версия библиотек DLL, поддерживаемых в MFC 1.0. Они описаны в техническом примечание 11 и образце DLLScreenCap
расширенных концепций MFC.
Примечание.
По состоянию на Visual C++ версии 4.0 термин USRDLL устарел и заменен обычным библиотекой DLL MFC, которая статически связывается с MFC. Вы также можете создать обычную библиотеку DLL MFC, которая динамически связывается с MFC.
MFC 3.0 (и выше) поддерживает обычные библиотеки DLL MFC со всеми новыми функциями, включая классы OLE и Database.
AFXDLL: также называется общей версией библиотек MFC. Это новая поддержка DLL, добавленная в MFC 2.0. Сама библиотека MFC находится в ряде БИБЛИОТЕК DLL (описано ниже). Клиентское приложение или библиотека DLL динамически связывает необходимые библиотеки DLL. Интерфейсы через границу приложения или библиотеки DLL — это интерфейсы классов C++/MFC. Клиентское приложение ДОЛЖНО быть приложением MFC. Эта библиотека DLL поддерживает все функциональные возможности MFC 3.0 (исключение: ЮНИКОД не поддерживается для классов базы данных).
Примечание.
По состоянию на Visual C++ версии 4.0 этот тип библиотеки DLL называется библиотекой DLL расширения.
Это примечание будет использоваться MFCxx.DLL
для ссылки на весь набор DLL MFC, который включает в себя:
Отладка:
MFCxxD.DLL
(объединенная) иMFCSxxD.LIB
(статичная).Выпуск:
MFCxx.DLL
(объединенный) иMFCSxx.LIB
(статический).Отладка Юникода:
MFCxxUD.DLL
(объединенная) иMFCSxxD.LIB
(статичная).Выпуск Юникода:
MFCxxU.DLL
(объединенный) иMFCSxxU.LIB
(статический).
Примечание.
Библиотеки MFCSxx[U][D].LIB
используются в сочетании с общими библиотеками DLL MFC. Эти библиотеки содержат код, который должен быть статически связан с приложением или библиотекой DLL.
Приложение ссылается на соответствующие библиотеки импорта:
Отладки:
MFCxxD.LIB
Выпуска:
MFCxx.LIB
Отладка Юникода:
MFCxxUD.LIB
Выпуск Юникода:
MFCxxU.LIB
Библиотека DLL расширения MFC — это библиотека DLL, которая расширяет MFCxx.DLL
(или другие общие библиотеки DLL MFC). Здесь архитектура компонента MFC начинается. Если вы наследуете полезный класс из класса MFC или создадите другой набор средств, похожий на MFC, его можно поместить в библиотеку DLL. Библиотека DLL использует MFCxx.DLL
, как и конечное клиентское приложение. Библиотека DLL расширения MFC позволяет повторно использовать конечные классы, повторно использовать базовые классы, а также повторно использовать классы представлений и документов.
Плюсы и минусы
Почему следует использовать общую версию MFC
Использование общей библиотеки может привести к более мелким приложениям. (Минимальное приложение, использующее большую часть библиотеки MFC, меньше 10 КБ).
Общая версия MFC поддерживает библиотеки DLL расширения MFC и обычные библиотеки DLL MFC.
Быстрее создавать приложение, использующее общие библиотеки MFC, чем статическое связанное приложение MFC. Это связано с тем, что не требуется связать сам MFC. Это особенно верно в
DEBUG
сборках, где компоновщик должен сжимать данные отладки. Когда приложение связывается с библиотекой DLL, которая уже содержит сведения об отладке, для сжатия меньше отладочной информации.
Почему не следует использовать общую версию MFC:
- Для доставки приложения, использующего общую библиотеку, необходимо отправить
MFCxx.DLL
и другие библиотеки с помощью программы.MFCxx.DLL
является свободно распространяемым, как и многие библиотеки DLL, но вы по-прежнему должны установить библиотеку DLL в программе УСТАНОВКИ. Кроме того, вам придется отправлять другие распространяемые библиотеки, используемые как программой, так и библиотеки DLL MFC.
Создание библиотеки DLL расширения MFC
Библиотека DLL расширения MFC — это библиотека DLL, содержащая классы и функции для расширения функциональных возможностей классов MFC. Библиотека DLL расширения MFC использует общие библиотеки DLL MFC таким же образом, как и приложение, с несколькими дополнительными рекомендациями.
Процесс сборки аналогичен созданию приложения, использующего общие библиотеки MFC с несколькими дополнительными параметрами компилятора и компоновщика.
Библиотека DLL расширения MFC не имеет производный
CWinApp
класс.Библиотека DLL расширения MFC должна предоставлять специальную
DllMain
библиотеку. AppWizard предоставляет функциюDllMain
, которую можно изменить.Библиотека DLL расширения MFC обычно предоставляет подпрограмму инициализации для создания
CDynLinkLibrary
библиотеки DLL расширения MFC, экспортируетCRuntimeClass
типы или ресурсы в приложение. ПроизводныйCDynLinkLibrary
класс может использоваться, если данные для каждого приложения должны поддерживаться библиотекой DLL расширения MFC.
Эти рекомендации подробно описаны ниже. Также см. пример DLLHUSK
расширенных концепций MFC. В примере показано:
Создайте приложение с помощью общих библиотек. (
DLLHUSK.EXE
— это приложение MFC, которое динамически связывается с библиотеками MFC и другими библиотеками DLL.)Создание библиотеки DLL расширения MFC. (В нем показано, как специальные флаги, такие как
_AFXEXT
использование при создании библиотеки DLL расширения MFC.)Создайте два примера библиотек DLL расширения MFC. В одной из них показана базовая структура библиотеки DLL расширения MFC с ограниченными экспортами (TESTDLL1), а другая — экспорт всего интерфейса класса (TESTDLL2).
Как клиентское приложение, так и все библиотеки MFCxx.DLL
DLL расширения MFC должны использовать одну и ту же версию. Следуйте соглашениям библиотек DLL MFC и предоставьте отладочную и выпускную/release
() версию библиотеки DLL расширения MFC. Эта практика позволяет клиентским программам создавать версии отладки и выпуска приложений и связывать их с соответствующей версией отладки или выпуска всех библиотек DLL.
Примечание.
Так как проблемы с изменением и экспортом имен C++ список экспорта из библиотеки DLL расширения MFC может отличаться от версий отладки и выпуска одной библиотеки DLL и DLL для разных платформ. MFCxx.DLL
Выпуск имеет около 2000 экспортированных точек входа. Отладка MFCxxD.DLL
имеет около 3000 экспортированных точек входа.
Краткое примечание по управлению памятью
В разделе "Управление памятью" в конце этой технической заметки описывается реализация MFCxx.DLL
с общей версией MFC. Сведения, необходимые для реализации только библиотеки DLL расширения MFC, описаны здесь.
MFCxx.DLL
и все библиотеки DLL расширения MFC, загруженные в адресное пространство клиентского приложения, будут использовать тот же распределитель памяти, загрузку ресурсов и другие состояния MFC "global", как если бы они находились в одном приложении. Это важно, так как библиотеки DLL, отличные от MFC, и обычные библиотеки DLL MFC, которые статически связываются с MFC, делают точно противоположное: каждая библиотека DLL выделяет из собственного пула памяти.
Если библиотека DLL расширения MFC выделяет память, то эта память может свободно взаимодействовать с любым другим выделенным приложением объектом. Кроме того, если приложение, использующее общие библиотеки MFC, завершает работу, операционная система сохраняет целостность любого другого приложения MFC, которое использует библиотеку DLL.
Аналогичным образом, другие "глобальные" состояния MFC, такие как текущий исполняемый файл для загрузки ресурсов, также получают общий доступ между клиентским приложением, всеми библиотеками DLL расширения MFC и MFCxx.DLL
самой собой.
Создание библиотеки DLL расширения MFC
С помощью AppWizard можно создать проект библиотеки DLL расширения MFC, который автоматически создает соответствующие параметры компилятора и компоновщика. Он также создает функцию DllMain
, которую можно изменить.
Если вы преобразуете существующий проект в библиотеку DLL расширения MFC, начните со стандартных параметров, которые создаются с помощью общей версии MFC. Внесите в нее следующие изменения:
Добавьте
/D_AFXEXT
в флаги компилятора. В диалоговом окне "Свойства проекта" выберите категорию препроцессора C/C++>. Добавьте_AFXEXT
в поле "Определение макросов" , разделяя каждый из элементов точкой с запятой.Удалите переключатель компилятора
/Gy
. В диалоговом окне "Свойства проекта" выберите категорию создания кода C/C++>. Убедитесь, что свойство Enable Function-Level Linking не включено. Этот параметр упрощает экспорт классов, так как компоновщик не удаляет функции без ссылок. Если исходный проект создал обычную библиотеку DLL MFC, которая статически связана с MFC, измените/MT
параметр компилятора (или) на/MD
(или/MTd
/MDd
).Создайте библиотеку экспорта с параметром
/DLL
LINK. Этот параметр задается при создании нового целевого объекта и указана библиотека Dynamic-Link Win32 в качестве целевого типа.
Изменение файлов заголовков
Обычной целью библиотеки DLL расширения MFC является экспорт некоторых общих функций в одно или несколько приложений, которые могут использовать эту функцию. По сути, библиотека DLL экспортирует классы и глобальные функции для использования клиентскими приложениями.
Чтобы гарантировать, что каждая функция-член помечается для импорта или экспорта соответствующим образом, используйте специальные объявления __declspec(dllexport)
и __declspec(dllimport)
. Если клиентские приложения используют классы, они должны быть объявлены как __declspec(dllimport)
. При построении библиотеки DLL расширения MFC функции должны быть объявлены как __declspec(dllexport)
. Встроенная библиотека DLL также должна экспортировать функции, чтобы клиентские программы могли привязать их во время загрузки.
Чтобы экспортировать весь класс, используйте AFX_EXT_CLASS
в определении класса. Платформа определяет этот макрос как __declspec(dllexport)
когда _AFXDLL
и _AFXEXT
определяется, но определяет его как __declspec(dllimport)
если _AFXEXT
не определено. _AFXEXT
определяется только при создании библиотеки DLL расширения MFC. Например:
class AFX_EXT_CLASS CExampleExport : public CObject
{ /* ... class definition ... */ };
Неполный экспорт класса
Иногда может потребоваться экспортировать только отдельные необходимые члены класса. Например, при экспорте производного CDialog
класса может потребоваться только экспортировать конструктор и DoModal
вызов. Вы можете экспортировать эти члены с помощью ФАЙЛА DEF библиотеки DLL, но вы также можете использовать AFX_EXT_CLASS
так же, как и для отдельных элементов, которые необходимо экспортировать.
Например:
class CExampleDialog : public CDialog
{
public:
AFX_EXT_CLASS CExampleDialog();
AFX_EXT_CLASS int DoModal();
// rest of class definition
// ...
};
При этом может возникнуть дополнительная проблема, так как вы не экспортируете все члены класса. Проблема заключается в том, как работают макросы MFC. Некоторые из вспомогательных макросов MFC объявляют или определяют элементы данных. Библиотека DLL также должна экспортировать эти элементы данных.
Например, макрос DECLARE_DYNAMIC определяется следующим образом при создании библиотеки DLL расширения MFC:
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
static AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
Строка, начинающаяся static AFX_DATA
с объявления статического объекта внутри класса. Чтобы правильно экспортировать этот класс и получить доступ к данным среды выполнения из клиентского EXE, необходимо экспортировать этот статический объект. Так как статический объект объявлен модификатором AFX_DATA
, необходимо определить AFX_DATA
__declspec(dllexport)
только при сборке библиотеки DLL. Определите его как __declspec(dllimport)
при сборке исполняемого файла клиента.
Как описано выше, AFX_EXT_CLASS
уже определен таким образом. Вам просто нужно переопределить AFX_DATA
определение класса, чтобы он был таким же, как AFX_EXT_CLASS
и в определении класса.
Например:
#undef AFX_DATA
#define AFX_DATA AFX_EXT_CLASS
class CExampleView : public CView
{
DECLARE_DYNAMIC()
// ... class definition ...
};
#undef AFX_DATA
#define AFX_DATA
MFC всегда использует AFX_DATA
символ для элементов данных, которые он определяет в своих макросах, поэтому этот метод будет работать для всех таких сценариев. Например, он будет работать для DECLARE_MESSAGE_MAP.
Примечание.
При экспорте всего класса вместо отдельных элементов класса статические элементы данных экспортируются автоматически.
Вы можете использовать тот же метод для автоматического CArchive
экспорта оператора извлечения для классов, использующих макросы DECLARE_SERIAL и IMPLEMENT_SERIAL. Экспортируйте оператор архива, закобив объявления класса (расположенные в файле заголовка) со следующим кодом:
#undef AFX_API
#define AFX_API AFX_EXT_CLASS
/* your class declarations here */
#undef AFX_API
#define AFX_API
Ограничения для символа _AFXEXT
Вы можете использовать _AFXEXT
символ предварительного процессора для библиотек DLL расширения MFC, если у вас нет нескольких уровней БИБЛИОТЕК DLL расширения MFC. Если у вас есть библиотеки DLL для расширения MFC, которые вызывают или выводят классы из ваших собственных библиотек DLL для расширения MFC, а те, в свою очередь, используют производные классы MFC, вам потребуется использовать собственный символ препроцессора, чтобы избежать неоднозначности.
Проблема заключается в том, что в Win32 необходимо явно объявить все данные для __declspec(dllexport)
экспорта из библиотеки DLL и __declspec(dllimport)
импортировать их из библиотеки DLL. При определении _AFXEXT
заголовки MFC убедитесь, что AFX_EXT_CLASS
он определен правильно.
При наличии нескольких слоев один символ, например AFX_EXT_CLASS
недостаточно: библиотека DLL расширения MFC может экспортировать собственные классы, а также импортировать другие классы из другой библиотеки DLL расширения MFC. Чтобы справиться с этой проблемой, используйте специальный символ препроцессора, указывающий, что вы создаете библиотеку DLL вместо использования библиотеки DLL. Например, представьте, что два библиотеки DLL расширения MFC и A.DLL
B.DLL
. Каждый из них экспортирует некоторые классы в A.H
и B.H
соответственно. B.DLL
использует классы из A.DLL
. Файлы заголовка будут выглядеть примерно так:
/* 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 ... */ };
Когда A.DLL
он построен, он построен с /DA_IMPL
помощью и когда B.DLL
он построен, он построен с /DB_IMPL
помощью. С помощью отдельных символов для каждой библиотеки DLL CExampleB
экспортируется и CExampleA
импортируется при сборке B.DLL
. CExampleA
экспортируется при создании A.DLL
и импорте при использовании другим клиентом B.DLL
.
Этот тип слоев нельзя сделать при использовании встроенных AFX_EXT_CLASS
и _AFXEXT
препроцессорных символов. Описанный выше метод решает эту проблему так же, как mFC. MFC использует этот метод при создании библиотек DLL расширения OLE, Database и Network MFC.
По-прежнему не экспортирует весь класс
Опять же, вам придется заботиться о том, что вы не экспортируете весь класс. Убедитесь, что необходимые элементы данных, созданные макросами MFC, экспортируются правильно. Это можно сделать, переопределяя AFX_DATA
макрос конкретного класса. Переопределите его всякий раз, когда вы не экспортируете весь класс.
Например:
// 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
Ниже приведен код, который следует поместить в основной исходный файл библиотеки DLL расширения MFC. Он должен прийти после стандартного включает. При использовании AppWizard для создания начальных файлов для библиотеки DLL расширения MFC он предоставляет вам доступ DllMain
.
#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
}
AfxInitExtensionModule
Вызов для записи классов среды выполнения модуля (CRuntimeClass
структур) и его фабрики объектов (COleObjectFactory
объекты) для последующего CDynLinkLibrary
использования при создании объекта. Вызов AfxTermExtensionModule
(необязательно) позволяет MFC очистить библиотеку DLL расширения MFC, когда каждый процесс отсоединяется (что происходит при выходе процесса или при выгрузке библиотеки DLL вызовом FreeLibrary
) из библиотеки DLL расширения MFC. Так как большинство библиотек DLL расширения MFC не загружаются динамически (обычно они связаны с помощью библиотек импорта), вызов AfxTermExtensionModule
обычно не требуется.
Если приложение загружает и освобождает библиотеки DLL расширений MFC динамически, обязательно вызовите AfxTermExtensionModule
, как показано выше. Также обязательно используйте AfxLoadLibrary
и AfxFreeLibrary
(вместо функций LoadLibrary
Win32 и FreeLibrary
) если приложение использует несколько потоков или динамически загружает библиотеку DLL расширения MFC. Использование AfxLoadLibrary
и AfxFreeLibrary
обеспечение того, что код запуска и завершения работы, выполняемый при загрузке и выгрузке библиотеки DLL расширения MFC, не повреждает глобальное состояние MFC.
Файл AFXDLLX.H
заголовка содержит специальные определения для структур, используемых в библиотеках DLL расширения MFC, таких как определение AFX_EXTENSION_MODULE
и CDynLinkLibrary
.
Глобальный extensionDLL должен быть объявлен, как показано ниже. В отличие от 16-разрядной версии MFC, вы можете выделить память и вызвать функции MFC в течение этого времени, так как MFCxx.DLL
полностью инициализирован к моменту DllMain
вызова.
Совместное использование ресурсов и классов
Простые библиотеки DLL расширения MFC должны экспортировать только несколько функций с низкой пропускной способностью в клиентское приложение и ничего больше. Более интенсивные библиотеки DLL пользовательского интерфейса могут потребоваться экспортировать ресурсы и классы C++ в клиентское приложение.
Экспорт ресурсов осуществляется посредством списка ресурсов. В каждом приложении представлен единый список CDynLinkLibrary
объектов. При поиске ресурса большинство стандартных реализаций MFC, которые загружают ресурсы, сначала смотрят на текущий модуль ресурсов (AfxGetResourceHandle
) и если не нашли список CDynLinkLibrary
объектов, пытающихся загрузить запрошенный ресурс.
Динамическое создание объектов C++ с именем класса C++ аналогично. Механизм десериализации объектов MFC должен иметь все CRuntimeClass
объекты, зарегистрированные таким образом, чтобы он смог восстановить путем динамического создания объекта C++ требуемого типа в зависимости от того, что было сохранено ранее.
Если вы хотите, чтобы клиентское приложение использовало классы в библиотеке DLL DECLARE_SERIAL
расширения MFC, необходимо экспортировать классы, чтобы сделать их видимыми для клиентского приложения. Это также сделано путем прогулки по списку CDynLinkLibrary
.
В примере DLLHUSK
расширенных концепций MFC список выглядит примерно так:
head -> DLLHUSK.EXE - or - DLLHUSK.EXE
| |
TESTDLL2.DLL TESTDLL2.DLL
| |
TESTDLL1.DLL TESTDLL1.DLL
| |
| |
MFC90D.DLL MFC90.DLL
Запись MFCxx.DLL
обычно выполняется в списке ресурсов и классов. MFCxx.DLL
включает все стандартные ресурсы MFC, включая строки запроса для всех стандартных идентификаторов команд. Размещение его в конце списка позволяет библиотекам DLL и клиентскому приложению полагаться на общие ресурсы в MFCxx.DLL
списке, а не иметь собственные копии.
Слияние ресурсов и имен классов всех библиотек DLL в пространство имен клиентского приложения имеет недостаток, который необходимо тщательно учитывать, какие идентификаторы или имена вы выбираете. Эту функцию можно отключить, не экспортируя ресурсы или CDynLinkLibrary
объект в клиентское приложение. Пример DLLHUSK
управляет пространством имен общего ресурса с помощью нескольких файлов заголовков. Дополнительные советы по использованию общих файлов ресурсов см . в техническом примечание 35 .
Инициализация библиотеки DLL
Как упоминание выше, обычно требуется создать CDynLinkLibrary
объект для экспорта ресурсов и классов в клиентское приложение. Для инициализации библиотеки DLL необходимо предоставить экспортированную точку входа. Минимально, это подпрограмма void
, которая не принимает аргументы и возвращает ничего, но это может быть все, что вам нравится.
Каждое клиентское приложение, которое хочет использовать библиотеку DLL, должно вызывать эту подпрограмму инициализации, если вы используете этот подход. Этот объект DllMain
также можно выделить CDynLinkLibrary
сразу после вызоваAfxInitExtensionModule
.
Подпрограмма инициализации должна создать CDynLinkLibrary
объект в куче текущего приложения, подключенную к сведениям о библиотеке DLL расширения MFC. Это можно сделать, определив такую функцию:
extern "C" extern void WINAPI InitXxxDLL()
{
new CDynLinkLibrary(extensionDLL);
}
Имя подпрограммы InitXxxDLL в этом примере может быть любым нужным. Это не обязательно, extern "C"
но это упрощает обслуживание списка экспорта.
Примечание.
Если вы используете библиотеку DLL расширения MFC из обычной библиотеки DLL MFC, необходимо экспортировать эту функцию инициализации. Эта функция должна вызываться из обычной библиотеки DLL MFC, прежде чем использовать все классы или ресурсы библиотеки DLL расширения MFC.
Экспорт записей
Простой способ экспорта классов — использовать __declspec(dllimport)
__declspec(dllexport)
и для каждого класса и глобальной функции, которую вы хотите экспортировать. Это гораздо проще, но это менее эффективно, чем именование каждой точки входа в DEF-файле, как описано ниже. Это связано с тем, что у вас меньше контроля над экспортируемыми функциями. И вы не можете экспортировать функции по порядковой версии. TESTDLL1 и TESTDLL2 использовать этот метод для экспорта записей.
Более эффективный метод — экспортировать каждую запись, именуя ее в DEF-файле. Этот метод используется MFCxx.DLL
. Так как мы экспортируем выборочно из библиотеки DLL, мы должны решить, какие определенные интерфейсы мы хотим экспортировать. Трудно, так как необходимо указать имена компоновщика в виде записей в файле DEF. Не экспортируйте класс C++, если для него не требуется символьная ссылка.
Если вы попытались экспортировать классы C++ с файлом DEF до этого, возможно, потребуется разработать средство для автоматического создания этого списка. Это можно сделать с помощью двухэтапного процесса компоновки. Свяжите библиотеку DLL один раз без экспорта и разрешите компоновщику создать файл MAP. Файл MAP содержит список функций, которые следует экспортировать. С помощью некоторых переупорядочений его можно использовать для создания записей EXPORT для файла DEF. Список экспорта для MFCxx.DLL
библиотек DLL расширений OLE и Database MFC, несколько тысяч, был создан с таким процессом (хотя это не полностью автоматически и требуется некоторая настройка рук каждый раз в некоторое время).
CWinApp и CDynLinkLibrary
Библиотека DLL расширения MFC не имеет CWinApp
собственного производного объекта. Вместо этого он должен работать с производным CWinApp
объектом клиентского приложения. Это означает, что клиентское приложение владеет основным насосом сообщений, циклом простоя и т. д.
Если библиотека DLL расширения MFC должна поддерживать дополнительные данные для каждого приложения, можно наследить новый класс и CDynLinkLibrary
создать его в описанном выше подпрограмме InitXxxDLL
. При запуске библиотека DLL может проверка список объектов текущего приложения, чтобы найти его для конкретной CDynLinkLibrary
библиотеки DLL расширения MFC.
Использование ресурсов в реализации БИБЛИОТЕКИ DLL
Как упоминание выше, загрузка ресурсов по умолчанию будет ходить по списку CDynLinkLibrary
объектов, которые ищут первый EXE или DLL, имеющий запрошенный ресурс. Все API MFC и весь внутренний код используются AfxFindResourceHandle
для обхода списка ресурсов, чтобы найти любой ресурс независимо от того, где он расположен.
Если вы хотите загрузить ресурсы только из определенного места, используйте API AfxGetResourceHandle
и AfxSetResourceHandle
сохраните старый дескриптор и задайте новый дескриптор. Не забудьте восстановить старый обработчик ресурсов перед возвратом в клиентское приложение. Пример TESTDLL2 использует этот подход для явной загрузки меню.
В списке есть некоторые недостатки: это немного медленнее и требует управления диапазонами идентификаторов ресурсов. Преимущество заключается в том, что клиентское приложение, которое ссылается на несколько библиотек DLL расширения MFC, может использовать любой ресурс, предоставляемый библиотекой DLL, без указания обработчика экземпляра DLL. AfxFindResourceHandle
— это API, используемый для анализа списка ресурсов для поиска заданного соответствия. Он принимает имя и тип ресурса, и возвращает дескриптор ресурса, где он сначала находит ресурс или NULL.
Написание приложения, использующего версию DLL
Требования к приложению
Приложение, использующее общую версию MFC, должно соответствовать нескольким основным правилам:
Он должен иметь
CWinApp
объект и следовать стандартным правилам для насоса сообщений.Он должен быть скомпилирован с набором обязательных флагов компилятора (см. ниже).
Он должен связаться с библиотеками импорта MFCxx. Задав необходимые флаги компилятора, заголовки MFC определяют во время ссылки, с какой библиотекой приложение должно связаться.
Чтобы запустить исполняемый файл,
MFCxx.DLL
должен находиться в пути или в системном каталоге Windows.
Создание среды разработки
Если вы используете внутренний файл makefile с большинством стандартных значений по умолчанию, вы можете легко изменить проект, чтобы создать версию DLL.
На следующем шаге предполагается, что у вас есть правильно функционирующее приложение MFC, связанное с NAFXCWD.LIB
(для отладки) и NAFXCW.LIB
(для выпуска), и вы хотите преобразовать его для использования общей версии библиотеки MFC. Вы запускаете среду Visual Studio и имеете внутренний файл проекта.
- В меню "Проекты" выберите "Свойства". На странице "Общие" в разделе "Значения по умолчанию проекта" задайте классы Microsoft Foundation для использования MFC в общей библиотеке DLL (MFCxx(d).dll).
Создание с помощью NMAKE
Если вы используете внешний компонент makefile компилятора или используете NMAKE напрямую, необходимо изменить файл makefile для поддержки необходимых параметров компилятора и компоновщика.
Обязательные флаги компилятора:
/D_AFXDLL /MD
/D_AFXDLL
Стандартные заголовки MFC должны _AFXDLL
определять символ.
/MD
Приложение должно использовать версию DLL библиотеки времени выполнения C.
Все остальные флаги компилятора следуют значениям по умолчанию MFC (например, _DEBUG
для отладки).
Измените список библиотек компоновщика. Измените NAFXCWD.LIB
на MFCxxD.LIB
, а NAFXCW.LIB
— на MFCxx.LIB
. Замените LIBC.LIB
на MSVCRT.LIB
. Как и в любой другой библиотеке MFC, важно MFCxxD.LIB
поместить ее перед любыми библиотеками среды выполнения C.
При необходимости добавьте /D_AFXDLL
параметры компилятора ресурсов выпуска и отладки (тот, с которыми фактически компилируется ресурсы)./R
Этот параметр делает окончательный исполняемый файл меньше, предоставляя общий доступ к ресурсам, которые присутствуют в библиотеках DLL MFC.
После внесения этих изменений требуется полная перестроение.
Создание примеров
Большинство примеров программ MFC можно создавать из Visual C++ или из общего ФАЙЛА MAKEFILE, совместимого с NMAKE, из командной строки.
Чтобы преобразовать любой из этих примеров для использования MFCxx.DLL
, можно загрузить MAK-файл в Visual C++ и задать параметры проекта, как описано выше. Если вы используете сборку NMAKE, можно указать AFXDLL=1
в командной строке NMAKE и создать пример с помощью общих библиотек MFC.
Пример расширенных концепций MFC DLLHUSK построен с версией БИБЛИОТЕКи DLL MFC. В этом примере не только показано, как создать приложение, связанное с MFCxx.DLL
приложением, но и иллюстрирует другие функции варианта упаковки БИБЛИОТЕК DLL MFC, например библиотеки DLL расширения MFC, описанные далее в этом техническом примечание.
Заметки о упаковке
Версии выпусков библиотек DLL (MFCxx.DLL
и MFCxxU.DLL
) являются свободно распространяемыми. Отладочные версии библиотек DLL не являются свободно распространяемыми и должны использоваться только во время разработки приложения.
Библиотеки DLL отладки предоставляются с информацией об отладке. С помощью отладчика Visual C++ можно отслеживать выполнение как приложения, так и библиотеки DLL. Библиотеки DLL выпуска (MFCxx.DLL
и MFCxxU.DLL
) не содержат сведения об отладке.
Если вы настраиваете или перестроите библиотеки DLL, их следует вызывать, кроме MFCxx. Файл SRC MFCDLL.MAK
MFC описывает параметры сборки и содержит логику переименования библиотеки DLL. Переименование файлов необходимо, так как эти библиотеки DLL потенциально совместно используются многими приложениями MFC. Если пользовательская версия библиотек DLL MFC заменяет те, которые установлены в системе, могут разорвать другое приложение MFC с помощью общих библиотек DLL MFC.
Перестроение библиотек DLL MFC не рекомендуется.
Реализация MFCxx.DLL
В следующем разделе описывается реализация библиотеки DLLMFCxx.DLL
(и MFCxxD.DLL
) MFC. Общие сведения здесь также не важны, если все, что вы хотите сделать, использует библиотеку DLL MFC с приложением. Сведения здесь не являются важными для понимания того, как писать библиотеку DLL расширения MFC, но понимание этой реализации может помочь вам написать собственную библиотеку DLL.
Обзор реализации
Библиотека DLL MFC действительно является особым случаем библиотеки DLL расширения MFC, как описано выше. Он имеет большое количество экспортов для большого количества классов. В библиотеке DLL MFC есть несколько дополнительных действий, которые делают его еще более особенным, чем обычная библиотека DLL расширения MFC.
Win32 выполняет большую часть работы
16-разрядная версия MFC требовала ряда специальных методов, включая данные для каждого приложения в сегменте стека, специальные сегменты, созданные примерно кодом сборки 80x86, контекстами исключений для каждого процесса и другими методами. Win32 напрямую поддерживает данные для каждого процесса в библиотеке DLL. Это то, что требуется больше всего времени. Большая часть MFCxx.DLL
только NAFXCW.LIB
что упакована в библиотеку DLL. Если вы посмотрите на исходный код MFC, вы найдете несколько #ifdef _AFXDLL
вариантов, так как не существует большого количества особых случаев, которые необходимо сделать. Специальные случаи, которые существуют специально для решения Win32 в Windows 3.1 (иначе называется Win32s). Win32s не поддерживает данные DLL для каждого процесса напрямую. Библиотека DLL MFC должна использовать API Win32 для обработки локальных данных потока (TLS).
Влияние на источники библиотеки, дополнительные файлы
_AFXDLL
Влияние версии на обычные источники и заголовки библиотеки классов MFC является относительно незначительным. Существует специальный файл версии () и дополнительный файл заголовка (AFXV_DLL.H
AFXDLL_.H
), включенный в основной AFXWIN.H
заголовок. Заголовок AFXDLL_.H
содержит CDynLinkLibrary
класс и другие сведения о реализации библиотек DLL для приложений и расширений _AFXDLL
MFC. Заголовок AFXDLLX.H
предоставляется для создания библиотек DLL расширения MFC (см. выше).
Обычные источники библиотеки MFC в SRC MFC имеют дополнительный условный код в _AFXDLL
#ifdef. Дополнительный исходный файл (DLLINIT.CPP
) содержит дополнительный код инициализации DLL и другой клей для общей версии MFC.
Чтобы создать общую версию MFC, предоставляются дополнительные файлы. (Дополнительные сведения о сборке библиотеки DLL см. ниже.
Два ФАЙЛА DEF используются для экспорта точек входа библиотеки DLL MFC для отладочных (
MFCxxD.DEF
) и выпусков (MFCxx.DEF
) версий библиотеки DLL.Rc-файл (
MFCDLL.RC
) содержит все стандартные ресурсы MFC иVERSIONINFO
ресурс библиотеки DLL.Файл CLW
MFCDLL.CLW
() предоставляется для просмотра классов MFC с помощью ClassWizard. Эта функция не является конкретной версией библиотеки DLL MFC.
Управление памятью
Приложение, использующее MFCxx.DLL
общий инструмент выделения памяти, предоставляемый MSVCRTxx.DLL
общей библиотекой DLL среды выполнения C. Приложение, все библиотеки DLL расширений MFC и библиотеки DLL MFC используют этот общий модуль выделения памяти. Используя общую библиотеку DLL для выделения памяти, библиотеки DLL MFC могут выделять память, которая позже освобождается приложением или наоборот. Так как приложение и библиотека DLL должны использовать один и тот же распределитель, вы не должны переопределить глобальный или operator delete
глобальный operator new
C++. Те же правила применяются к остальным подпрограммам выделения памяти во время выполнения C (напримерmalloc
, , realloc
free
и другим).
Порядковые номера и классы __declspec(dllexport) и именование БИБЛИОТЕК DLL
Мы не используем функциональные class
__declspec(dllexport)
возможности компилятора C++. Вместо этого список экспортов включается в источники библиотеки классов (MFCxx.DEF
и MFCxxD.DEF
). Экспортируются только набор точек входа (функций и данных). Другие символы, такие как функции или классы частной реализации MFC, не экспортируются. Все операции экспорта выполняются порядковым порядком без имени строки в таблице имен резидентов или не резидентов.
Использование class
__declspec(dllexport)
может быть жизнеспособной альтернативой для создания небольших библиотек DLL, но в большой библиотеке DLL, такой как MFC, механизм экспорта по умолчанию имеет ограничения эффективности и емкости.
Все это означает, что мы можем упаковыть большое количество функциональных возможностей в выпускеMFCxx.DLL
, что только около 800 КБ без ущерба для выполнения или загрузки скорости. MFCxx.DLL
было бы 100 КБ больше, если бы этот метод не использовался. Этот метод позволяет добавить дополнительные точки входа в конце ФАЙЛА DEF. Он позволяет легко использовать управление версиями без ущерба для скорости и размера экспорта по порядковой версии. Изменения основных версий в библиотеке классов MFC изменят имя библиотеки. То есть распространяемая библиотека DLL, MFC30.DLL
содержащая версию 3.0 библиотеки классов MFC. Обновление этой библиотеки DLL, например, в гипотетической версии MFC 3.1, вместо этого будет называться MFC31.DLL
библиотека DLL. Опять же, если изменить исходный код MFC, чтобы создать пользовательскую версию библиотеки DLL MFC, используйте другое имя (и желательно без MFC в имени).
См. также
Технические примечания по номеру
Технические примечания по категории