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. В границах приложения или библиотеки DLL используются интерфейсы C, а клиентское приложение не обязательно должно быть приложением MFC.

Обычные библиотеки DLL MFC — это версия библиотек DLL, поддерживаемых в MFC 1,0. Они описаны в техническом примечании 11 и в примере DLLScreenCap с дополнительными концепциями MFC.

Примечание

Начиная с версии Visual C++ 4,0 термин усрдлл устарел и заменен обычной библиотекой DLL MFC, которая статически связывается с MFC. Вы также можете создать обычную библиотеку DLL MFC, которая динамически связывается с MFC.

MFC 3,0 (и более поздние версии) поддерживает регулярные библиотеки DLL MFC со всеми новыми функциями, включая классы OLE и базы данных.

AFXDLL: также называется общей версией библиотек MFC. Это новая поддержка DLL, добавленная в MFC 2,0. Сама библиотека MFC состоит из нескольких библиотек DLL (описанных ниже). Клиентское приложение или библиотека DLL динамически связывают необходимые библиотеки DLL. Интерфейсы через границы приложения или библиотеки DLL являются интерфейсами классов C++/МФК. Клиентское приложение должно быть приложением MFC. Эта библиотека DLL поддерживает все функции MFC 3,0 (исключение: Юникод не поддерживается для классов баз данных).

Примечание

Начиная с версии Visual C++ 4,0 этот тип DLL называется "DLL расширения".

Это примечание будет использоваться MFCxx.DLL для ссылки на весь набор DLL MFC, в том числе:

  • Отладка: MFCxxD.DLL (Объединенная) и MFCSxxD.LIB (static).

  • Выпуск: MFCxx.DLL (Объединенная) и MFCSxx.LIB (static).

  • Отладка в Юникоде: MFCxxUD.DLL (Объединенная) и MFCSxxD.LIB (static).

  • Выпуск Юникод: MFCxxU.DLL (Объединенная) и MFCSxxU.LIB (static).

Примечание

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 000).

  • Общая версия 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 значение. Помощью мастера предоставляет DllMain функцию, которую можно изменить.

  • Библиотека DLL расширения MFC обычно предоставляет подпрограммы инициализации для создания CDynLinkLibrary , если библиотека DLL расширения MFC экспортирует CRuntimeClass в приложение типы или ресурсы. Производный класс CDynLinkLibrary может использоваться, если данные каждого приложения должны поддерживаться библиотекой DLL расширения MFC.

Эти рекомендации подробно описаны ниже. См. также пример DLLHUSK с дополнительными понятиями MFC. В примере показано:

  • Создайте приложение с помощью общих библиотек. ( DLLHUSK.EXE — это приложение MFC, которое динамически связывается с библиотеками MFC и другими библиотеками DLL.)

  • Создайте библиотеку DLL расширения MFC. (В нем показано, как использовать специальные флаги, такие как _AFXEXT Get, при построении библиотеки DLL расширения MFC.)

  • Создайте два примера библиотек DLL расширения MFC. В одной показана базовая структура библиотеки DLL расширения MFC с ограниченными экспортами (TESTDLL1), а другая — экспорт всего интерфейса класса (TESTDLL2).

Как клиентское приложение, так и любая библиотека DLL расширения MFC должны использовать одну и ту же версию MFCxx.DLL . Следуйте соглашениям библиотек DLL MFC и предоставьте версию библиотеки DLL расширения MFC для отладки и выпуска ( /release ). Такой подход позволяет клиентским программам создавать отладочные и окончательные версии своих приложений и связывать их с соответствующей отладочной или окончательной версией всех библиотек DLL.

Примечание

Так как проблемы с искажением и экспортом имен C++, список экспорта из библиотеки DLL расширения MFC может различаться для версий отладки и выпуска одной и той же библиотеки DLL и библиотек DLL для разных платформ. В выпуске MFCxx.DLL содержится около 2000 экспортированных точек входа; Отладка MFCxxD.DLL содержит около 3000 экспортированных точек входа.

Быстрая Заметка об управлении памятью

В разделе "Управление памятью" около конца этой технической заметки описывается реализация платформы MFCxx.DLL с общей версией MFC. Сведения, которые необходимо знать для реализации только библиотеки DLL расширения MFC, описаны здесь.

MFCxx.DLL все библиотеки DLL расширения MFC, загруженные в адресное пространство клиентского приложения, будут использовать один и тот же распределитель памяти, загрузку ресурсов и другие глобальные состояния MFC, как если бы они находились в одном приложении. Это важно, так как библиотеки DLL MFC и обычные библиотеки MFC, которые статически связываются с MFC, выполняют точную противоположную задачу: каждая библиотека DLL выделяется из собственного пула памяти.

Если библиотека DLL расширения MFC выделяет память, то эта память может свободно смешивание с любым другим объектом, выделенным приложением. Кроме того, если приложение, использующее общие библиотеки MFC, аварийно завершает работу, операционная система сохраняет целостность любого другого приложения MFC, использующего библиотеку DLL.

Аналогичным образом, другие "глобальные" состояния MFC, такие как текущий исполняемый файл для загрузки ресурсов, также могут совместно использоваться клиентским приложением, всеми библиотеками DLL расширения MFC и MFCxx.DLL сама по себе.

Создание библиотеки DLL расширения MFC

Помощью мастера можно использовать для создания проекта библиотеки DLL расширения MFC и автоматически создает соответствующие параметры компилятора и компоновщика. Он также создает DllMain функцию, которую можно изменить.

Если вы преобразуете существующий проект в библиотеку DLL расширения MFC, начните со стандартных параметров, которые создаются с помощью общей версии MFC. Внесите в нее следующие изменения:

  • Добавьте /D_AFXEXT к флагам компилятора. в диалоговом окне свойства Project выберите категориюпрепроцессораC/C++> . Добавьте _AFXEXT в поле define Macros (определение макросов ), разделяя каждый элемент точкой с запятой.

  • /GyУдалите параметр компилятора. в диалоговом окне свойства Project выберите категориюсоздания кодаC/C++> . Убедитесь, что свойство включить Function-Level связывания не включено. Этот параметр упрощает экспорт классов, так как компоновщик не удаляет функции без ссылок. Если исходный проект создал обычную библиотеку DLL MFC, которая статически связана с MFC, измените /MT параметр компилятора (или /MTd ) на /MD (или /MDd ).

  • Создайте библиотеку экспорта с /DLL возможностью связывания. Этот параметр задается при создании нового целевого объекта и указании библиотеки 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 расширения MFC для OLE, базы данных и сети.

Все еще не экспортируется весь класс

Опять же, вам придется уделять особое внимание, если вы не экспортируете весь класс. Убедитесь, что необходимые элементы данных, созданные с помощью макросов 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. Он должен следовать после стандартных включений. При использовании помощью мастера для создания начальных файлов для библиотеки 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 при отсоединении каждого процесса (что происходит при завершении процесса или при выгрузке FreeLibrary библиотеки DLL в вызове) из библиотеки 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 .

Глобальный екстенсиондлл должен быть объявлен как показано ниже. В отличие от 16-разрядной версии MFC, можно выделить память и вызвать функции MFC в это время, так как объект MFCxx.DLL полностью инициализируется на время DllMain вызова.

Совместное использование ресурсов и классов

Простые библиотеки расширения MFC требуют только экспортировать несколько функций с низкой пропускной способностью в клиентское приложение и ничего больше. Для большего числа библиотек DLL с интенсивным использованием пользовательского интерфейса может потребоваться экспортировать ресурсы и классы C++ в клиентское приложение.

Экспорт ресурсов осуществляется посредством списка ресурсов. В каждом приложении используется однонаправленный список CDynLinkLibrary объектов. При поиске ресурса большинство стандартных реализаций MFC, которые загружают ресурсы, сначала выглядят в текущем модуле ресурсов ( AfxGetResourceHandle ), а если не найдены, то список CDynLinkLibrary объектов, пытающихся загрузить запрошенный ресурс.

Динамическое создание объектов C++ с именем класса C++ аналогично. Механизм десериализации объектов MFC должен иметь зарегистрированные объекты, чтобы его можно было CRuntimeClass восстановить путем динамического создания объекта C++ требуемого типа в зависимости от того, что было сохранено ранее.

Если требуется, чтобы клиентское приложение использовало классы в библиотеке DLL расширения MFC, которые являются DECLARE_SERIAL , необходимо экспортировать классы, чтобы сделать их видимыми для клиентского приложения. Это также делается путем прохода 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, должно вызывать эту подпрограммы инициализации при использовании такого подхода. Этот CDynLinkLibrary объект также можно выделить в DllMain сразу после вызова метода AfxInitExtensionModule .

Подпрограммы инициализации должны создать CDynLinkLibrary объект в куче текущего приложения, проводных до сведений о библиотеке DLL расширения MFC. Это можно сделать, определив функцию, подобную следующей:

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

Имя подпрограммы, инитксксксдлл в этом примере, может быть любым. Это не обязательно 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 один раз без экспорта и разрешите компоновщику создать файл отображения. Файл отображения содержит список функций, которые необходимо экспортировать. С помощью некоторых переупорядочений можно использовать его для создания записей экспорта для DEF файла. Список экспорта для MFCxx.DLL библиотек DLL расширения MFC и OLE и баз данных с несколькими тысячами в числе был создан с таким процессом (хотя он не является полностью автоматическим и требует какой-либо настройки в течение некоторого интервала).

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 объект и следовать стандартным правилам для конвейера сообщений.

  • Он должен быть скомпилирован с набором обязательных флагов компилятора (см. ниже).

  • Он должен быть связан с библиотеками импорта Мфккскс. Устанавливая необходимые флаги компилятора, заголовки MFC определяют во время компоновки, с какой библиотекой приложение должно связываться.

  • для запуска исполняемого файла MFCxx.DLL он должен находиться в пути или в системном каталоге Windows.

Сборка с помощью среды разработки

Если вы используете внутренний файл makefile с большинством стандартных значений по умолчанию, можно легко изменить проект, чтобы построить версию библиотеки DLL.

На следующем этапе предполагается, что у вас есть корректно работающее приложение MFC, связанное с NAFXCWD.LIB (для отладки) и NAFXCW.LIB (для выпуска), и вы хотите преобразовать его для использования общей версии библиотеки MFC. вы используете среду Visual Studio и имеете внутренний файл проекта.

  1. В меню проекты выберите пункт свойства. на странице общие в разделе Project значения по умолчаниюзадайте Microsoft Foundation Classes, чтобы использовать MFC в общей библиотеке DLL (мфккскс (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++ и задать параметры Project, как описано выше. Если вы используете сборку NMAKE, можно указать AFXDLL=1 в командной строке NMAKE и выполнить построение образца с помощью общих библиотек MFC.

Пример "Дополнительные понятия MFC" для примера DLLHUSK создан с использованием библиотеки DLL MFC. В этом примере не только показано, как создать приложение, связанное с MFCxx.DLL , но также показаны другие функции пакета DLL MFC, такие как библиотеки расширения MFC, описанные далее в этой технической ноте.

Заметки о упаковке

Окончательные версии библиотек DLL ( MFCxx.DLL и MFCxxU.DLL ) свободно распространяемы. Отладочные версии библиотек DLL не являются свободно распространяемыми и должны использоваться только во время разработки приложения.

Отладочные библиотеки DLL предоставляются с отладочной информацией. С помощью отладчика Visual C++ можно отслеживать выполнение приложения и библиотеки DLL. Библиотеки DLL выпуска ( MFCxx.DLL и MFCxxU.DLL ) не содержат отладочную информацию.

Если вы настраиваете или перестраиваете библиотеки DLL, следует вызывать их не так, как "Мфккскс". Файл MFCDLL.MAK src библиотеки MFC описывает параметры сборки и содержит логику для переименования библиотеки DLL. Переименование файлов является обязательным, так как эти библиотеки DLL потенциально являются общими для многих приложений MFC. Замена установленных в системе пользовательских версий библиотек DLL MFC может нарушить работу другого приложения MFC, использующего общие библиотеки DLL MFC.

Перестроение библиотек DLL MFC не рекомендуется.

Как реализуется MFCxx.DLL

В следующем разделе описано, как реализуется библиотека DLL MFC ( MFCxx.DLL и MFCxxD.DLL ). Понимание этой информации также не имеет значения, если все, что нужно сделать, — использовать библиотеку 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 класс и другие сведения о реализации как приложений, так _AFXDLL и библиотек DLL расширения MFC. AFXDLLX.HЗаголовок предоставляется для создания библиотек DLL расширения MFC (Дополнительные сведения см. выше).

Обычные источники для библиотеки MFC в MFC SRC имеют некоторый дополнительный условный код в _AFXDLL #ifdef. Дополнительный исходный файл ( DLLINIT.CPP ) содержит дополнительный код инициализации DLL и другое привязывание для общей версии MFC.

Для создания общей версии MFC предоставляются дополнительные файлы. (Дополнительные сведения о построении библиотеки DLL см. ниже.)

  • Два DEF-файла используются для экспорта точек входа MFC DLL для версий библиотеки DLL Debug ( MFCxxD.DEF ) и Release ( MFCxx.DEF ).

  • RC-файл ( MFCDLL.RC ) содержит все стандартные ресурсы MFC и VERSIONINFO ресурс для библиотеки DLL.

  • Предоставлен файл КЛВ ( MFCDLL.CLW ), позволяющий просматривать классы MFC с помощью ClassWizard. Эта функция не относится к версии DLL MFC.

Управление памятью

Приложение MFCxx.DLL , использующее, использует общий распределитель памяти, предоставляемый MSVCRTxx.DLL средой, общей библиотекой времени выполнения C. Приложение, любые библиотеки DLL расширения MFC, а также библиотеки DLL MFC используют этот механизм распределения общей памяти. Используя общую библиотеку DLL для выделения памяти, библиотеки DLL MFC могут выделить память, которая позже освобождается приложением, или наоборот. Так как приложение и библиотека DLL должны использовать один и тот же распределитель, не следует переопределять глобальное operator new или operator delete . Те же правила применяются к остальным частям подпрограммы выделения памяти среды выполнения C (например malloc , reallocfree , и др.).

Порядковые номера и __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 изменит имя библиотеки. MFC30.DLLТо есть это распространяемая DLL-библиотека, содержащая версию 3,0 библиотеки классов MFC. При обновлении этой библиотеки DLL, скажем, в гипотетическом MFC 3,1, вместо этого будет называться MFC31.DLL Библиотека DLL. Опять же, если изменить исходный код MFC для создания пользовательской версии библиотеки DLL MFC, используйте другое имя (и, желательно, если имя не "MFC" в имени).

См. также раздел

Технические примечания по номеру
Технические примечания по категориям