Как писать dll файлы

Сейчас мы рассмотрим для чего нужны DLL (Dynamic Link Library — динамически компануемая библиотека) и как их создавать. DLL- это участок кода хранимый в файле с расширением .dll. Код может быть использован другими программами, но сама посебе библиотека прораммой не является. Вобщем-то, динамически компонуемые библиотеки представляют собой набао скомпилированныых функций. Но у ютих библиотек есть свой особенности, так например, если каккието две или более программы для Windows одновременно исполняются и используют функции, находящиеся в одной DLL, то в памяти будет постоянно находится только одна библиотека, обеспечивая тем самым экономное расходование памяти. Загрузка библиотеки в память может быть статической и динамической.

При статической загрузке DLL автоматически загружается при запуске исользующего ее приложения. Такая DLL содержит экспортируемые функции, описание которых находится в файле библиотеки импорта(import library file — .lib). Для использования статической загрузки вы должны на этапе компоновки к программе додключить .lib файл вашей DLL. В C++ Builder это сводится к включения в проект .lib файла через менджер проектов.

При диамической загрузке вы можете загружать 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. Ваш исполняемый файл должен представлять собой слдующее:

Не забудьте об объявлениях в начале файла. Зайдите в менеджер проектов.Там откройте свой проект и добавте .lib файл из предыдушего проект с DLL( правый клик, пункт ADD). Запустите проект.

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

Динамическая загрузка

Динамическая загрузка горазда сложнее. Однако для динамической загрузки требуется только сама DLL ( не ненужен ни .lib ни заголовочный файл, хотя его можно исполбзовать для описания экспортируемых функций для предполагемого пользователя).

Давайте рассмотрим на примере, как производится динамическая загрузка. Создайте новый прокт DLL и внесите в него следующее:

Cкомпилируйте проект, в результате чего будет создана DLL.

Теперь создайте проект приложения анологичный проекту для использования статической загрузки (форма с кнопкой и обработчиком события кнопки OnClick) ниже приведен код приложения:(Unit11.cpp)

запустите это проект, при нажатии на кнопку должно выдаватся сообшение. Теперь разберемся, как это работает.

  • vo ); рабочий вызов фунциий (собственно то для чего все это и делается).
  • FreeLibrary(dllp);— выгрузка библиотеки из памяти.

Обратите внимание на то, что призагрузке можно указать точное местоположние библиотеки (необезательно в том же каталоге где и приложение).

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

Я постараюсь за 10 минут на примерах показать весь процесс создания простых DLL библиотек и раскрою некоторые технические детали нашей реализации связывания. Демонстрация будет на примере Visual Studio 2005 / 2008, бесплатные Express-версии которых можно свободно скачать с сайта Microsoft.

Читайте также:  Как поменять геолокацию в перископе

1. Создание проекта DLL на С++ в Visual Studio 2005/2008

Запустите визард через меню ‘File -> New‘, выберите тип проекта ‘Visual C++‘, шаблон ‘Win32 Console Application‘ и укажите имя проекта (например, ‘MQL5DLLSamples‘). Выберите отдельный корневой каталог хранения проектов ‘Location‘ вместо предлагаемого по умолчанию, отключите галочку ‘Create directory for solution‘ и нажмите на кнопку ‘OK‘:

Рис 1. Win32 Application Wizard, создание проекта DLL

На следующем шаге просто нажмите на кнопку ‘Next‘ для перехода на страницу настроек:

Рис 2. Win32 Application Wizard, параметры проекта

На финальной странице выберите тип ‘DLL‘, оставив остальные поля пустыми как есть, и нажмите на ‘Finish‘. Ставить галочку на ‘Export symbols‘ не нужно, чтобы потом не удалять автоматически добавленный демонстрационный код:

Рис 3. Win32 Application Wizard, н астройка свойств приложения

В результате получите пустой проект:

Рис 4. Пустой проект DLL

Для удобства тестирования лучше всего прямо в настройках ‘Output Directory‘ указать выкладку DLL файлов напрямую в каталог ‘. MQL5Libraries‘ клиентского терминала. Это сэкономит много времени в последующей работе:

Рис 5. Каталог выкладки DLL файлов

2. Подготовка к добавлению функций

Добавьте макрос ‘_DLLAPI‘ в конец файла stdafx.h, чтобы можно было удобно и просто описывать экспортируемые функции:

В вызовах функций MQL5 используется соглашение о связях __stdcall и __cdecl. Хотя вызовы stdcall и cdecl отличаются вариантами извлечения параметров со стека, но исполняющая среда MQL5 позволяет безболезненно использовать оба варианта за счет специального враппера DLL вызовов.

По умолчанию в настройках компилятора С++ для функций используется __cdecl, но я рекомендую для экспортируемых функций явным образом указывать режим __stdcall.

Правильно оформленная экспортная функции должна иметь следующий вид:

а в MQL5 программе описываться и вызываться так:

После сборки проекта DLL эта stdcall функция будет видна в таблице экспорта под именем _fnCalculateSpeed@8, где компилятором добавляются знак подчеркивания и количество передаваемых через стек данных в байтах. Такое декорирование позволяет лучше контролировать безопасность вызовов DLL функций за счет того, что вызывающая сторона точно знает, сколько (но не каких!) данных нужно помещать в стек.

Если при описании импорта DLL функции будет ошибка в итоговом размере блока параметров, то функция не будет вызвана, а в журнале появится сообщение вида ‘Cannot find ‘fnCrashTestParametersStdCall’ in ‘MQL5DLLSamples.dll’‘. В этом случае надо тщательно перепроверить все параметры как в протопите функции, так и в самой DLL.

При отсутствии полного имени функции в таблице экспорта для совместимости используется поиск упрощенного описания без декорирования. Такие имена вида fnCalculateSpeed создаются при описаниях функции в формате __cdecl:

3. Способы передачи параметров и обмен данными

Давайте посмотрим на несколько вариантов передаваемых параметров:

  1. Прием и передача простых переменных
    С простыми переменными все просто — их можно передавать по значению или по ссылке через &.
    Вызов из MQL5:
    Результат:
  2. Прием и передача массива с заполнением элементов

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

Вызов из MQL5:
Результат:

Передача и модификация строк
Строки (unicode) также передаются через прямые ссылки на рабочие буферы без служебной информации. Обратите внимание, что функция для примера описана в формате cdecl:
Вызов из MQL5:
Результат:
Оказалось, что строка не изменилась! Это обычная ошибка начинающих программистов, когда они передают копии объектов (а string — это объект) вместо ссылки на них. Для строки ‘text’ была автоматически создана копия, которая была модифицирована в DLL, а затем копия также автоматически удалилась, не затронув оригинал.

Чтобы исправить ситуацию, надо передавать строку по ссылке. Для этого просто модифицируем блок импорта, добавив знак & к параметру text:
После перекомпиляции и запуска получим правильный результат:

4. Перехват исключений в DLL функциях

Чтобы избежать падения самого терминала, каждый вызов функций DLL автоматически защищается оберткой Unhandled Exception. Этот механизм позволяет уберечься от большинства стандартных ошибок (обращения в недоступную память, деления на ноль и т.д.)

Читайте также:  Как перевести картинку с телефона на компьютер

Для проверки работоспособности этого механизма создадим следующий код:

и вызовем его из терминала:

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

5. Враппер DLL вызовов и потери скорости на вызовах

Как уже было рассказано выше, для обеспечения безопасности каждый вызов DLL функции оборачивается в специальный враппер. Эта обвязка маскирует основной код, подменяет стек, поддерживает stdcall/cdecl соглашения и контролирует исключения внутри вызываемых функций.

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

6. Финальная сборка

Соберите все вышеприведенные примеры DLL функций в файле ‘MQL5DLLSamples.cpp‘, а MQL5 примеры в скрипт ‘MQL5DLL Test.mq5‘. Готовый проект для Visual Studio 2008 и скрипт на MQL5 приложены к статье.

Спасибо за внимание! Буду рад ответить на вопросы.

Active member

Cоздание файла dll

Очень часто в своей работе, Вы будете сталкиваться с такой ситуацией.

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

Через пару недель программа была написана, затем она попала в Интернет, пользователи оценили новый продукт и стали им пользоваться. Цель достигнута.

Проходит время и перед вами ставят новую задачу, написать программу "Супер парсер". Одной из функции данной программы, будет подсчет слов в тексте. Вы понимаете, что снова придется разрабатывать метод, который будет вести подсчёт слов. Но, при этом вспоминаете, что совсем не давно уже разрабатывали программу, в которой применялась данная функция. Чтобы не изобретать велосипед, Вы открываете исходник программы "Супер блокнот"; и копируете весь метод в исходник новой программы "Супер парсер". Отлично, теперь Вам не придется тратить время на написание этого метода заново, и Вы можете посветить больше времени остальным элементам программы. Задача решена.

Но, что если метод по подсчету слов, писали не Вы, а допустим, какой-нибудь коллега по работе и по каким-то причинам, Вы не можете получить доступ к исходному коду программы "Супер блокнот". То есть первый вариант, копирование метода из исходника, не прокатит и данный метод придется писать самому ммм, печалька.

Но, тут вам звонит ваш коллега по работе и говорит: Ты знаешь, я тут вспомнил, когда я разрабатывал данный метод, я подумал, что возможно мне придется его использовать ещё где-то, и по этому я решил вынести его в отдельную сборку, в виде файла динамической библиотеки (dll).Ты просто скопируй этот файл dll в свой проект, и подключи его, как внешнюю сборку, после чего ты получишь доступ к моему методу и сможешь им пользоваться.

Отлично! Вы проделываете все описанные действия, в программе “Супер парсер” появляется нужный метод, задача решена и вам вновь не пришлось повторно писать код руками.

На этом присказка закончена и теперь переходим к более подробному изучению.

DLL (dynamic-link library) — это динамически подключаемая библиотека, или сокращено динамическая библиотека.

Как уже писал ранее, динамические библиотеки, позволяют повторно использовать ранее написанный код, а так же они обеспечивают лучшую переносимость кода. Достаточно, скинуть файл на флешку, или скачать dll файл из Интернета, после чего добавить его в текущий проект и тут же получить набор разных дополнительных функций для вашего приложения. Так же стоит знать, что в одном dll файле может храниться любое количество типов, членов и пространств имён.

Создание файла dll

Для начала выберем тип нового создаваемого приложения, а точнее проекта.

Выбираем Class Library, то есть создаем файл динамической библиотеки (dll)

Так же Вы можете указать, под какую версию Фреймворка будет создаваться данный проект.

После того, как Visual Studio создаст каркас проекта, Вы увидите следующее:

Так будет выглядеть окно Solution Explorer

А так будет выглядеть рабочая область, где Вы обычно пишите код программы

И так дано пространство имён: Car и класс: Class1. Class1 не удачное название, давайте немного изменим наш код, заменив Class1 на BMW, и добавим метод, который будет выводить имя нашего класса.

Читайте также:  Как изменить слайды в презентации powerpoint

И так код написан, и теперь необходимо выполнить компиляцию, чтобы получить сборку.
Если сейчас попытаться нажать F5 или Ctrl+F5, то вы увидите данное окно

Данная ошибка, говорит лишь о том, что был создан файл динамической библиотеки (dll), а не исполняемый файл (exe), который не может быть запущен.

Для того, чтобы скомпилировать проект, нажмите клавишу F6, после чего в директории binDebug появиться файл Car.dll.

Чтобы проверить был ли создан файл библиотеки, воспользуйтесь кнопкой Show All Files на вкладке Solution Explorer

Сборка, в виде файла динамической библиотеки успешно создана.

Теперь перейдем в папку binDebug, для того, чтобы быстро переместиться в текущую директорию проекта, в том же Solution Explorer воспользуйтесь пунктом Open Folder in Windows Explorer

Скопируйте полученный файл сборки (в нашем случае — это файл Car.dll) в какую-нибудь временную папку. На самом деле, это делать необязательно, Вы можете оставить данный файл в этой папке, но для удобства, создадим новую папку, и поместим туда созданный файл библиотеки.

После чего закроем текущий проект и создадим новый. Но, на этот раз выберем тип проекта — консольное приложение.

Создаем новый проект.

Новый проект создан. Теперь подключим в текущий проект, нашу библиотеку (Car.dll)

Для этого на папке References, в окне Solution Explorer нужно нажать правую кнопку мыши и выбрать пункт Add Reference, откроется вот такое окно:

  1. Выберите вкладку Browse
  2. Укажите папку, в которой лежит файл созданной библиотеки (в нашем примере — Car.dll)
  3. Выделите файл нужной библиотеки и нажмите кнопку ОК;

Если всё выполнено, верно, Вы увидите следующую картинку

На ней видно, что в наш текущий проект была успешна, добавлена ссылка на нашу сборку Car.dll, в которой храниться наш код на языке IL. Надеюсь, Вы помните, что после компиляции весь написанный вами код преобразуется в промежуточный код на языке IL (CIL, MSIL — это одно и тоже). А так же видно, что в папку binDebug была помещёна копия нашей библиотеки.

Если вдруг Вы забыли, какое пространство имен, типы, или члены содержит ваша созданная библиотека. Вы всегда можете воспользоваться таким инструментом Visual Studio, как Object Browser. Для этого перейдите на вкладку Solution Explorer, откройте папку References, и просто щёлкните правой кнопкой мыши по добавленной ранее библиотеке, в нашем случае напоминаю — это файл (Car.dll) и выберите пункт View in Object Browser, появиться вот такое окно.

В окне Object Browser можно посмотреть содержимое нашей сборки.

Сборка подключена и теперь Вы можете работать с её содержимым. Далее выполним необязательный пункт.

Добавим, с помощью ключевого слова using пространство имен Car из созданной нами библиотеки Car.dll, после чего создадим объект класса BMW и выполним метод Вывести_Имя_Класса().

И так ещё раз по порядку:

  1. Создаем файл динамической библиотеки (dll)
  2. Подключаем созданную библиотеку в наш проект, путем добавления в папку References ссылки на наш файл dll.
  3. (Необязательный пункт) Подключаем пространство имен из подключенной сборки, с помощью ключевого слова using, либо используем полное наименование, то есть Пространство имён.Тип (Car.BMW).
  4. Profit

Эти четыре действия помогут Вам создать библиотеку dll и подключить её к своему проекту.

И в конце не много информации о типах сборок:

Сборки бывают следующих основных видов: общие и частные.

Частная сборка (private assembly)

Это файлы библиотек, как наш ранее созданный файл Car.dll, которые содержаться на протяжении всего времени в каталоге текущего приложения или любом из его подкаталогов.

Вернёмся к началу статьи.

После того, как было создано приложение “Супер парсер”, мы получили сборку в виде файла (exe). Затем мы решили протестировать нашу программу и отдаём её нашему другу, при этом Вы так же упоминаете, что если он хочет иметь дополнительные функции в программе, то ему нужно просто рядом с его exe файлом поместить файл библиотеки Car.dll. После чего он получит возможность подсчёта слов в тексте. То есть библиотека будет храниться в той же директории, что и исполняемый файл.

Общие сборки (shared assembly)

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

Adblock
detector