Поделиться через


TN011: использование MFC в составе библиотеки DLL

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

Интерфейсы DLL

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

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

Обычные библиотеки DLL MFC имеют несколько преимуществ:

  • Приложение, использующее библиотеку DLL, не должно использовать MFC и не должно быть приложением Visual C++.

  • При использовании обычных библиотек DLL MFC, которые статически связываются с MFC, размер библиотеки DLL зависит только от подпрограмм среды выполнения MFC и C, используемых и связанных.

  • При использовании обычных библиотек DLL MFC, которые динамически связываются с MFC, экономия памяти от использования общей версии MFC может быть значительной. Однако необходимо распространить общие DLL, версии Mfc<version>.dll и Msvvcrt<version>.dll, с вашим DLL.

  • Проект DLL не зависит от того, как реализуются классы. Проект DLL экспортирует только те API, которые вы хотите. В результате, если реализация изменяется, обычные библиотеки DLL MFC по-прежнему действительны.

  • При использовании обычных библиотек DLL MFC, которые статически связываются с MFC, если библиотека DLL и приложение используют MFC, нет проблем с приложением, которое хочет другой версии MFC, чем библиотека DLL или наоборот. Так как библиотека MFC статически связана с каждой библиотекой DLL или EXE, нет никаких вопросов о какой версии у вас есть.

Ограничения API

Некоторые функции MFC не применяются к версии DLL из-за технических ограничений или из-за того, что эти службы обычно предоставляются приложением. С текущей версией MFC единственная функция, которая не применима, это CWinApp::SetDialogBkColor.

Создание библиотеки DLL

При компиляции обычных библиотек DLL MFC, которые статически связываются с MFC, символы _USRDLL и _WINDLL должны быть определены. Код DLL также должен быть скомпилирован со следующими коммутаторами компилятора:

  • /D_WINDLL указывает, что компиляция для библиотеки DLL

  • /D_USRDLL указывает, что вы создаете обычную библиотеку DLL MFC

Необходимо также определить эти символы и использовать эти параметры компилятора при компиляции обычных библиотек DLL MFC, которые динамически связываются с MFC. Кроме того, символ _AFXDLL должен быть определен, а код библиотеки DLL должен быть скомпилирован с помощью:

  • /D_AFXDLL указывает, что вы создаете обычную библиотеку DLL MFC, которая динамически связывается с MFC

Интерфейсы (API) между приложением и библиотекой DLL должны быть явно экспортированы. Мы рекомендуем определить интерфейсы для низкой пропускной способности и использовать только интерфейсы C, если это возможно. Интерфейсы Direct C проще поддерживать, чем более сложные классы C++.

Поместите API-интерфейсы в отдельный заголовок, который можно включить в файлы C и C++. См. заголовок ScreenCap.h в примере библиотеки DLLScreenCap mFC Advanced Concepts. Чтобы экспортировать функции, введите их в EXPORTS разделе файла определения модуля (). DEF) или включить __declspec(dllexport) в определения функций. Используется __declspec(dllimport) для импорта этих функций в исполняемый файл клиента.

Необходимо добавить макрос AFX_MANAGE_STATE в начале всех экспортированных функций в обычных библиотеках DLL MFC, которые динамически связываются с MFC. Этот макрос устанавливает текущее состояние модуля на соответствующее для библиотеки DLL. Чтобы использовать этот макрос, добавьте следующую строку кода в начало функций, экспортированных из библиотеки DLL:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

WinMain —> DllMain

Библиотека MFC определяет стандартную точку входа Win32 DllMain , которая инициализирует производный объект CWinApp , как в обычном приложении MFC. Поместите все инициализации библиотеки DLL в метод InitInstance, как это делается в обычном приложении MFC.

Обратите внимание, что механизм CWinApp::Run не применяется к библиотеке DLL, так как приложение владеет основным насосом сообщений. Если ваша DLL отображает немодальные диалоговые окна или имеет собственное окно основного фрейма, основной цикл обработки сообщений вашего приложения должен вызывать процедуру, экспортируемую DLL, которая вызывает CWinApp::PreTranslateMessage.

См. пример DLLScreenCap для использования этой функции.

Функция DllMain, которую предоставляет MFC, вызовет метод CWinApp::ExitInstance класса, производного от CWinApp, до того, как библиотека DLL выгрузится.

Связывание библиотеки DLL

С помощью обычных библиотек DLL MFC, которые статически связываются с MFC, необходимо связать вашу DLL с библиотеками Nafxcwd.lib или Nafxcw.lib, а также с версией среды выполнения C, названной Libcmt.lib. Эти библиотеки предварительно созданы и могут быть установлены путем указания их при запуске программы установки Visual C++.

Пример кода

Полный пример программы DLLScreenCap см. в примере программы MFC Advanced Concepts. Ниже приведены некоторые интересные моменты, которые следует отметить в этом примере:

  • Флаги компилятора библиотеки DLL и приложения отличаются.

  • Строки ссылок и файлы .DEF для библиотеки DLL отличаются от тех, что используются для приложения.

  • Приложение, использующее библиотеку DLL, не должно находиться в C++.

  • Интерфейс между приложением и библиотекой DLL — это API, доступный для использования C или C++ и экспортируемый с помощью DLLScreenCap.def.

В следующем примере показан интерфейс API, определенный в обычной библиотеке MFC DLL, которая статически связывается с MFC. В этом примере объявление заключено в блок extern "C" { } для пользователей C++. Это имеет несколько преимуществ. Во-первых, это делает api DLL доступными для клиентских приложений, отличных от C++. Во-вторых, это снижает нагрузку на библиотеку DLL, так как к экспортируемому имени не применяется модификация имен C++. Наконец, это упрощает явное добавление в файл .DEF (для экспорта по порядковому номеру) без необходимости беспокоиться об искажении имен.

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */

struct TracerData
{
    BOOL bEnabled;
    UINT flags;
};

BOOL PromptTraceFlags(TracerData FAR* lpData);

#ifdef __cplusplus
}
#endif

Структуры, используемые API, не являются производными от классов MFC и определены в заголовке API. Это снижает сложность интерфейса между библиотекой DLL и приложением и делает библиотеку DLL для использования программами C.

См. также

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