Как создать dll файл
Как создать dll файл
Создаем и подключаем DLL
При диамической загрузке вы можете загружать DLL при необходимости, выгрузить ее когода она ненужна. Однако работать с такими библиотеками сложнее чем со статическими. Рассмотрим созздание и использование DLL статической загрузки.
Статическая загрузка
Создадим сперва проект (File / New / DLL). Будет создан проект, содержащий следующее:
и длинный коментарий предупреждающий вас о том, что для работо способности вашей DLL необходимо снеи обеспечить поствку некоторых dll если вы используете экземпляры класса String.
Для экспорта и импорта из DLL необходимо использовать моди фикаторы __export и __import соответсвенно. Но в C++ Builder можно использовать новое ключевое слово __delspec() с параметрами dllexport и dllimport соответсвенно. Сами понимаете, что для того чтобы эспортировать функции из библиотеки еужен один заголовочный файл с описаниями _delspec(dllexport) для экспортируемых функций, для импорта функций в приложение вам необходимо будет поставить анологичный заголовочный файл но с _delspec(dllimport) описаниями, что достаточно неудобно. Эта проблема решается легко: добавте в заголовочный файл библиотеки следующее:
в исходном файле DLL напишите #define BUILD_DLL, а вместо __declspec(dllexport) пишите DLL_EXP. При написании программы добавте строчку #define BUILD_APP, и просто подключите заголовочный файл DLL.
Пример DLL: файл P.cpp
Если вы нажмете Run то после завершенния построения будет выдано сообщение что данная программа не можнт быть исполнена (естественно). Теперь напишем вызывающую программу. Втомже каталоге создайде новый проект (File / New Application) в форму поместите одну кнопку и создай обработчик события OnClick. Ваш исполняемый файл должен представлять собой слдующее:
Динамическая загрузка
Давайте рассмотрим на примере, как производится динамическая загрузка. Создайте новый прокт DLL и внесите в него следующее:
Cкомпилируйте проект, в результате чего будет создана DLL.
Теперь создайте проект приложения анологичный проекту для использования статической загрузки (форма с кнопкой и обработчиком события кнопки OnClick) ниже приведен код приложения:(Unit11.cpp)
запустите это проект, при нажатии на кнопку должно выдаватся сообшение. Теперь разберемся, как это работает.
Обратите внимание на то, что призагрузке можно указать точное местоположние библиотеки (необезательно в том же каталоге где и приложение).
Статья Как создать dll библиотеку?
Cоздание файла dll
Очень часто в своей работе, Вы будете сталкиваться с такой ситуацией.
Перед вами стоит задача, нужно написать программу » Супер Блокнот» которая должна сохранить все функции стандартного блокнота, но при этом иметь ряд каких-то дополнительных функций, благодаря которым, при выборе программы для работы с текстом, пользователь будет отдавать предпочтение именно вашей программе. Для этого было решено добавить несколько новых функций, одна из них, будет отвечать за подсчет и вывод количества слов в тексте.
Через пару недель программа была написана, затем она попала в Интернет, пользователи оценили новый продукт и стали им пользоваться. Цель достигнута.
Проходит время и перед вами ставят новую задачу, написать программу «Супер парсер«. Одной из функции данной программы, будет подсчет слов в тексте. Вы понимаете, что снова придется разрабатывать метод, который будет вести подсчёт слов. Но, при этом вспоминаете, что совсем не давно уже разрабатывали программу, в которой применялась данная функция. Чтобы не изобретать велосипед, Вы открываете исходник программы «Супер блокнот«; и копируете весь метод в исходник новой программы «Супер парсер«. Отлично, теперь Вам не придется тратить время на написание этого метода заново, и Вы можете посветить больше времени остальным элементам программы. Задача решена.
Но, что если метод по подсчету слов, писали не Вы, а допустим, какой-нибудь коллега по работе и по каким-то причинам, Вы не можете получить доступ к исходному коду программы «Супер блокнот«. То есть первый вариант, копирование метода из исходника, не прокатит и данный метод придется писать самому ммм, печалька.
Но, тут вам звонит ваш коллега по работе и говорит: Ты знаешь, я тут вспомнил, когда я разрабатывал данный метод, я подумал, что возможно мне придется его использовать ещё где-то, и по этому я решил вынести его в отдельную сборку, в виде файла динамической библиотеки (dll).Ты просто скопируй этот файл dll в свой проект, и подключи его, как внешнюю сборку, после чего ты получишь доступ к моему методу и сможешь им пользоваться.
Отлично! Вы проделываете все описанные действия, в программе “Супер парсер” появляется нужный метод, задача решена и вам вновь не пришлось повторно писать код руками.
На этом присказка закончена и теперь переходим к более подробному изучению.
Как уже писал ранее, динамические библиотеки, позволяют повторно использовать ранее написанный код, а так же они обеспечивают лучшую переносимость кода. Достаточно, скинуть файл на флешку, или скачать dll файл из Интернета, после чего добавить его в текущий проект и тут же получить набор разных дополнительных функций для вашего приложения. Так же стоит знать, что в одном dll файле может храниться любое количество типов, членов и пространств имён.
Создание файла dll
Для начала выберем тип нового создаваемого приложения, а точнее проекта.
Выбираем Class Library, то есть создаем файл динамической библиотеки (dll)
Так же Вы можете указать, под какую версию Фреймворка будет создаваться данный проект.
После того, как Visual Studio создаст каркас проекта, Вы увидите следующее:
Так будет выглядеть окно Solution Explorer
А так будет выглядеть рабочая область, где Вы обычно пишите код программы
И так дано пространство имён: Car и класс: Class1. Class1 не удачное название, давайте немного изменим наш код, заменив Class1 на BMW, и добавим метод, который будет выводить имя нашего класса.
И так код написан, и теперь необходимо выполнить компиляцию, чтобы получить сборку.
Если сейчас попытаться нажать F5 или Ctrl+F5, то вы увидите данное окно
Данная ошибка, говорит лишь о том, что был создан файл динамической библиотеки (dll), а не исполняемый файл (exe), который не может быть запущен.
Для того, чтобы скомпилировать проект, нажмите клавишу F6, после чего в директории bin\Debug появиться файл Car.dll.
Чтобы проверить был ли создан файл библиотеки, воспользуйтесь кнопкой Show All Files на вкладке Solution Explorer
Сборка, в виде файла динамической библиотеки успешно создана.
Теперь перейдем в папку bin\Debug, для того, чтобы быстро переместиться в текущую директорию проекта, в том же Solution Explorer воспользуйтесь пунктом Open Folder in Windows Explorer
После чего закроем текущий проект и создадим новый. Но, на этот раз выберем тип проекта — консольное приложение.
Создаем новый проект.
Новый проект создан. Теперь подключим в текущий проект, нашу библиотеку (Car.dll)
Для этого на папке References, в окне Solution Explorer нужно нажать правую кнопку мыши и выбрать пункт Add Reference, откроется вот такое окно:
В окне Object Browser можно посмотреть содержимое нашей сборки.
Сборка подключена и теперь Вы можете работать с её содержимым. Далее выполним необязательный пункт.
Добавим, с помощью ключевого слова using пространство имен Car из созданной нами библиотеки Car.dll, после чего создадим объект класса BMW и выполним метод Вывести_Имя_Класса().
И в конце не много информации о типах сборок:
Сборки бывают следующих основных видов: общие и частные.
Частная сборка (private assembly)
Это файлы библиотек, как наш ранее созданный файл Car.dll, которые содержаться на протяжении всего времени в каталоге текущего приложения или любом из его подкаталогов.
Вернёмся к началу статьи.
После того, как было создано приложение “Супер парсер”, мы получили сборку в виде файла (exe). Затем мы решили протестировать нашу программу и отдаём её нашему другу, при этом Вы так же упоминаете, что если он хочет иметь дополнительные функции в программе, то ему нужно просто рядом с его exe файлом поместить файл библиотеки Car.dll. После чего он получит возможность подсчёта слов в тексте. То есть библиотека будет храниться в той же директории, что и исполняемый файл.
Общие сборки (shared assembly)
Это сборки, предназначенные для множественного использования разными приложениями, установленными на одном компьютере.
Разработка библиотек DLL
Область применения: Excel 2013 | Office 2013 | Visual Studio
Библиотека — это скомпилированный код, который обеспечивает функции исполняемого приложения и предоставляет ему данные. Библиотеки могут быть связаны статически и динамически, обычно у них расширение LIB и DLL соответственно. Статические библиотеки (например, библиотека времени выполнения C) связываются с приложением во время компиляции и становятся частью полученного исполняемого файла. Приложение загружает библиотеку DLL, когда она необходима (обычно при запуске). Одна библиотека DLL может загружать другую библиотеку DLL и динамически ссылаться на нее.
Преимущества библиотек DLL
Ниже приведены основные преимущества библиотек DLL.
С помощью DLL можно добавлять функции и команды в Microsoft Excel.
Ресурсы для создания библиотек DLL
Вот что нужно, чтобы создать библиотеку DLL:
В современных интегрированных средах разработки (IDE), например Microsoft Visual Studio, есть все эти компоненты, а также смарт-редакторы, инструменты для отладки кода и управления несколькими проектами, мастера создания проектов и другие важные инструменты.
Вы можете создавать библиотеки DLL на нескольких языках, например C/C++, Pascal и Visual Basic. Так как исходный код API Excel — C и C++, в этой документации рассматриваются только эти два языка.
Экспорт функций и команд
При компиляции проекта DLL компилятор и компоновщик должны знать, какие функции экспортировать, чтобы предоставить к ним доступ в приложении. В этом разделе описаны возможные способы.
При компиляции исходного кода компиляторы обычно изменяют имена функций. Для этого они обычно добавляют символы в начало или конец имени. Этот процесс называется декорирование имени. Необходимо убедиться, что приложение, загружающее библиотеку DLL, может распознать имя экспортируемой функции. Для этого может потребоваться дать указание компоновщику сопоставить расширенное имя с более простым именем экспорта. Это может быть имя из исходного кода или другое.
Способ декорирования имени зависит от языка и способа вызова функции, задаваемого компилятором, т. е. соглашения о вызовах. Стандартное межпроцессное соглашение о вызовах для Windows, используемое в библиотеках DLL, — это WinAPI. В файлах заголовков Windows оно обозначено как WINAPI с помощью декларатора Win32 __stdcall.
Функция, экспортируемая в DLL-файл для Excel (функция листа, функция, эквивалентная листу макросов, или пользовательская команда) всегда должна использовать соглашение о вызовах WINAPI / __stdcall. В определение функции необходимо явно включить указатель WINAPI, так как по умолчанию в компиляторах Win32 используется соглашение о вызовах __cdecl, также обозначенное как WINAPIV.
Вы можете сообщить компоновщику о необходимости экспорта функции, а также ее внешнее имя несколькими способами:
В проекте можно использовать все три способа, они поддерживаются и компилятором, и компоновщиком, но не следует экспортировать одну функцию более чем одним способом. Например, предположим, что библиотека DLL содержит два модуля исходного кода, C и C++, которые содержат две функции для экспорта — my_C_export и my_Cpp_export соответственно. Для простоты предположим, что функции принимают один числовой аргумент двойной точности и возвращают данные того же типа. В следующих разделах описываются варианты экспорта функций с помощью каждого из этих методов.
С помощью DEF-файла
DEF-файл должен содержать следующие строки.
EXPORTS my_C_export = _my_C_export@8 my_Cpp_export
Ниже приведен общий синтаксис строки, следующей за оператором EXPORTS.
entryname[=internalname] [@ordinal[NONAME]] [DATA] [PRIVATE]
Обратите внимание, что функция C декорирована, но в DEF-файле компоновщику явно дано указание предоставлять эту функцию, используя имя из исходного кода (в данном примере). Компоновщик неявно экспортирует функцию C++, используя имя исходного кода, так что в DEF-файл необязательно включать расширенное имя.
Соглашение для вызовов функций API 32-разрядной Windows предусматривает следующее декорирование функций C: function_name становится function_name@ n, где n — количество байт, выраженное в виде десятичного числа, принимаемого всеми аргументами, при этом количество байт для каждого округляется до ближайшего числа, кратного четырем.
Размер всех указателей в Win32 — четыре байта. Тип возвращаемого значения не влияет на декорирование имени.
Чтобы компилятор C++ предоставлял функции, используя нерасширенные имена, заключите функцию и ее прототипы во внешний блок «C» <…>, как показано в приведенном ниже примере. (Фигурные скобки <> здесь опущены, так как объявление относится только к блоку кода функции, расположенному непосредственно за ним.)
При размещении прототипов функции C в файлах заголовков, которые могут быть включены в исходные файлы C или C++, необходимо включить следующую директиву препроцессора.
С помощью декларатора __declspec(dllexport)
Ключевое слово __declspec(dllexport) можно использовать в объявлении функции указанным ниже образом.
Ключевое слово __declspec(dllexport) необходимо добавить в крайней левой части объявления. Преимущества этого подхода: функцию не нужно указывать в DEF-файле, а состояние экспорта указывается вместе с определением.
Если вы не хотите декорировать имя функции C++, необходимо объявить функцию следующим образом.
Компоновщик сделает функцию доступной под именем my_undecorated_Cpp_export, то есть именем из исходного кода, без декорирования.
С помощью директивы компоновщика препроцессора #pragma
Последние версии Microsoft Visual Studio поддерживают два предопределенных макроса, которые при использовании с директивой #pragma позволяют давать указание компоновщику экспортировать функции непосредственно в коде функции. Макросы FUNCTION и FUNCDNAME (обратите внимание на два символа подчеркивания с каждой стороны) используются с недекорированными и декорированными именами функций соответственно.
Например, при использовании Microsoft Visual Studio эти строки можно включить в общий файл заголовка, как показано ниже.
Если такой заголовок включен в исходные файлы, две функции, приведенные в качестве примера, можно экспортировать следующим образом.
Обратите внимание, что директиву необходимо поместить в код функции. Ее можно развернуть, только если параметры /EP и /P компилятора не заданы. При использовании этого способа исчезает необходимость в DEF-файле и объявлении __declspec(dllexport), а спецификация состояния экспорта указывается в коде функции.
Создание библиотек DLL на C и C++ в Visual Studio
В Windows библиотека динамической компоновки (DLL) является исполняемым файлом, который выступает в качестве общей библиотеки функций и ресурсов. Динамическая компоновка — это возможность операционной системы. Она позволяет исполняемому файлу вызывать функции или использовать ресурсы, хранящиеся в отдельном файле. Эти функции и ресурсы можно компилировать и развертывать отдельно от использующих их исполняемых файлов.
Библиотека DLL не является отдельным исполняемым файлом. Библиотеки DLL выполняются в контексте приложений, которые их вызывают. Операционная система загружает библиотеку DLL в область памяти приложения. Это делается либо при загрузке приложения (неявная компоновка), либо по запросу во время выполнения (явная компоновка). Библиотеки DLL также упрощают совместное использование функций и ресурсов различными исполняемыми файлами. Несколько приложений могут осуществлять одновременный доступ к содержимому одной копии библиотеки DLL в памяти.
Различия между динамической и статической компоновкой
При статической компоновке весь код объектов копируется из статической библиотеки в использующие их исполняемые файлы во время сборки. При динамической компоновке включаются только те сведения, которые позволяют Windows найти и загрузить библиотеку DLL, содержащую элемент данных или функцию, во время выполнения. При создании библиотеки DLL также создается библиотека импорта, содержащая эту информацию. При сборке исполняемого файла, который вызывает библиотеку DLL, компоновщик использует экспортированные символы в библиотеке импорта, чтобы сохранить эти сведения для загрузчика Windows. Когда загрузчик загружает библиотеку DLL, она сопоставляется с областью памяти приложения. Для выполнения операций инициализации, необходимых библиотеке DLL, вызывается специальная функция DllMain из библиотеки DLL (если она имеется).
Различия между приложениями и библиотеками DLL
Хотя и библиотеки DLL, и приложения являются исполняемыми модулями, они отличаются некоторыми особенностями. Наиболее очевидное различие заключается в том, что библиотеку DLL нельзя запустить. С точки зрения системы, между приложениями и библиотеками DLL имеется два существенных различия.
В системе может одновременно выполняться несколько экземпляров приложения. Экземпляр библиотеки DLL может быть только один.
Приложение может загружаться как процесс. Ему могут принадлежать такие компоненты, как стек, потоки выполнения, глобальная память, дескрипторы файлов и очередь сообщений. У библиотеки DLL таких компонентов быть не может.
Преимущества использования библиотек DLL
Динамическая компоновка кода и ресурсов имеет некоторые преимущества над статической.
Динамическая компоновка экономит память и сокращает подкачку. Многие процессы могут использовать библиотеку DLL совместно, одновременно обращаясь к одной доступной только для чтения копии ее частей в памяти. В отличие от этого, каждое приложение, созданное с помощью библиотеки статической компоновки, имеет полную копию кода библиотеки, которую система Windows должна загрузить в память.
Динамическая компоновка экономит место на диске и пропускную способность. Несколько приложений могут совместно использовать одну копию библиотеки DLL на диске. В отличие от этого, каждое приложение, созданное с помощью библиотеки статической компоновки, имеет код библиотеки, связанный с исполняемым образом. Это увеличивает занимаемое на диске место и используемую для передачи данных пропускную способность.
Обслуживание, применение исправлений для системы безопасности и обновление могут быть проще. Если приложения используют общие функции в библиотеке DLL, можно реализовать исправления ошибок и развернуть обновления для нее. При обновлении библиотек DLL использующие их приложения не нужно перекомпилировать или повторно компоновать. Они могут использовать новые библиотеки DLL сразу после их развертывания. В отличие от этого, при внесении исправлений в код статически скомпонованного объекта необходимо повторно скомпоновать и развернуть каждое использующее его приложение.
С помощью библиотек DLL можно оказывать послепродажную поддержку. Например, библиотеку DLL драйвера дисплея можно изменить так, чтобы она поддерживала дисплей, который не был доступен на момент предоставления приложения.
С помощью явной компоновки можно обнаруживать и загружать библиотеки DLL во время выполнения. Например, это могут быть расширения приложения, которые добавляют новые функциональные возможности без повторной сборки и развертывания приложения.
Динамическая компоновка упрощает поддержку приложений, написанных на разных языках программирования. Программы, написанные на разных языках программирования, могут вызывать одну и ту же функцию в библиотеке DLL при условии соблюдения соглашения о ее вызове. Программы и функция в библиотеке DLL должны отвечать следующим требованиям к совместимости: ожидаемый функцией порядок передачи аргументов в стек; выполнение очистки стека функцией или приложением; передача аргументов в регистрах.
Динамическая компоновка обеспечивает механизм для расширения классов библиотеки Microsoft Foundation Classes (MFC). На основе существующих классов MFC можно создавать производные классы и помещать их в библиотеку расширения DLL, используемую приложениями MFC.
Динамическая компоновка упрощает создание международных версий приложения. Библиотеки DLL — это удобный способ предоставления ресурсов для конкретных языковых стандартов, благодаря чему значительно упрощается создание международных версий приложения. Вместо предоставления множества локализованных версий приложения можно поместить строки и изображения для каждого языка в отдельную библиотеку DLL ресурсов. Затем приложение может загружать ресурсы для нужного языкового стандарта во время выполнения.
Возможным недостатком использования библиотек DLL является то, что приложения не являются автономными. Они требуют наличия отдельного модуля DLL, которое должно проверяться в процессе установки.
Дополнительные сведения о создании и использовании библиотек DLL
В приведенных ниже статьях приводятся подробные сведения о создании библиотек DLL на C и C++ в Visual Studio.
Пошаговое руководство. Создание и использование библиотеки DLL (C++)
Описывает создание и использование библиотек DLL при помощи Visual Studio.
Типы библиотек DLL
Предоставляет сведения о различных типах библиотек DLL, которые доступны для сборки.
Вопросы и ответы по библиотекам DLL
Ответы на часто задаваемые вопросы о библиотеках DLL.
Связывание исполняемого файла с библиотекой DLL
Описание явного и неявного соединения с библиотекой DLL.
Инициализация библиотеки DLL
Описывается код инициализации библиотеки DLL, который должен выполняться при загрузке библиотеки DLL.
Библиотеки DLL и поведение библиотеки времени выполнения Visual C++
Описывается последовательность запуска библиотеки DLL средой выполнения.
Функции LoadLibrary и AfxLoadLibrary
Описывается использование функций LoadLibrary и AfxLoadLibrary для явной связи с библиотекой DLL во время выполнения.
Функция GetProcAddress
Описывается использование GetProcAddress для получения адреса экспортированной функции в DLL.
Порядок поиска библиотеки динамической компоновки (DLL)
Описание пути поиска, который операционная система Windows использует для поиска библиотеки DLL в системе.
Состояния модулей обычной библиотеки DLL MFC, динамически связанной с MFC
Описываются состояния модулей обычной библиотеки DLL, динамически связываемой с MFC.
Библиотеки DLL расширений MFC
Описываются библиотеки DLL, которые обычно реализуют классы многократного использования, производные от существующих классов MFC.
Создание библиотек DLL, содержащих только ресурсы
Библиотека DLL, содержащая только ресурсы, например значки, растровые изображения, строки и диалоговые окна.
Локализованные ресурсы в приложениях MFC: вспомогательные библиотеки DLL
Расширенная поддержка библиотек спутниковой связи DLL и содержит возможность, которая позволяет создавать приложения, локализированные на различные языки.
Импорт и экспорт
Импортирование открытых символов в приложение или экспортирование функций из библиотеки DLL
Технология Active и библиотеки DLL
Размещение серверов объектов внутри библиотеки DLL.
Автоматизация в библиотеке DLL
Параметр автоматизации в решениях мастера библиотек DLL MFC.
Соглашения об именовании библиотек DLL MFC
Способ встраивания библиотек DLL в MFC, опираясь на четко структурированное соглашение об именовании.
Связанные разделы
Использование MFC как части библиотеки DLL
Описываются постоянные библиотеки DLL MFC, которые позволяют использовать библиотеку MFC как часть библиотеки динамической компоновки Windows.
Пошаговое руководство. Создание и использование собственной библиотеки динамической компоновки (C++)
В этом пошаговом руководстве показано, как с помощью интегрированной среды разработки (IDE) Visual Studio создать собственную библиотеку динамической компоновки (DLL), написанную на Microsoft C++ (MSVC). Далее в нем показано, как использовать библиотеку DLL из другого приложения C++. Библиотеки DLL (также называемые общими библиотеками в операционных системах на основе UNIX) являются одним из наиболее полезных компонентов Windows. Их можно использовать для обмена кодом и ресурсами, а также для уменьшения размера своих приложений. Библиотеки DLL могут упростить обслуживание и расширение приложений.
В этом пошаговом руководстве вы создадите библиотеку DLL, которая реализует некоторые математические функции. Затем вы создадите консольное приложение, использующее функции из библиотеки DLL. Попутно вы познакомитесь с некоторыми методами программирования и соглашениями, используемыми в библиотеках DLL для Windows.
В этом пошаговом руководстве рассматриваются следующие задачи:
Создание проекта библиотеки DLL в Visual Studio.
Добавление экспортированных функций и переменных в библиотеку DLL.
Создание проекта «Консольное приложение» в Visual Studio.
Использование в консольном приложении функций и переменных, импортированных из библиотеки DLL.
Запуск готового приложения.
Как и в случае со статически связанной библиотекой, библиотека DLL экспортирует переменные, функции и ресурсы по имени. Клиентское приложение импортирует имена для использования этих переменных, функций и ресурсов. В отличие от статически компонуемой библиотеки, Windows соединяет импорт в вашем приложении с экспортом в библиотеку DLL во время загрузки или выполнения, а не во время компоновки. Для выполнения этих подключений Windows требуются дополнительные сведения, которые не являются частью стандартной модели компиляции C++. Чтобы предоставить эти дополнительные сведения, компилятор MSVC реализует некоторые специальные расширения Майкрософт для C++. Мы рассмотрим эти расширения далее.
В этом пошаговом руководстве создаются два решения Visual Studio. Первое решение создает библиотеку DLL, а второе — клиентское приложение. Библиотека DLL использует соглашение о вызовах языка C. Ее можно вызывать из приложений, написанных на других языках программирования, при условии, что платформа, соглашения о вызовах и соглашения о связывании совпадают. Клиентское приложение использует неявную компоновку, в рамках которой Windows связывает приложение с библиотекой DLL во время загрузки. Эта компоновка позволяет приложению вызывать функции, предоставляемые библиотекой DLL, точно так же, как функции в библиотеке статической компоновки.
В этом пошаговом руководстве не рассматриваются некоторые общие ситуации. В нем не рассматривается использование библиотеки DLL для C++ другими языками. Он не показывает, как создавать DLL-библиотеки только для ресурсов или как использовать явную компоновку для загрузки библиотек DLL во время выполнения, а не во время загрузки. Уверяем вас, все это можно выполнять с помощью MSVC и Visual C++.
Несмотря на то что код библиотеки DLL написан на C++, мы использовали интерфейсы в стиле C для экспортированных функций. Существует две основные причины: во-первых, многие другие языки поддерживают импорт функций в стиле C. Клиентское приложение не обязательно должно быть записано на C++. Во-вторых, это позволяет избежать некоторых распространенных ошибок, связанных с экспортируемыми классами и функциями-членами. При экспорте классов легко выполнять диагностику ошибок, так как все, что упоминалось в объявлении класса, также должно иметь экземпляр, который также экспортирован. Это ограничение применяется к библиотекам DLL, но не к статическим библиотекам. Если классы имеют обычный старый стиль данных, эту проблему не следует выполнять.
Предварительные требования
Базовые значения об использовании интегрированной среды разработки Visual Studio. Если вы уже использовали классические приложения для Windows, вы, вероятно, справитесь. Общие сведения см. в обзоре возможностей интегрированной среды разработки Visual Studio.
Основные навыки владения языком C++. Не волнуйтесь, мы не будем делать ничего сложного.
В данном пошаговом руководстве подразумевается, что вы используете Visual Studio 2017 версии 15.9 или более поздней. В некоторых более ранних версиях Visual Studio 2017 имелись дефекты в шаблонах кода или использовались разные диалоговые окна пользовательского интерфейса. Чтобы избежать проблем, используйте Visual Studio Installer для обновления Visual Studio 2017 до версии 15.9 или более поздней.
Создание проекта библиотеки DLL
Этот набор задач позволяет создать проект для библиотеки DLL, добавить код и выполнить его сборку. Для начала запустите IDE Visual Studio и выполните вход, если это необходимо. Инструкции немного отличаются в зависимости от используемой версии Visual Studio. Убедитесь, что в элементе управления в верхнем левом углу этой страницы выбрана правильная версия.
Создание проекта библиотеки DLL в Visual Studio 2019
В строке меню выберите Файл>Создать>Проект, чтобы открыть диалоговое окно Создание проекта.
В верхней части диалогового окна задайте язык C++, задайте для платформыWindows и задайте для Project тип«Библиотека«.
На странице Настроить новый проект введите MathLibrary в поле Имя проекта. Примите заданные по умолчанию Расположение и Имя решения. Для параметра Решение задайте Создать новое решение. Снимите флажок Разместить решение и проект в одном каталоге, если он установлен.
Нажмите кнопку Создать, чтобы создать проект.
После создания решения созданный проект вместе с исходными файлами отобразится в окне обозревателя решений в Visual Studio.
Создание проекта библиотеки DLL в Visual Studio 2017
В строке меню выберите Файл >Создать>Проект, чтобы открыть диалоговое окно Новый проект.
Нажмите кнопку ОК, чтобы создать проект.
После создания решения созданный проект вместе с исходными файлами отобразится в окне обозревателя решений в Visual Studio.
Создание проекта библиотеки DLL в Visual Studio 2015 и более ранних версий
В строке меню выберите Файл>Создать>Проект.
Нажмите кнопку ОК, чтобы закрыть диалоговое окно Новый проект, и запустите мастер приложений Win32.
Нажмите кнопку Далее. На странице Параметры приложения в поле Тип приложения выберите пункт DLL.
Когда мастер завершит создание решения, вы сможете увидеть созданный проект вместе с исходными файлами в окне обозревателя решений в Visual Studio.
Пока эта библиотека DLL ничего не делает. Затем вы создадите файл заголовка для объявления функций, экспортируемых вашей библиотекой DLL, и добавите определения функций в библиотеку DLL, чтобы сделать ее более полезной.
Добавление файла заголовка в библиотеку DLL
Чтобы создать файл заголовка для функций, последовательно щелкните Проект>Добавить новый элемент.
Нажмите кнопку Добавить, чтобы создать пустой файл заголовка, который отображается в новом окне редактора.
Замените все содержимое этого файла заголовка следующим кодом:
Этот файл заголовка объявляет некоторые функции для создания обобщенной последовательности Фибоначчи, исходя из двух начальных значений. Вызов fibonacci_init(1, 1) создает знакомую последовательность чисел Фибоначчи.
Обратите внимание на операторы препроцессора в верхней части файла. Новый шаблон проекта для проекта DLL добавляется
MATHLIBRARY_EXPORTS При определении MATHLIBRARY_API макроса макрос задает __declspec(dllexport) модификатор для объявлений функций. Этот модификатор предписывает компилятору и компоновщику экспортировать функцию или переменную из библиотеки DLL для использования другими приложениями. Если MATHLIBRARY_EXPORTS значение не определено, например, если файл заголовка включен клиентским приложением, MATHLIBRARY_API применяет __declspec(dllimport) модификатор к объявлениям. Этот модификатор оптимизирует импорт функции или переменной в приложении. Дополнительные сведения см. в статье dllexport, dllimport.
Добавление реализации в библиотеку DLL
В обозревателе решений щелкните узел Файлы решения правой кнопкой мыши и выберите пункты Добавить > Новый элемент. Создайте новый CPP-файл с именем MathLibrary.cpp, аналогично добавлению нового файла заголовка на предыдущем шаге.
В окне редактора выберите вкладку MathLibrary.cpp, если она уже открыта. Если нет, то в обозревателе решений дважды щелкните файл MathLibrary.cpp в папке Исходные файлы проекта MathLibrary.
В редакторе замените содержимое файла MathLibrary.cpp следующим кодом:
В окне редактора выберите вкладку MathLibrary.cpp, если она уже открыта. Если нет, то в обозревателе решений дважды щелкните файл MathLibrary.cpp в папке Исходные файлы проекта MathLibrary.
В редакторе замените содержимое файла MathLibrary.cpp следующим кодом:
Чтобы убедиться, что все работает, скомпилируйте библиотеку динамической компоновки. Чтобы выполнить компиляцию, последовательно выберите Сборка>Собрать решение. Библиотека DLL и связанные выходные данные компилятора помещаются в папку с именем Debug непосредственно под папкой решения. При создании сборки выпуска выходные данные помещаются в папку с именем Release. Результат должен выглядеть следующим образом.
Поздравляем, вы создали библиотеку DLL с помощью Visual Studio! Далее вы создадите клиентское приложение, которое использует функции, экспортируемые из библиотеки DLL.
Создание клиентского приложения, которое использует библиотеку DLL
При создании библиотеки DLL подумайте о том, как клиентские приложения могут его использовать. Чтобы вызвать функции или получить доступ к данным, экспортированным библиотекой DLL, исходный код клиента должен иметь объявления, доступные во время компиляции. Во время компоновки компоновщику требуются сведения для разрешения вызовов функций или доступа к данным. Библиотека DLL предоставляет эти сведения в библиотеке импорта — файле, который содержит сведения о поиске функций и данных вместо фактического кода. Во время выполнения библиотека DLL должна быть доступна клиенту в месте, которое может найти операционная система.
Независимо от того, является ли он вашим или предоставлен сторонним разработчиком, вашему проекту клиентского приложения требуется несколько фрагментов информации для использования библиотеки DLL. Ему необходимо найти заголовки, в которых объявляются экспорты библиотеки DLL, библиотеки импорта для компоновщика и сама библиотека DLL. Одним из решений является копирование всех этих файлов в ваш клиентский проект. Для сторонних библиотек DLL, которые вряд ли изменятся во время разработки вашего клиента, этот метод может быть лучшим способом их использования. Однако, когда вы также создаете библиотеку DLL, лучше избегать дублирования. Если вы делаете локальную копию файлов библиотеки DLL, которые находятся в стадии разработки, вы можете случайно изменить файл заголовка только в одной копии или использовать устаревшую библиотеку.
Чтобы избежать рассинхронизации, мы рекомендуем вам установить путь включения в своем клиентском проекте, чтобы добавить файлы заголовков библиотеки DLL напрямую из проекта DLL. Кроме того, укажите путь к библиотеке в своем клиентском проекте, чтобы добавить библиотеки импорта DLL из проекта DLL. Наконец, скопируйте встроенную библиотеку DLL из проекта DLL в выходной каталог своей сборки клиента. Этот шаг позволяет вашему клиентскому приложению использовать тот же код библиотеки DLL, который вы создали.
Создание клиентского приложения в Visual Studio
В строке меню выберите FileNew> >Project чтобы открыть диалоговое окно «Создание проекта«.
В верхней части диалогового окна задайте для параметра Язык значение C++, для параметра Платформа значение Windows, а для Типа проекта — Консоль.
В отфильтрованном списке типов проектов щелкните Консольное приложение, а затем нажмите кнопку Далее.
На странице Настроить новый проект введите MathClient в поле Имя проекта. Примите заданные по умолчанию Расположение и Имя решения. Для параметра Решение задайте Создать новое решение. Снимите флажок Разместить решение и проект в одном каталоге, если он установлен.
Нажмите кнопку Создать, чтобы создать клиентский проект.
Создается минимальный проект консольного приложения. Имя главного исходного файла будет совпадать с ранее введенным именем проекта. В этом примере используется имя MathClient.cpp. Вы можете создать проект, но он еще не использует вашу библиотеку DLL.
Создание клиентского приложения в Visual Studio 2017
Чтобы создать приложение C++, использующее созданную библиотеку DLL, в строке меню выберите FileNew> >Project.
Visual C plus plus > Windows Desktop selected, Windows Console Application highlighted, and Math Client typed in the Name text box.» title=»Name the client project» data-linktype=»relative-path»>
Нажмите кнопку ОК, чтобы создать проект клиентского приложения.
Создается минимальный проект консольного приложения. Имя главного исходного файла будет совпадать с ранее введенным именем проекта. В этом примере используется имя MathClient.cpp. Вы можете создать проект, но он еще не использует вашу библиотеку DLL.
Создание клиентского приложения в Visual Studio 2015
Чтобы создать приложение C++, использующее созданную библиотеку DLL, в строке меню выберите FileNew> >Project.
Templates > Visual C plus plus > Win32 selected, Win32 Console Application Visual C plus plus highlighted, and Math Client typed in the Name text box.» title=»Name the client project» data-linktype=»relative-path»>
На странице Параметры приложения в поле Тип приложения выберите пункт Консольное приложение, если он еще не выбран.
После завершения работы мастера создается минимальный проект консольного приложения. Имя главного исходного файла будет совпадать с ранее введенным именем проекта. В этом примере используется имя MathClient.cpp. Вы можете создать проект, но он еще не использует вашу библиотеку DLL.
Затем, чтобы вызвать функции MathLibrary в исходном коде, ваш проект должен содержать файл MathLibrary.h. Этот файл заголовка можно скопировать в проект клиентского приложения, а затем добавить его в проект как существующий элемент. Этот метод подходит для сторонних библиотек. Однако если вы работаете с кодом для библиотеки DLL и клиента одновременно, файлы заголовков могут оказаться несинхронизированными. Чтобы избежать этой проблемы, задайте путь Дополнительные каталоги включаемых файлов в проекте, чтобы добавить путь к исходному заголовку.
Добавление заголовка библиотеки DLL в путь включения
Щелкните правой кнопкой мыши узел MathClient в обозревателе решений, чтобы открыть диалоговое окно Страницы свойств.
В раскрывающемся списке Конфигурация выберите пункт Все конфигурации, если он еще не выбран.
В области слева выберите пункт Свойства конфигурации >C/C++ >Общие.
На панели свойств щелкните раскрывающийся элемент управления рядом с полем ввода параметра Дополнительные каталоги включаемых файлов, а затем щелкните Правка.
Дважды щелкните в верхней панели диалогового окна Дополнительные каталоги включаемых файлов, чтобы включить элемент управления «Поле ввода». Или щелкните значок папки, чтобы создать новую запись.
В элементе управления «Поле ввода» укажите путь к расположению файла заголовка MathLibrary.h. Чтобы перейти к нужной папке, можно выбрать элемент управления с многоточием ( . ).
Можно также ввести относительный путь от исходных файлов клиента к папке, содержащей файлы заголовков библиотеки DLL. Если вы следовали инструкциям по размещению клиентского проекта в отдельном решении, отличном от библиотеки DLL, относительный путь должен выглядеть следующим образом:
Если библиотеки DLL и клиентские проекты находятся в одном решении, относительный путь может выглядеть следующим образом:
Если библиотеки DLL и клиентские проекты находятся в других папках, измените относительный путь для соответствия. Или используйте элемент управления «Многоточие» для поиска папки.
После ввода пути к файлу заголовка в диалоговом окне Дополнительные каталоги включаемых файлов нажмите кнопку ОК. В диалоговом окне Страницы свойств нажмите кнопку OK, чтобы сохранить изменения.
Теперь можно добавить файл MathLibrary.h и использовать функции, которые он объявляет, в вашем клиентском приложении. Замените содержимое файла MathClient.cpp, используя следующий код:
Этот код может быть скомпилирован, но не скомпилирован. Если вы создаете клиентское приложение, в списке ошибок появится несколько ошибок LNK2019. Это связано с тем, что в проекте отсутствуют некоторые сведения: Вы не указали, что проект пока еще зависит от библиотеки MathLibrary.lib. И вы не указали компоновщику, как найти файл MathLibrary.lib.
Чтобы устранить эту проблему, можно скопировать файл библиотеки непосредственно в проект клиентского приложения. Компоновщик сможет найти и использовать его автоматически. Однако если и библиотека, и клиентское приложение находятся в стадии разработки, это может привести к изменениям в одной копии, которые не будут отображаться в другой. Чтобы избежать этой проблемы, можно задать свойство Дополнительные зависимости, чтобы сообщить системе сборки о том, что проект зависит от MathLibrary.lib. Также можно задать путь Дополнительные каталоги библиотек в проекте, включив в него путь к исходной библиотеке при компоновке.
Добавление библиотеки импорта DLL в проект
Щелкните правой кнопкой мыши узел MathClient в обозревателе решений и выберите Свойства, чтобы открыть диалоговое окно Страницы свойств.
В раскрывающемся списке Конфигурация выберите пункт Все конфигурации, если он еще не выбран. Это гарантирует, что любые изменения свойств применяются к сборкам отладки и выпуска.
В области слева выберите пункт Свойства конфигурации >Компоновщик>Ввод. На панели свойств щелкните раскрывающийся элемент управления рядом с полем ввода параметра Дополнительные зависимости, а затем щелкните Правка.
Input > Additional Dependencies property drop-down.» title=»Edit the Additional Dependencies property» data-linktype=»relative-path»>
В диалоговом окне Дополнительные зависимости добавьте MathLibrary.lib в список в верхнем элементе управления «Поле ввода».
Нажмите кнопку OK, чтобы вернуться в диалоговое окно Страницы свойств.
В области слева выберите пункт Свойства конфигурации >Компоновщик>Общие. На панели свойств щелкните раскрывающийся элемент управления рядом с полем ввода параметра Дополнительные каталоги библиотек, а затем щелкните Правка.
General > Additional Library Directories property drop-down.» title=»Edit the Additional Library Directories property» data-linktype=»relative-path»>
Если библиотеки DLL и клиентские проекты находятся в других расположениях, измените относительный путь для соответствия.
Как только вы ввели путь к файлу библиотеки, в диалоговом окне Дополнительные каталоги библиотек нажмите кнопку ОК, чтобы вернуться в диалоговое окно Страницы свойств. Нажмите ОК, чтобы сохранить изменения свойств.
Ваше клиентское приложение теперь можно компилировать и компоновать, но в нем по-прежнему нет всего необходимого для запуска. Когда операционная система загружает ваше приложение, оно ищет библиотеку DLL MathLibrary. Если она не может найти библиотеку DLL в определенных системных каталогах, в пути среды или локальном каталоге приложения, загрузка завершается сбоем. В зависимости от операционной системы вы увидите сообщение об ошибке следующего вида:
Чтобы избежать этой проблемы, можно скопировать библиотеку DLL в каталог, в котором находится исполняемый файл клиента, в процессе сборки. Можно добавить событие после сборки в ваш проект, чтобы добавить команду, которая копирует библиотеку DLL в выходной каталог вашей сборки. Указанная здесь команда копирует библиотеку DLL только в том случае, если она отсутствует или была изменена. Он использует макросы для копирования в расположения отладки или выпуска на основе конфигурации сборки.
Копирование библиотеки DLL в событие после сборки
Щелкните правой кнопкой мыши узел MathClient в обозревателе решений и выберите Свойства, чтобы открыть диалоговое окно Страницы свойств.
В раскрывающемся списке Конфигурация выберите пункт Все конфигурации, если он еще не выбран.
В области слева выберите Свойства конфигурации >События сборки >Событие после сборки.
В области свойств щелкните элемент управления «Поле ввода» в поле Командная строка. Если вы следовали инструкциям по размещению клиентского проекта в отдельном решении, отличном от проекта DLL, введите следующую команду:
xcopy /y /d «..\..\MathLibrary\$(IntDir)MathLibrary.dll» «$(OutDir)»
Если библиотеки DLL и клиентские проекты находятся в других каталогах, измените относительный путь к библиотеке DLL для соответствия.
Нажмите кнопку OK, чтобы сохранить изменения в свойствах проекта.
Теперь в вашем клиентском приложении есть все, что нужно для сборки и запуска. Соберите приложение, щелкнув команду Сборка>Собрать решение в меню. Окно Вывод в Visual Studio должно иметь примерно следующий вид в зависимости от используемой версии Visual Studio:
Поздравляем, вы создали приложение, которое вызывает функции в вашей библиотеке DLL. Теперь запустите свое приложение, чтобы увидеть, как оно работает. В строке меню щелкните Отладка>Начать без отладки. В Visual Studio открывается командное окно для запуска программы. Последняя часть выходных данных должна выглядеть так:
Для закрытия командного окна нажмите любую клавишу.
Теперь, когда вы создали библиотеку DLL и клиентское приложение, вы можете экспериментировать. Попробуйте задать точки останова в коде клиентского приложения и запустите приложение в отладчике. Посмотрите, что происходит, когда вы входите в вызов библиотеки. Добавьте другие функции в библиотеку или напишите другое клиентское приложение, которое использует вашу библиотеку DLL.
При развертывании приложения необходимо также развернуть используемые им библиотеки DLL. Самый простой способ сделать библиотеки DLL, которые вы создаете или добавляете из сторонних источников, доступными — поместить их в тот же каталог, что и ваше приложение. Это также называется локальным развертыванием приложений. Дополнительные сведения о развертывании см. в разделе Deployment in Visual C++.
Создание библиотек DLL, содержащих только ресурсы
В библиотеке DLL, содержащей только ресурсы, нет ничего, кроме ресурсов, таких как значки, растровые изображения, строки и диалоговые окна. Библиотеки DLL, содержащие только ресурсы, удобно использовать, когда необходимо предоставить нескольким программам общий доступ к одному и тому же набору ресурсов. С их помощью можно также легко предоставлять приложение с ресурсами, локализованными для нескольких языков. Дополнительные сведения см. в разделе Локализованные ресурсы в приложениях MFC: вспомогательные библиотеки DLL.
Создание библиотеки DLL, содержащей только ресурсы
Чтобы создать библиотеку DLL, содержащую только ресурсы, создайте новый проект Windows DLL (не MFC) и добавьте в него свои ресурсы.
Выберите Проект Win32 в диалоговом окне Создание проекта. Введите имена проекта и решения, а затем нажмите кнопку ОК.
В мастере приложений Win32 выберите Параметры приложения. В качестве типа приложения выберите DLL. В поле Дополнительные параметрывыберите Пустой проект. Нажмите кнопку Готово, чтобы создать проект.
Укажите параметр компоновщика /NOENTRY. Параметр /NOENTRY запрещает компоновщику привязывать ссылку на _main к библиотеке DLL, поэтому его необходимо указывать при создании библиотеки DLL, содержащей только ресурсы.
Построение библиотеки DLL.
В диалоговом окне Создание проекта выберите Мастер классических приложений Windows и нажмите кнопку Далее. На странице Настройка нового проекта введите имена проекта и решения, а затем нажмите кнопку Создать.
В диалоговом окне Проект классического приложения Windows в разделе Тип приложения выберите Библиотека динамической компоновки. В поле Дополнительные параметрывыберите Пустой проект. Нажмите кнопку ОК, чтобы создать проект.
Укажите параметр компоновщика /NOENTRY. Параметр /NOENTRY запрещает компоновщику привязывать ссылку на _main к библиотеке DLL, поэтому его необходимо указывать при создании библиотеки DLL, содержащей только ресурсы.
Построение библиотеки DLL.
Использование библиотеки DLL, содержащей только ресурсы
Как создать dll файл
Издано: 2003, BHV
Твердый переплет, 560 стр..
Динамические библиотеки для начинающих
Обо всем этом (и многом другом) рассказывает настоящая глава. Материал рассчитан на пользователей Microsoft Visual C++, а поклонникам других языков и компиляторов придется разбираться с ключами компиляции приведенных примеров самостоятельно.
Создание собственной DLL
Листинг 10 Демонстрация экспорта функции из DLL
Для компиляции этого примера в режиме командной строки можно запустить компилятор Microsoft Visual Studio: «cl.exe myfirstdll.c /LD«. Ключ «/LD» указывает линкеру, что требуется получить именно DLL.
Вызов функций из DLL
Существует два способа загрузки DLL: с явной и неявной компоновкой.
Загрузка DLL с неявной компоновкой
Листинг 11 Демонстрация вызова функции из DLL неявной компоновкой
Разумеется, «чужие» DLL не всегда поставляются вместе с сопутствующими библиотеками, но их можно легко изготовить самостоятельно! На этот случай предусмотрена специальная утилита implib, поставляемая вместе с компилятором, и вызываемая так: «implib.exe Имя_файла _создаваемой_библиотеки Имя_DLL«.
Если все прошло успешно, появится новый файл «ImplictDll.exe«, который, будучи запущенным, горделиво выведет на экран «Hello, Word!«. Это означает, что наша DLL подключена и успешно работает.
Заглянем внутрь: как это происходит? Запустим «dumpbin /IMPORTS ImplictDll.exe» и посмотрим, что нам сообщит программа:
Загрузка DLL с явной компоновкой
Явную загрузку динамических библиотек осуществляет функция HINSTANCE LoadLibrary(LPCTSTR lpLibFileName) или ее расширенный аналог HINSTANCE LoadLibraryEx(LPCTSTR lpLibFileName, HANDLE hFile, DWORD dwFlags).
Листинг 12 Демонстрация вызова функции из DLL явной компоновкой
Наша динамическая библиотека загрузилась; но не сразу после запуска файла (как это происходило при неявной компоновке), а только когда в ней возникла необходимость!
Выгрузка динамических библиотек из памяти
ООП и DLL
Что ж; испытаем компилятор на «сообразительность», включив в описание класса ключевое слово __declspec(dllexport) – и посмотрим, что из этого выйдет:
Листинг 13 Демонстрация экспорта класса из DLL
Откомпилируем этот код как обычную DLL и заглянем в таблицу импорта утилитой dumpbin:
Листинг 14 Демонстрация импорта класса из DLL неявной компоновкой
Откомпилируйте пример как обычную программу с неявной компоновкой («cl DLLClassCall.cpp DLLClass.lib«) и попробуйте запустить полученный файл. Работает? Никакой разницы с «классическим» Си нет, не правда ли? Вот только как подключить DLL с явной компоновкой? Неужели нельзя запретить компилятору «калечить» имена функций?! Конечно же, можно
Мангл и как его побороть или импорт классов из DLL явной компоновкой
Листинг 15 Отказ от «замангления» имен
Сперва идет ключевое слово «EXPORTS», за которым следуют одно или несколько «неприкасаемых» имен. Каждое имя начинается с новой строки, и в его конце не указывается точка с запятой.
Заглянув в таблицу импорта полученного DLL-файла, мы, среди прочей информации, увидим следующее:
Листинг 16 Демонстрация вызова функции MyDllCLass::Demo явной компоновкой
Загрузка ресурсов из DLL
Листинг 17 Создание DLL, содержащей одни лишь ресурсы
Листинг 18 Демонстрация оптимизированной загрузки DLL, не содержащей ничего кроме ресурсов
Что такое библиотека DLL?
В этой статье описывается, что такое библиотека динамической компоновки (DLL) и какие проблемы могут возникнуть при использовании библиотек DLL. В ней также описаны некоторые дополнительные проблемы, которые следует учитывать при разработке собственных DLL-библиотек.
Применяется к: Windows 10 — все выпуски
Оригинальный номер базы знаний: 815065
Сводка
В этой статье поясняется, что такое DLL, описываются методы динамической компоновки, зависимости DLL, точки входа DLL, экспорт функций DLL и средства устранения неполадок DLL.
Для операционных систем Windows большая часть функциональности операционной системы обеспечивается DLL. Кроме того, при запуске программы в одной из операционных систем Windows большая часть функциональности программы может обеспечиваться библиотеками DLL. Например, некоторые программы могут содержать множество различных модулей, при этом каждый модуль программы содержится и распространяется в библиотеках DLL.
Использование DLL-библиотек способствует разбиению кода на модули, повторному использованию кода, эффективному использованию памяти и сокращению дискового пространства. Таким образом, операционная система и программы загружаются и работают быстрее, а также занимают меньше места на диске компьютера.
Когда программа использует DLL, проблема, которая называется зависимостью, может привести к тому, что программа не запустится. Когда программа использует библиотеку DLL, создается зависимость. Если другая программа перезаписывает и прерывает эту зависимость, исходная программа может не запуститься успешно.
Дополнительная информация
DLL — это библиотека, содержащая код и данные, которые могут использоваться несколькими программами одновременно. Например, в операционных системах Windows DLL-библиотека Comdlg32 выполняет общие функции, связанные с диалоговыми окнами. Каждая программа может использовать функции, содержащиеся в этой библиотеке DLL, для реализации диалогового окна Открыть. Это способствует повторному использованию кода и эффективному использованию памяти.
С помощью библиотеки DLL программу можно разделить на отдельные компоненты. Например, бухгалтерская программа может продаваться по модулям. Каждый модуль можно загрузить в основную программу во время выполнения, если он установлен. Так как модули являются отдельными, время загрузки программы ускоряется. Модуль загружается только тогда, когда запрашивается эта функциональность.
Кроме того, обновления проще применять к каждому модулю, не затрагивая другие части программы. Например, у вас может быть программа расчета заработной платы, и ставки налога меняются каждый год. Если эти изменения изолированы в библиотеке DLL, можно применить обновление, не требуя сборки или установки всей программы еще раз.
В следующем списке описаны некоторые файлы, которые реализованы в виде библиотек DLL в операционных системах Windows:
Файлы элементов ActiveX (OCX)
Примером элемента управления ActiveX является элемент управления календарем, который позволяет выбрать дату из календаря.
Файлы панели управления (CPL)
Примером CPL-файла является элемент, расположенный в панели управления. Каждый элемент является специализированной библиотекой DLL.
Файлы драйвера устройства (DRV)
Примером драйвера устройства является драйвер принтера, который управляет печатью на принтере.
Преимущества DLL-библиотек
В следующем списке описаны некоторые преимущества использования программой DLL-библиотек:
Использует меньше ресурсов
Если несколько программ используют одну и ту же библиотеку функций, DLL-библиотека может уменьшить дублирование кода, загружаемого на диск и в физическую память. Это может сильно повлиять на производительность не только программы, выполняемой на переднем плане, но и других программ, работающих под управлением операционной системы Windows.
Улучшает модульную архитектуру
Библиотека DLL помогает в разработке модульных программ. Она помогает разрабатывать большие программы, требующие использования нескольких языковых версий, или программы, требующие модульной архитектуры. Примером модульной программы является бухгалтерская программа со множеством модулей, которые могут быть динамически загружены во время выполнения.
Облегчает развертывание и установку
Если функции в библиотеке DLL требуется обновление или исправление, для развертывания и установки библиотеки DLL не требуется повторная компоновка программы с этой библиотекой. Кроме того, если несколько программ используют одну и ту же библиотеку DLL, они смогут получить преимущества от обновления или исправления. Эта проблема может возникать чаще при использовании сторонних библиотек DLL, которые регулярно обновляются или исправляются.
Зависимости DLL
Если программа или библиотека DLL использует функцию DLL в другой библиотеке DLL, создается зависимость. Программа больше не является автономной, и при нарушении зависимости могут возникнуть проблемы. Например, программа может не запуститься, если произойдет одно из следующих действий:
Эти действия известны как конфликты DLL. Если обратная совместимость не применяется, программа может не запуститься.
В следующем списке описываются изменения, которые были представлены в Windows 2000 и операционных системах Windows более поздних версий, чтобы свести к минимуму проблемы с зависимостями:
Защита файлов Windows
В защите файлов Windows операционная система предотвращает обновление или удаление системных библиотек DLL неавторизованным агентом. Когда установка программы пытается удалить или обновить библиотеку DLL, которая определена как системная библиотека DLL, защита файлов Windows выполнит поиск действительной цифровой подписи.
Частные библиотеки DLL
Средства устранения неполадок, связанных с библиотеками DLL
Для устранения неполадок с библиотеками DLL доступно несколько средств. Ниже приведены некоторые из этих средств.
Обходчик зависимостей
Средство «Обходчик зависимостей» может рекурсивно проверять все зависимые библиотеки DLL, используемые программой. При открытии программы в обходчике зависимостей будут выполнены следующие проверки:
С помощью обходчика зависимостей можно документировать все библиотеки DLL, используемые программой. Это может помочь предотвратить и устранить проблемы с библиотеками DLL, которые могут возникнуть в будущем. При установке Visual Studio 6.0 обходчик зависимостей находится в следующем каталоге:
drive\Program Files\Microsoft Visual Studio\Common\Tools
Универсальный устранитель проблем DLL
Универсальный устранитель проблем DLL (DUPS) используется для аудита, сравнения, документирования и отображения информации DLL. В следующем списке описаны утилиты, входящие в состав инструмента DUPS:
Эта утилита перечисляет все библиотеки DLL на компьютере и записывает информацию в текстовый файл или в файл базы данных.
Эта утилита сравнивает библиотеки DLL, перечисленные в двух текстовых файлах, и создает третий текстовый файл, содержащий различия.
Эта утилита загружает текстовые файлы, созданные с помощью утилит Dlister.exe и Dcomp.exe, в базу данных dllHell.
Эта утилита предоставляет графический интерфейс пользователя (GUI) версии утилиты Dtxt2DB.exe.
Справочная база данных библиотеки DLL
Справочная база данных библиотеки DLL помогает найти определенные версии библиотек DLL, установленных продуктами корпорации Microsoft.
Разработка библиотеки DLL
В этом разделе описываются проблемы и требования, которые следует учитывать при разработке собственных библиотек DLL.
Типы библиотек DLL
При загрузке библиотеки DLL в приложение два метода компоновки позволяют вызывать экспортированные функции DLL. Два метода компоновки — динамическая компоновка во время загрузки и динамическая компоновка во время выполнения.
Динамическая компоновка во время загрузки
При динамической компоновке во время загрузки приложение производит явные вызовы экспортированных функций DLL, таких как локальные функции. Чтобы использовать динамическую компоновку во время загрузки, укажите файл заголовка (H) и файл библиотеки импорта (LIB) при компиляции и компоновке приложения. При этом компоновщик предоставит системе сведения, необходимые для загрузки библиотеки DLL и разрешения экспортированных расположений функций DLL во время загрузки.
Динамическая компоновка во время выполнения
При динамической компоновке во время выполнения приложение вызывает функцию LoadLibrary или LoadLibraryEx для загрузки библиотеки DLL во время выполнения. После успешной загрузки библиотеки DLL используйте функцию GetProcAddress для получения адреса экспортированной функции DLL, которую требуется вызвать. При использовании динамической компоновки во время выполнения файл библиотеки импорта не требуется.
В следующем списке описаны критерии приложения для использования динамической компоновки во время загрузки и использования динамической компоновки во время выполнения:
Если важна начальная производительность запуска приложения, следует использовать динамическую компоновку во время выполнения.
При динамической компоновке во время загрузки экспортированные функции DLL являются локальными функциями. Это упрощает вызов этих функций.
При динамической компоновке во время выполнения приложение может выполнять ветвление для загрузки различных модулей по мере необходимости. Это важно при разработке версий на нескольких языках.
Точка входа DLL
При создании библиотеки DLL можно дополнительно указать функцию точки входа. Функция точки входа вызывается, когда процессы или потоки присоединяются к библиотеке DLL или отсоединяются от библиотеки DLL. Функцию точки входа можно использовать для инициализации структур данных или удаления структур данных, требуемых библиотекой DLL. Кроме того, если приложение является многопоточным, можно использовать локальную память потока (TLS) для выделения памяти, которая является частной для каждого потока в функции точки входа. Следующий код является примером функции точки входа DLL.
Когда функция точки входа возвращает значение FALSE, приложение не запускается при использовании динамической компоновки во время загрузки. Если вы используете динамическую компоновку во время выполнения, не будет загружаться только отдельная библиотека DLL.
Убедитесь, что доступ в многопоточных приложениях к глобальным данным DLL синхронизирован (потокобезопасный), чтобы избежать возможного повреждения данных. Для этого используйте TLS для предоставления уникальных данных для каждого потока.
Экспорт функций DLL
Чтобы экспортировать функции DLL, можно добавить ключевое слово функции в экспортированные функции DLL или создать файл определения модуля (DEF), в котором перечислены экспортированные функции DLL.
Чтобы использовать ключевое слово функции, необходимо объявить каждую функцию, которую требуется экспортировать, с помощью следующего ключевого слова:
__declspec(dllexport)
Вы также можете использовать файл определения модуля для объявления экспортированных функций DLL. При использовании файла определения модуля не нужно добавлять ключевое слово функции в экспортированные функции DLL. В файле определения модуля объявляется инструкция LIBRARY и инструкция EXPORTS для библиотеки DLL. Следующий код является примером файла определения.
Пример библиотеки DLL и приложения
В Visual C++ 6.0 можно создать библиотеку DLL, выбрав тип проекта Библиотека динамической компоновки Win32 или тип проекта Мастер приложений MFC (dll).
Следующий код является примером библиотеки DLL, созданной в Visual C++ с использованием типа проекта Библиотека динамической компоновки Win32.
Следующий код является примером проекта Приложение Win32, который вызывает экспортированную функцию DLL в библиотеке DLL SampleDLL.
При динамической компоновке во время загрузки необходимо компоновать библиотеку импорта SampleDLL.lib, которая создается при сборке проекта SampleDLL.
При динамической компоновке во время выполнения для вызова экспортируемой функции DLL SampleDLL.dll используется код, аналогичный следующему коду:
При компиляции и компоновке приложения SampleDLL операционная система Windows ищет библиотеку DLL SampleDLL в следующих расположениях в следующем порядке:
Системная папка Windows
Функция GetSystemDirectory возвращает путь к системной папке Windows.
Функция GetWindowsDirectory возвращает путь к папке Windows.
Файл сборки содержит манифест сборки, метаданные типа, код MSIL и другие ресурсы. Манифест сборки содержит метаданные сборки, которые предоставляют все сведения, необходимые для самостоятельного описания сборки. В манифест сборки включены следующие сведения:
Код MSIL, содержащийся в сборке, не может выполняться напрямую. Вместо этого выполнение кода MSIL управляется через среду CLR. По умолчанию создаваемая сборка является закрытой для приложения. Чтобы создать общую сборку, необходимо присвоить сборке строгое имя, а затем опубликовать сборку в глобальном кэше сборок.
В следующем списке описаны некоторые функции сборок по сравнению с функциями библиотек DLL Win32:
При создании сборки все сведения, необходимые среде CLR для запуска сборки, содержатся в манифесте сборки. Манифест сборки содержит список зависимых сборок. Таким образом, среда CLR может поддерживать согласованный набор сборок, используемых в приложении. В библиотеках DLL Win32 нельзя поддерживать согласованность между набором библиотек DLL, используемых в приложении, когда используются общие библиотеки DLL.
В манифесте сборки сведения о версии записываются и применяются средой CLR. Кроме того, политики версии позволяют принудительно применять использование конкретной версии. В библиотеках DLL Win32 управление версиями не может применяться операционной системой. Необходимо убедиться, что библиотеки DLL поддерживают обратную совместимость.
Сборки поддерживают параллельное развертывание. Одно приложение может использовать одну версию сборки, а другое — другую. Начиная с Windows 2000, параллельное развертывание поддерживается путем поиска библиотек DLL в папке приложения. Кроме того, защита файлов Windows предотвращает перезапись или замену системных библиотек DLL неавторизованным агентом.
Автономность и изоляция
Приложение, разработанное с помощью сборки, может быть автономным и изолированным от других приложений, запущенных на компьютере. Эта функция помогает создавать установки с нулевым влиянием.
Сборка запускается с учетом разрешений безопасности, которые указаны в манифесте сборки и контролируются средой CLR.
Независимость от языка
Работа с библиотеками динамической компоновки (DLL)
Рассмотрим ряд аспектов создания и использования библиотек DLL:
Использование DLL
Практически невозможно создать приложение Windows, в котором не использовались бы библиотеки DLL. В DLL содержатся все функции Win32 API и несчетное количество других функций операционных систем Win32.
Однако, если библиотека используется только одним приложением, лучше сделать ее обычной, статической. Конечно, если входящие в ее состав функции будут использоваться только в одной программе, можно просто вставить в нее соответствующий файл с исходным текстом.
Чаще всего проект подключается к DLL статически, или неявно, на этапе компоновки. Загрузкой DLL при выполнении программы управляет операционная система. Однако, DLL можно загрузить и явно, или динамически, в ходе работы приложения.
Библиотеки импортирования
Согласование интерфейсов
При использовании собственных библиотек или библиотек независимых разработчиков придется обратить внимание на согласование вызова функции с ее прототипом.
По умолчанию в Visual C++ интерфейсы функций согласуются по правилам C++. Это значит, что параметры заносятся в стек справа налево, вызывающая программа отвечает за их удаление из стека при выходе из функции и расширении ее имени. Расширение имен (name mangling) позволяет редактору связей различать перегруженные функции, т.е. функции с одинаковыми именами, но разными списками аргументов. Однако в старой библиотеке С функции с расширенными именами отсутствуют.
Хотя все остальные правила вызова функции в С идентичны правилам вызова функции в C++, в библиотеках С имена функций не расширяются. К ним только добавляется впереди символ подчеркивания (_).
Если необходимо подключить библиотеку на С к приложению на C++, все функции из этой библиотеки придется объявить как внешние в формате С:
Объявления функций библиотеки обычно помещаются в файле заголовка этой библиотеки, хотя заголовки большинства библиотек С не рассчитаны на применение в проектах на C++. В этом случае необходимо создать копию файла заголовка и включить в нее модификатор extern «C» к объявлению всех используемых функций библиотеки. Модификатор extern «C» можно применить и к целому блоку, к которому с помощью директивы #tinclude подключен файл старого заголовка С. Таким образом, вместо модификации каждой функции в отдельности можно обойтись всего тремя строками:
В программах для старых версий Windows использовались также соглашения о вызове функций языка PASCAL для функций Windows API. В новых программах следует использовать модификатор winapi, преобразуемый в _stdcall. Хотя это и не стандартный интерфейс функций С или C++, но именно он используется для обращений к функциям Windows API. Однако обычно все это уже учтено в стандартных заголовках Windows.
Загрузка неявно подключаемой DLL
При запуске приложение пытается найти все файлы DLL, неявно подключенные к приложению, и поместить их в область оперативной памяти, занимаемую данным процессом. Поиск файлов DLL операционной системой осуществляется в следующей последовательности.
Если библиотека DLL не обнаружена, приложение выводит диалоговое окно с сообщением о ее отсутствии и путях, по которым осуществлялся поиск. Затем процесс отключается.
Если нужная библиотека найдена, она помещается в оперативную память процесса, где и остается до его окончания. Теперь приложение может обращаться к функциям, содержащимся в DLL.
Динамическая загрузка и выгрузка DLL
Вместо того, чтобы Windows выполняла динамическое связывание с DLL при первой загрузке приложения в оперативную память, можно связать программу с модулем библиотеки во время выполнения программы (при таком способе в процессе создания приложения не нужно использовать библиотеку импорта). В частности, можно определить, какая из библиотек DLL доступна пользователю, или разрешить пользователю выбрать, какая из библиотек будет загружаться. Таким образом можно использовать разные DLL, в которых реализованы одни и те же функции, выполняющие различные действия. Например, приложение, предназначенное для независимой передачи данных, сможет в ходе выполнения принять решение, загружать ли DLL для протокола TCP/IP или для другого протокола.
Загрузка обычной DLL
Когда Windows обнаружит файл, его полный путь будет сравнен с путем библиотек DLL, уже загруженных данным процессом. Если обнаружится тождество, вместо загрузки копии приложения возвращается дескриптор уже подключенной библиотеки.
Если файл обнаружен и библиотека успешно загрузилась, функция ::LoadLibrary возвращает ее дескриптор, который используется для доступа к функциям библиотеки.
Перед тем, как использовать функции библиотеки, необходимо получить их адрес. Для этого сначала следует воспользоваться директивой typedef для определения типа указателя на функцию и определить переменную этого нового типа, например:
Затем следует получить дескриптор библиотеки, при помощи которого и определить адреса функций, например адрес функции с именем MyFunction:
Адрес функции определяется при помощи функции ::GetProcAddress, ей следует передать имя библиотеки и имя функции. Последнее должно передаваться в том виде, в котором экспортируется из DLL.
Можно также сослаться на функцию по порядковому номеру, по которому она экспортируется (при этом для создания библиотеки должен использоваться def-файл, об этом будет рассказано далее):
После завершения работы с библиотекой динамической компоновки, ее можно выгрузить из памяти процесса с помощью функции ::FreeLibrary:
Загрузка MFC-расширений динамических библиотек
При загрузке MFC-расширений для DLL (подробно о которых рассказывается далее) вместо функций LoadLibraryи FreeLibrary используются функции AfxLoadLibrary и AfxFreeLibrary. Последние почти идентичны функциям Win32 API. Они лишь гарантируют дополнительно, что структуры MFC, инициализированные расширением DLL, не были запорчены другими потоками.
Ресурсы DLL
Динамическая загрузка применима и к ресурсам DLL, используемым MFC для загрузки стандартных ресурсов приложения. Для этого сначала необходимо вызвать функцию LoadLibrary и разместить DLL в памяти. Затем с помощью функции AfxSetResourceHandle нужно подготовить окно программы к приему ресурсов из вновь загруженной библиотеки. В противном случае ресурсы будут загружаться из файлов, подключенных к выполняемому файлу процесса. Такой подход удобен, если нужно использовать различные наборы ресурсов, например для разных языков.
Замечание. С помощью функции LoadLibrary можно также загружать в память исполняемые файлы (не запускать их на выполнение!). Дескриптор выполняемого модуля может затем использоваться при обращении к функциям FindResource и LoadResource для поиска и загрузки ресурсов приложения. Выгружают модули из памяти также при помощи функции FreeLibrary.
Пример обычной DLL и способов загрузки
Приведем исходный код динамически подключаемой библиотеки, которая называется MyDLL и содержит одну функцию MyFunction, которая просто выводит сообщение.
Сначала в заголовочном файле определяется макроконтстанта EXPORT. Использование этого ключевого слова при определении некоторой функции динамически подключаемой библиотеке позволяет сообщить компоновщику, что эта функция доступна для использования другими программами, в результате чего он заносит ее в библиотеку импорта. Кроме этого, такая функция, точно так же, как и оконная процедура, должна определяться с помощью константы CALLBACK:
Файл библиотеки также несколько отличается от обычных файлов на языке C для Windows. В нем вместо функции WinMain имеется функция DllMain. Эта функция используется для выполнения инициализации, о чем будет рассказано позже. Для того, чтобы библиотека осталась после ее загрузки в памяти, и можно было вызывать ее функции, необходимо, чтобы ее возвращаемым значением было TRUE:
Пример неявного подключения DLL приложением
Приведем теперь исходный код простого приложения, которое использует функцию MyFunction из библиотеки MyDLL.dll:
Эта программа выглядит как обычная программ для Windows, чем она в сущности и является. Тем не менее, следует обратить внимание, что в исходный ее текст помимо вызова функции MyFunction из DLL-библиотеки включен и заголовочный файл этой библиотеки MyDLL.h. Также необходимо на этапе компоновки приложения подключить к нему библиотеку импорта MyDLL.lib (процесс неявного подключения DLL к исполняемому модулю).
Чрезвычайно важно понимать, что сам код функции MyFunction не включается в файл MyApp.exe. Вместо этого там просто имеется ссылка на файл MyDLL.dll и ссылка на функцию MyFunction, которая находится в этом файле. Файл MyApp.exe требует запуска файла MyDLL.dll.
Заголовочный файл MyDLL.h включен в файл с исходным текстом программы MyApp.c точно так же, как туда включен файл windows.h. Включение библиотеки импорта MyDLL.lib для компоновки аналогично включению туда всех библиотек импорта Windows. Когда программа MyApp.exe работает, она подключается к библиотеке MyDLL.dll точно так же, как ко всем стандартным динамически подключаемым библиотекам Windows.
Пример динамической загрузки DLL приложением
Приведем теперь полностью исходный код простого приложения, которое использует функцию MyFunction из библиотеки MyDLL.dll, используя динамическую загрузку библиотеки:
Создание DLL
Теперь, познакомившись с принципами работы библиотек DLL в приложениях, рассмотрим способы их создания. При разработке приложении функции, к которым обращается несколько процессов, желательно размещать в DLL. Это позволяет более рационально использовать память в Windows.
Проще всего создать новый проект DLL с помощью мастера AppWizard, который автоматически выполняет многие операции. Для простых DLL, таких как рассмотренные в этой главе, необходимо выбрать тип проекта Win32 Dynamic-Link Library. Новому проекту будут присвоены все необходимые параметры для создания библиотеки DLL. Файлы исходных текстов придется добавлять к проекту вручную.
Если же планируется в полной мере использовать функциональные возможности MFC, такие как документы и представления, или намерены создать сервер автоматизации OLE, лучше выбрать тип проекта MFC AppWizard (dll). В этом случае, помимо присвоения проекту параметров для подключения динамических библиотек, мастер проделает некоторую дополнительную работу. В проект будут добавлены необходимые ссылки на библиотеки MFC и файлы исходных текстов, содержащие описание и реализацию в библиотеке DLL объекта класса приложения, производного от CWinApp.
Функция DllMain
Функция DllMain вызывается в нескольких случаях. Причина ее вызова определяется параметром dwReason, который может принимать одно из следующих значений.
При первой загрузке библиотеки DLL процессом вызывается функция DllMain с dwReason, равным DLL_PROCESS_ATTACH. Каждый раз при создании процессом нового потока DllMainO вызывается с dwReason, равным DLL_THREAD_ATTACH (кроме первого потока, потому что в этом случае dwReason равен DLL_PROCESS_ATTACH).
По окончании работы процесса с DLL функция DllMain вызывается с параметром dwReason, равным DLL_PROCESS_DETACH. При уничтожении потока (кроме первого) dwReason будет равен DLL_THREAD_DETACH.
Все операции по инициализации и очистке для процессов и потоков, в которых нуждается DLL, необходимо выполнять на основании значения dwReason, как было показано в предыдущем примере. Инициализация процессов обычно ограничивается выделением ресурсов, совместно используемых потоками, в частности загрузкой разделяемых файлов и инициализацией библиотек. Инициализация потоков применяется для настройки режимов, свойственных только данному потоку, например для инициализации локальной памяти.
В состав DLL могут входить ресурсы, не принадлежащие вызывающему эту библиотеку приложению. Если функции DLL работают с ресурсами DLL, было бы, очевидно, полезно сохранить где-нибудь в укромном месте дескриптор hInst и использовать его при загрузке ресурсов из DLL. Указатель IpReserved зарезервирован для внутреннего использования Windows. Следовательно, приложение не должно претендовать на него. Можно лишь проверить его значение. Если библиотека DLL была загружена динамически, оно будет равно NULL. При статической загрузке этот указатель будет ненулевым.
В случае успешного завершения функция DllMain должна возвращать TRUE. В случае возникновения ошибки возвращается FALSE, и дальнейшие действия прекращаются.
Замечание. Если не написать собственной функции DllMain(), компилятор подключит стандартную версию, которая просто возвращает TRUE.
Экспортирование функций из DLL
Чтобы приложение могло обращаться к функциям динамической библиотеки, каждая из них должна занимать строку в таблице экспортируемых функций DLL. Есть два способа занести функцию в эту таблицу на этапе компиляции.
Метод __declspec (dllexport)
Метод __declspec применяется не так часто, как второй метод, работающий с файлами определения модуля (.def), и позволяет лучше управлять процессом экспортирования.
Файлы определения модуля
В строке экспорта функции можно указать ее порядковый номер, поставив перед ним символ @. Этот номер будет затем использоваться при обращении к GetProcAddress (). На самом деле компилятор присваивает порядковые номера всем экспортируемым объектам. Однако способ, которым он это делает, отчасти непредсказуем, если не присвоить эти номера явно.
В строке экспорта можно использовать параметр NONAME. Он запрещает компилятору включать имя функции в таблицу экспортирования DLL:
Иногда это позволяет сэкономить много места в файле DLL. Приложения, использующие библиотеку импортирования для неявного подключения DLL, не «заметят» разницы, поскольку при неявном подключении порядковые номера используются автоматически. Приложениям, загружающим библиотеки DLL динамически, потребуется передавать в GetProcAddress порядковый номер, а не имя функции.
При использовании вышеприведенного def-файл описания экспортируемых функций DLL-библиотеки может быть,например, не таким:
Экспортирование классов
Если взглянуть на реализованный в классе файл распределения памяти, в нем можно заметить некоторые весьма необычные функции. Оказывается, здесь есть неявные конструкторы и деструкторы, функции, объявленные в макросах MFC, в частности _DECLARE_MESSAGE_MAP, а также функции, которые написанные программистом.
Хотя можно экспортировать каждую из этих функций в отдельности, есть более простой способ. Если в объявлении класса воспользоваться макромодификатором AFX_CLASS_EXPORT, компилятор сам позаботится об экспортировании необходимых функций, позволяющих приложению использовать класс, содержащийся в DLL.
Память DLL
В отличие от статических библиотек, которые, по существу, становятся частью кода приложения, библиотеки динамической компоновки в 16-разрядных версиях Windows работали с памятью несколько иначе. Под управлением Win 16 память DLL размещалась вне адресного пространства задачи. Размещение динамических библиотек в глобальной памяти обеспечивало возможность совместного использования их различными задачами.
В Win32 библиотека DLL располагается в области памяти загружающего ее процесса. Каждому процессу предоставляется отдельная копия «глобальной» памяти DLL, которая реинициализируется каждый раз, когда ее загружает новый процесс. Это означает, что динамическая библиотека не может использоваться совместно, в общей памяти, как это было в Winl6.
И все же, выполнив ряд замысловатых манипуляций над сегментом данных DLL, можно создать общую область памяти для всех процессов, использующих данную библиотеку.
Допустим, имеется массив целых чисел, который должен использоваться всеми процессами, загружающими данную DLL. Это можно запрограммировать следующим образом:
Полная компиляция DLL
Для MFC предусмотрен ряд особых режимов, касающихся использования динамической библиотекой библиотек MFC. Этому вопросу посвящен следующий раздел.
DLL и MFC
Программист не обязан использовать MFC при создании динамических библиотек. Однако использование MFC открывает ряд очень важных возможностей.
Обычные MFC DLL
Обычные MFC DLL позволяют применять MFC в динамических библиотеках. При этом приложения, обращающиеся к таким библиотекам, не обязательно должны быть построены на основе MFC. В обычных DLL можно использовать MFC любым способом, в том числе создавая в DLL новые классы на базе классов MFC и экспортируя их в приложения.
Однако обычные DLL не могут обмениваться с приложениями указателями на классы, производные от MFC.
Если приложению необходимо обмениваться с DLL указателями на объекты классов MFC или их производных, нужно использовать расширение DLL, описанное в следующем разделе.
Архитектура обычных DLL рассчитана на использование другими средами программирования, такими как Visual Basic и PowerBuilder.
Управление информацией о состоянии MFC
В каждом модуле процесса MFC содержится информация о его состоянии. Таким образом, информация о состоянии DLL отлична от информации о состоянии вызвавшего ее приложения. Поэтому любые экспортируемые из библиотеки функции, обращение к которым исходит непосредственно из приложений, должны сообщать MFC, какую информацию состояния использовать. В обычной MFC DLL, использующей динамические библиотеки MFC, перед вызовом любой подпрограммы MFC в начале экспортируемой функции нужно поместить следующую строку:
Данный оператор определяет использование соответствующей информации о состоянии во время выполнения функции, обратившейся к данной подпрограмме.
Динамические расширения MFC
MFC позволяет создавать такие библиотеки DLL, которые воспринимаются приложениями не как набор отдельных функций, а как расширения MFC. С помощью данного вида DLL можно создавать новые классы, производные от классов MFC, и использовать их в своих приложениях.
Чтобы обеспечить возможность свободного обмена указателями на объекты MFC между приложением и DLL, нужно создать динамическое расширение MFC. DLL этого типа подключаются к динамическим библиотекам MFC так же, как и любые приложения, использующие динамическое расширение MFC.
Чтобы создать новое динамическое расширение MFC, проще всего, воспользовавшись мастером приложении, присвоить проекту тип MFC AppWizard (dll) и на шаге 1 включить режим «MFC Extension DLL». В результате новому проекту будут присвоены все необходимые атрибуты динамического расширения MFC. Кроме того, будет создана функция DllMain для DLL, выполняющая ряд специфических операций по инициализации расширения DLL. Следует обратить внимание, что динамические библиотеки данного типа не содержат и не должны содержать объектов, производных от CWinApp.
Инициализация динамических расширений
Чтобы «вписаться» в структуру MFC, динамические расширения MFC требуют дополнительной начальной настройки. Соответствующие операции выполняются функцией DllMain. Рассмотрим пример этой функции, созданный мастером AppWizard.
Самой важной частью этой функции является вызов AfxInitExtensionModule. Это инициализация динамической библиотеки, позволяющая ей корректно работать в составе структуры MFC. Аргументами данной функции являются передаваемый в DllMain дескриптор библиотеки DLL и структура AFX_EXTENSION_MODULE, содержащая информацию о подключаемой к MFC динамической библиотеке.
Нет необходимости инициализировать структуру AFX_EXTENSION_MODULE явно. Однако объявить ее нужно обязательно. Инициализацией же займется конструктор CDynLinkLibrary. В DLL необходимо создать класс CDynLinkLibrary. Его конструктор не только будет инициализировать структуру AFX_EXTENSION_MODULE, но и добавит новую библиотеку в список DLL, с которыми может работать MFC.
Загрузка динамических расширений MFC
Начиная с версии 4.0 MFC позволяет динамически загружать и выгружать DLL, в том числе и расширения. Для корректного выполнения этих операций над создаваемой DLL в ее функцию DllMain в момент отключения от процесса необходимо добавить вызов AfxTermExtensionModule. Последней функции в качестве параметра передается уже использовавшаяся выше структура AFX_EXTENSION_MODULE. Для этого в текст DllMain нужно добавить следующие строки.
Кроме того, следует помнить, что новая библиотека DLL является динамическим расширением и должна загружаться и выгружаться динамически, с помощью функций AfxLoadLibrary и AfxFreeLibrary,а не LoadLibrary и FreeLibrary.
Экспортирование функций из динамических расширений
Рассмотрим теперь, как осуществляется экспортирование в приложение функций и классов из динамического расширения. Хотя добавить в DEF-файл все расширенные имена можно и вручную, лучше использовать модификаторы для объявлений экспортируемых классов и функций, такие как AFX_EXT_CLASS и AFX_EXT_API,например:
Создаем DLL
При диамической загрузке вы можете загружать DLL при необходимости, выгрузить ее когода она ненужна. Однако работать с такими библиотеками сложнее чем со статическими. Рассмотрим созздание и использование DLL статической загрузки.
Статическая загрузка
Создадим сперва проект (File / New / DLL). Будет создан проект, содержащий следующее:
и длинный коментарий предупреждающий вас о том, что для работо способности вашей DLL необходимо снеи обеспечить поствку некоторых dll если вы используете экземпляры класса String.
Для экспорта и импорта из DLL необходимо использовать моди фикаторы __export и __import соответсвенно. Но в C++ Builder можно использовать новое ключевое слово __delspec() с параметрами dllexport и dllimport соответсвенно. Сами понимаете, что для того чтобы эспортировать функции из библиотеки еужен один заголовочный файл с описаниями _delspec(dllexport) для экспортируемых функций, для импорта функций в приложение вам необходимо будет поставить анологичный заголовочный файл но с _delspec(dllimport) описаниями, что достаточно неудобно. Эта проблема решается легко: добавте в заголовочный файл библиотеки следующее:
в исходном файле DLL напишите #define BUILD_DLL, а вместо __declspec(dllexport) пишите DLL_EXP. При написании программы добавте строчку #define BUILD_APP, и просто подключите заголовочный файл DLL.
Пример DLL: файл P.cpp
Если вы нажмете Run то после завершенния построения будет выдано сообщение что данная программа не можнт быть исполнена (естественно). Теперь напишем вызывающую программу. Втомже каталоге создайде новый проект (File / New Application) в форму поместите одну кнопку и создай обработчик события OnClick. Ваш исполняемый файл должен представлять собой слдующее:
Динамическая загрузка
Давайте рассмотрим на примере, как производится динамическая загрузка. Создайте новый прокт DLL и внесите в него следующее:
Cкомпилируйте проект, в результате чего будет создана DLL.
Теперь создайте проект приложения анологичный проекту для использования статической загрузки (форма с кнопкой и обработчиком события кнопки OnClick) ниже приведен код приложения:(Unit11.cpp)
запустите это проект, при нажатии на кнопку должно выдаватся сообшение. Теперь разберемся, как это работает.
Обратите внимание на то, что призагрузке можно указать точное местоположние библиотеки (необезательно в том же каталоге где и приложение).
следует следить что бы совпадали объявление функции в *.dll и *.exe (в частности в этом примере : __stdcall не совпадают, потому выкидывает External exception)
привожу рабочий пример:
void __fastcall TForm1::Button1Click(TObject *Sender) <
void (*Message)(char *s);
HINSTANCE dllp = LoadLibrary(«p.dll»);
if (dllp) <
Message= (void(*) (char*))
GetProcAddress(dllp, «_Message»);
if (Message) Message(«Hi From Dinamic DLL»);
>
FreeLibrary(dllp);
>
dll-ку оставляем прежнюю
Содержимое параграфа «Сущность динамического связывания»
Статическая и динамическая компоновка
Обычная Windows-программа представляет собой исполняемый файл, который обычно создает одно или более окон, а для получения данных от пользователя использует цикл обработки сообщений.
Рассмотрим сущность динамического и статического связывания (или компоновки)
Три значения одного слова “библиотека”
В предыдущем изложении слово “библиотека” встречается в разных контекстах. Помимо динамически подключаемых библиотек оно упоминается в сочетаниях “объектная библиотек” и “библиотека импорта”. Разберемся в этих понятиях.
· Например, в Microsoft Visual C++ обычная объектная библиотека времени выполнения, которая при компоновке присоединяется к программе, называется libc.lib.
Пример простой DLL
· Когда программа MyApp.exe работает, она подключается к библиотеке MyDLL.dll точно так же, как ко всем стандартным динамически подключаемым библиотекам Windows.
Содержимое параграфа «Способы связывания приложения с DLL»
Связывание с DLL при загрузке приложения (неявное подключение)
Динамическая загрузка DLL в процессе работы приложения
Для получения адреса функции сначала следует воспользоваться директивой typedef для определения типа функций и определить переменную этого типа, например:
Можно также сослаться на функцию по порядковому номеру, по которому она экспортируется (при этом для создания библиотеки должен использоваться def-файл):
Когда приложение прекращает работать с функциями библиотеки, ее следует удалить из памяти процесса. Библиотеку динамической компоновки (вернее, ее данные) можно выгрузить из памяти процесса с помощью функции FreeLibrary :
В заключение отметим следующее:
Содержимое параграфа «Некоторые важные аспекты создания DLL»
Познакомившись с принципами работы DLL в приложениях, рассмотрим подробнее некоторые аспекты их создания.
Назначение функции DllMain
Кроме этих двух типов функций , в каждой библиотеке DLL есть функция DllMain. Эта функция предназначена для инициализации и для очистки DLL – она обязательно вызывается при первом запуске библиотеки и при завершении ее работы. Структура простейшей функции DllMain может выглядеть, например, так:
Замечание. Перед продолжением изложения материала следует отметить, что одна и та же программа (приложение) может быть загружена несколько раз, и ее экземпляры работают как отдельные процесс.
Если инициализация проходит удачно, возвращаемым значением функции DllMain должно быть ненулевое значение. Нулевое возвращаемое значение приводит к тому, что Windows не загрузит библиотеку в память.
Экспортирование функций из DLL
Есть два способа занесения информации об этих функциях в такую таблицу на этапе компиляции.
Следующий def-файл содержит имя и описание библиотеки, а также список экспортируемых функций :
Замечание. На самом деле компилятор присваивает номера всем экспортируемым объектам, даже если строка описана так
При использовании вышеприведенного def-файла исходный текст DLL-библиотеки может быть таким :
Разделяемая память в DLL
Допустим, имеется массив целых чисел, который должен использоваться всеми процессами, загружающими некоторую библиотеку DLL. Это можно запрограммировать в коде библиотеки следующим образом:
Некоторые ограничения DLL
Динамически подключаемая библиотека может загружать ресурсы (такие, как пиктограммы, строки и битовые образы) либо из файла библиотеки, либо из файла программы, которая вызывает библиотеку. Функциям, которые загружают ресурсы, требуется дескриптор экземпляра модуля.
· Если библиотека использует собственный дескриптор экземпляра (который передается библиотеке при инициализации), то библиотека может получить ресурсы из собственного файла.
Источники информации:
- http://codeby.net/threads/kak-sozdat-dll-biblioteku.65655/
- http://docs.microsoft.com/ru-ru/office/client-developer/excel/developing-dlls
- http://docs.microsoft.com/ru-ru/cpp/build/dlls-in-visual-cpp?view=msvc-170
- http://docs.microsoft.com/ru-ru/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-160&viewFallbackFrom=vs-2019
- http://docs.microsoft.com/ru-ru/cpp/build/creating-a-resource-only-dll?view=msvc-170
- http://citforum.ru/book/cook/dll0.shtml
- http://docs.microsoft.com/ru-ru/troubleshoot/windows-client/deployment/dynamic-link-library
- http://codenet.ru/progr/visualc/Using-DLL.php
- http://www.ishodniki.ru/art/art_progr/builder/224.html