15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту

E:\Program Files\Apache Group\Apache\users\sources\mastak\cpp\cpp_winsock_inside

Создание DLL в MFC для начинающих, Часть 1


Автор: Andrew Fenster.

На сегодняшний день существует множество оснований для использования .DLL. Самое важное из них, это возможность быстро и без лишних затруднений обновлять приложение в процессе его использования, ведь достаточно поместить часть кода, который предполагается совершенствовать, в .DLL, а дальше просто предлагать пользователям скачивать эту .DLL не переустанавливая при этом приложение.

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

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

Различные типы .DLL

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

MFC extension .DLL

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

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

MFC extension .DLL обычно маленькие по размерам и очень быстро загружаются.

Regular DLL

Как уже упоминалось выше, MFC extension .DLL может использоваться приложениями созданными в MFC. Поэтому, если Вы хотите, чтобы Ваша DLL могла использоваться широким кругом приложений Win32, то необходимо создать регулярную DLL. Недостаток такой DLL заключается в том, что можно экспортировать только функции C-style. Т.е. не получится экспортировать классы, функции C++, а так же перезагруженные функции. Ваши функции не смогут использовать типы данных MFC, как в параметрах, так и в возвращаемых значениях. Конечно же в такую DLL можно запихнуть функции C++ и MFC, но внешние приложения смогут использовать только C-style функции.

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

Создание DLL

Создать DLL на основе MFC можно при помощи Визарда (App Wizard). Выберите в меню "File | New". На закладке "Projects" выберите "MFC AppWizard (.DLL)." Наберите имя Вашего проекта и нажмите "OK". На следующем окошке будет предложено выбрать различные типы DLL. Выберите необходимы Вам тип и нажмите "Finish".

После этого визард создаст пустую DLL, которая естевственно не будет ничего делать. Теперь Вам необходимо проделать следующее:

  1. добавить необходимые функции в DLL;
  2. изменить Ваше приложение так, чтобы оно могло работать с DLL.

Экспорт класса

Как упоминалось выше, только MFC extension .DLL может экспортировать MFC / C++ класс. Итак, для того, чтобы добавить класс в проект достаточно подключить уже готовые файлы .cpp и .h либо создать класс заново. Для экспорта класса необходимо добавить к объявлению класса макрокоманду "AFX_EXT_CLASS", например так:

     class AFX_EXT_CLASS CMyClass
     {
          //здесь идёт объявление класса
     };

Конечно же существуют и другие способы экспортирования класса, но данный самый простой. Далее мы рассмотрим, что нужно сделать в приложении, чтобы оно могло использовать экспортированный класс.

Экспортирование объектов и переменных

Вместо экспорта целого класса, иногда достаточно создать объект класса и экспортировать его. При этом приложение сможет вызывать все public функции экспортированного объекта, а так же иметь доступ к переменным, объявленным как public.

Для начала необходимо создать файл .h, в котором объявляется Ваш класс, затем создать файл .cpp, который будет содержать методы и переменные класса. В конце файла .cpp создаётся экземпляр самого класса:

     _declspec(dllexport) CMyClass myObject;

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

Этот же принцип можно использовать и для экспорта переменных:

     _declspec(dllexport) int x;

Важно помнить, что экспортировать объекты и переменные необходимо с глобальной видимостью. Например, следующий код не будет работать:

     MyFunction( )
     {
          _declspec(dllexport) CMyClass myObject;
          _declspec(dllexport) int x;
     }

Так как объект и переменная прекращают своё существование за пределами функции.

Экспорт функции

Экспорт функций подобен экспорту объектов и переменных. Опять же достаточно добавить макрокоманду "_declspec(dllexport)" в начало прототипа Вашей функции:

     _declspec(dllexport) int MyExportedFunction(int);

Не забывайте, что только MFC extension .DLL могут экспортировать функции C++ или функции с типами данных MFC в качестве параметров или в качестве возвращаемых величин. Regular .DLLs могут экспортировать функции только C-style.

Использование .DLL в приложении

Как извесно, .DLL не может запускаться сама по себе. Необходимо приложение, которое будет загружать и использовать её интерфейс. Так или иначе, создание такого приложения для Вас не составит труда.

При компиляции .DLL, создаётся два важных файла - .DLL и .lib. Вашему приложению необходимы оба этих файла. Вы должны скопировать их в директорию, содержащую Ваш проект. Обратите внимание, что файлы .DLL и .lib, созданные в отладочной версии (Debug) отличаются от тех, которые будут скомпилированы в релизовой версии (Release). Это значит, что если Вы компилируете приложение в Debug, то и файлы .DLL и .lib должны быть созданы в Debug. Это утвержение справедливо и для Release версии. Простой способ избежать путаницы в данном случае - это поместить отладочные файлы .DLL и .lib в папку Debug, а релизные в папку Release.

Итак, в заголовочном файле Вашего приложения необходимо будет объявить импортирование классов, функций, объектов и переменных. При создании DLL мы пользовались макрокомандой "_declspec(dllexport)", теперь же нам прийдётся воспользоваться командой "_declspec(dllimport)". Вот как будет выглядеть объявления для класса, переменной и функции:

     _declspec(dllimport) CMyClass myObject;
     _declspec(dllimport) int x;
     _declspec(dllimport) int MyExportedFunction(int);

Чтобы часто не набирать эту макрокоманду, можно воспользоваться макросом:

     #define DLLIMPORT _declspec(dllimport)
     
     DLLIMPORT CMyClass myObject;
     DLLIMPORT int x;
     DLLIMPORT int MyExportedFunction(int);

После того, как объект, переменная и функция объявлены в заголовочном файле Вашего приложения, они готовы к использованию.

Для импорта всего класса, необходимо скопировать весь заголовочный файл .h. При этом .DLL и приложение будут иметь идентичные заголовочные файлы для экспортированного класса. Не забудьте при объявлении класса добавить макрос AFX_EXT_CLASS.

После того, как Вы создали приложение, то конечному пользователю можно отдать исполняемый файл Release версии и .DLL тоже Release версии. При этом необходимость в файле .lib отпадает. DLL может располагаться в одной директории с исполняемым файлом приложения либо в системной директории Windows.

Недостатки DLL.

Появление на свет COM и ATL говорит о двух серьёзных недостатках DLL. Во первых, .DLL, созданная в одной версии компилятора не может использоваться приложением, созданным в другой версии компилятора. Во вторых, когда Вы изменяете .DLL, то существует вероятность, что прийдётся перекомпилировать и приложение использующее эту DLL, даже если в приложение не вносилось никаких изменений.

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