Библиотеки DLL для расширения MFC
Библиотеки DLL расширения MFC — это библиотеки DLL, которые обычно реализуют классы многократного использования в существующих библиотеках классов Microsoft Foundation Class.
Библиотека DLL расширения MFC обладает следующими функциями и требованиями.
Клиентский исполняемый файл должен быть приложением MFC, скомпилированным с определенным
_AFXDLL
.Библиотека DLL расширения MFC может также использоваться в обычной библиотеке DLL MFC, которая динамически связана с MFC.
Библиотеки DLL расширения MFC должны быть скомпилированы с определенным
_AFXEXT
. Это приводит также к определению_AFXDLL
и гарантирует, что в файлах заголовков MFC будут извлечены соответствующие объявления. Это также гарантирует, чтоAFX_EXT_CLASS
определяется как__declspec(dllexport)
при сборке библиотеки DLL, что необходимо, если вы используете этот макрос для объявления классов в библиотеке DLL расширения MFC.Библиотеки DLL расширения MFC не должны создавать экземпляры класса, производного от
CWinApp
, но для предоставления этого объекта следует полагаться на клиентское приложение (или библиотеку DLL).Однако библиотеки DLL расширения MFC должны предоставлять функцию
DllMain
и выполнять необходимую инициализацию.
Библиотеки DLL расширения создаются с помощью библиотеки динамической компоновки MFC (также известной как общая версия MFC). Только исполняемые файлы MFC (приложения или обычные библиотеки DLL MFC), созданные с помощью общей версии MFC, могут использовать библиотеку DLL расширения MFC. Как клиентское приложение, так и библиотека DLL расширения MFC должны использовать одну и ту же версию MFCx0.dll. С помощью библиотеки DLL расширения MFC можно создавать новые пользовательские классы из MFC, а затем предоставлять эту расширенную версию MFC приложениям, которые вызывают библиотеку DLL.
Библиотеки DLL расширения также можно использовать для передачи объектов, производных от MFC, между приложением и библиотекой DLL. Функции элементов, связанные с переданным объектом, существуют в модуле, в котором был создан объект. Поскольку эти функции должны быть правильно экспортированы при использовании общей версии библиотеки MFC, можно свободно передавать указатели на объекты, производные от MFC или MFC, между приложением и загружаемыми библиотеками DLL расширения MFC.
Библиотека DLL расширения MFC использует общую версию MFC так же, как приложение использует общую версию библиотеки DLL MFC с несколькими дополнительными моментами, которые следует учитывать.
Она не содержит объект, производный от
CWinApp
. Она должна работать с объектом, производным отCWinApp
, клиентского приложения. Это означает, что клиентское приложение владеет основным конвейером сообщений, циклом бездействия и т. д.Она вызывает
AfxInitExtensionModule
в своей функцииDllMain
. Необходимо проверить возвращаемое значение этой функции. Если изAfxInitExtensionModule
возвращается нулевое значение, из функцииDllMain
возвращается 0.Она создает объект CDynLinkLibrary во время инициализации, если библиотека DLL расширения MFC экспортирует объекты
CRuntimeClass
или ресурсы в приложение.
В версиях MFC, предшествующих версии 4.0, этот тип библиотеки DLL назывался AFXDLL. AFXDLL относится к символу препроцессора _AFXDLL
, который определяется при построении библиотеки DLL.
Библиотеки импорта для общей версии MFC именуются в соответствии с соглашением, описанным в статье Соглашения об именовании библиотек DLL MFC. Visual Studio предоставляет предварительно созданные версии библиотек DLL MFC, а также ряд библиотек DLL, не относящихся к MFC, которые можно использовать и распространять вместе с приложениями. Они описаны в файле Redist.txt, который устанавливается в папку Program Files\Microsoft Visual Studio.
Если вы выполняете экспорт с применением DEF-файла, поместите следующий код в начало и в конец файла заголовка:
#undef AFX_DATA
#define AFX_DATA AFX_EXT_DATA
// <body of your header file>
#undef AFX_DATA
#define AFX_DATA
Эти четыре строки гарантируют правильную компиляцию кода для библиотеки DLL расширения MFC. Если не указать эти четыре строки, компиляция или компоновка библиотеки DLL может быть выполнена неправильно.
Если необходимо передать указатель на объект MFC или объект MFC, производный от библиотеки DLL MFC, библиотека DLL должна быть библиотекой DLL расширения MFC. Функции элементов, связанные с переданным объектом, существуют в модуле, в котором был создан объект. Поскольку эти функции должны быть правильно экспортированы при использовании общей версии библиотеки MFC, можно свободно передавать указатели на объекты, производные от MFC или MFC, между приложением и загружаемыми библиотеками DLL расширения MFC.
Ввиду искажения имен C++ и проблем с экспортом список экспорта из библиотеки DLL расширения MFC может отличаться в отладочной и розничной версиях одной и той же библиотеки DLL и библиотеках DLL для разных платформ. В розничной версии MFCx0.dll содержится около 2000 экспортированных точек входа; в отладочной версии MFCx0D.dll содержится порядка 3000 экспортированных точек входа.
Управление памятью
MFCx0.dll и все библиотеки DLL расширения MFC, загруженные в адресное пространство клиентского приложения, используют одни и те же распределитель памяти, загрузку ресурсов и другие глобальные состояния MFC, как если бы они находились в одном приложении. Это важно, так как библиотеки DLL, не относящиеся к MFC, и обычные библиотеки DLL MFC выполняют в точности противоположные задачи и каждая библиотека DLL выделяется из собственного пула памяти.
Если библиотека DLL расширения MFC выделяет память, эта память может свободно смешиваться с любым другим объектом, выделенным приложением. Кроме того, если в работе приложения, которое динамически связывается с MFC, возникает сбой, защита операционной системы обеспечивает целостность любого другого приложения MFC, которое совместно использует библиотеку DLL.
Аналогично, другие глобальные состояния MFC, такие как текущий исполняемый файл для загрузки ресурсов, также совместно используются клиентским приложением и всеми библиотеками DLL расширения MFC, а также самой MFCx0.dll.
Совместное использование ресурсов и классов
Экспорт ресурсов осуществляется посредством списка ресурсов. Каждое приложение содержит отдельно связанный список объектов CDynLinkLibrary. При поиске ресурса большинство стандартных реализаций MFC, которые загружают ресурсы, сначала выполняют поиск в текущем модуле ресурсов (AfxGetResourceHandle
). Если ресурс не найден, выполните анализ списка объектов CDynLinkLibrary, пытающихся загрузить запрошенный ресурс.
Анализ списка имеет недостатки, которые немного замедляют работу и требуют управления диапазонами идентификаторов ресурсов. Преимущество заключается в том, что клиентское приложение, которое ссылается на несколько библиотек DLL расширения MFC, может использовать любой ресурс, предоставляемый библиотекой DLL, без указания обработчика экземпляра DLL. AfxFindResourceHandle
— это API, используемый для анализа списка ресурсов для поиска заданного соответствия. Он принимает имя и тип ресурса и возвращает маркер ресурса, в котором он был впервые найден (или значение NULL).
Если вы не хотите анализировать список и хотите загрузить ресурсы только из определенного места, используйте функции AfxGetResourceHandle
и AfxSetResourceHandle
, чтобы сохранить старый обработчик и задать новый. Не забудьте восстановить старый обработчик ресурсов перед возвратом в клиентское приложение. Пример использования этого подхода для явной загрузки меню см. в разделе Testdll2.cpp в примере MFC DLLHUSK.
Динамическое создание объектов MFC с учетом имени MFC выполняется аналогичным образом. Механизму десериализации объектов MFC требуются зарегистрированные объекты CRuntimeClass
, чтобы он мог восстановить их путем динамического создания объектов C++ требуемого типа в зависимости от того, что было сохранено ранее.
В случае с примером MFC DLLHUSK список выглядит примерно следующим образом:
head -> DLLHUSK.EXE - or - DLLHUSK.EXE
| |
TESTDLL2.DLL TESTDLL2.DLL
| |
TESTDLL1.DLL TESTDLL1.DLL
| |
MFCOxxD.DLL |
| |
MFCDxxD.DLL |
| |
MFCxxD.DLL MFCxx.DLL
где xx — это номер версии; например, 42 представляет версию 4.2.
MFCxx.dll обычно является последним в списке ресурсов и классов. MFCxx.dll содержит все стандартные ресурсы MFC, включая строки запросов для всех стандартных идентификаторов команд. Размещение в конце списка позволяет библиотекам DLL и клиентскому приложению не иметь собственной копии стандартных ресурсов MFC, но вместо этого использовать общие ресурсы в MFCxx.dll.
Объединение ресурсов и имен классов всех библиотек DLL в пространство имен клиентского приложения не требует соблюдения осторожности при выборе идентификаторов или имен.
Пример DLLHUSK управляет пространством имен общих ресурсов с помощью нескольких файлов заголовков.
Если библиотека DLL расширения MFC должна поддерживать дополнительные данные для каждого приложения, можно создать новый класс на основе CDynLinkLibrary и создать его в DllMain
. При запуске библиотека DLL может проверить список объектов CDynLinkLibrary текущего приложения, чтобы найти ресурс для конкретной библиотеки DLL расширения MFC.