Создание и внедрение в программу библиотеки 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: файл P.cpp
Не забудьте об объявлениях в начале файла. Зайдите в менеджер проектов.Там откройте свой проект и добавте .lib файл из предыдушего проект с DLL( правый клик, пункт ADD). Запустите проект.
Как видите, для того, чтобы вашу DLL можно было использовать необходимо три файла: сама DLL, заголовочный файл и библиотечный файл .lib.
Динамическая загрузка
Динамическая загрузка горазда сложнее. Однако для динамической загрузки требуется только сама DLL ( не ненужен ни .lib ни заголовочный файл, хотя его можно исполбзовать для описания экспортируемых функций для предполагемого пользователя).
Давайте рассмотрим на примере, как производится динамическая загрузка. Создайте новый прокт DLL и внесите в него следующее:
Cкомпилируйте проект, в результате чего будет создана DLL.
Теперь создайте проект приложения анологичный проекту для использования статической загрузки (форма с кнопкой и обработчиком события кнопки OnClick) ниже приведен код приложения:(Unit11.cpp)
запустите это проект, при нажатии на кнопку должно выдаватся сообшение. Теперь разберемся, как это работает.
- void (__stdcall *Message)(char *s);-объявление указателя на функцию.
- HINSTANCE dllp = LoadLibrary("p.dll");- загрузка библиотеки в память.
- Message= (void(__stdcall *) (char*)) GetProcAddress(dllp, "_Message"); присвоение указателю адреса функции DLL.
- Message("Hi From Dinamic DLL"); рабочий вызов фунциий (собственно то для чего все это и делается).
- FreeLibrary(dllp);- выгрузка библиотеки из памяти.
Обратите внимание на то, что призагрузке можно указать точное местоположние библиотеки (необезательно в том же каталоге где и приложение).
В Windows библиотека динамической компоновки (DLL) является исполняемым файлом, который выступает в качестве общей библиотеки функций и ресурсов. Динамическая компоновка — это возможность операционной системы. Она позволяет исполняемому файлу вызывать функции или использовать ресурсы, хранящиеся в отдельном файле. Эти функции и ресурсы можно компилировать и развертывать отдельно от использующих их исполняемых файлов.
Библиотека DLL не является отдельным исполняемым файлом. Библиотеки DLL выполняются в контексте приложений, которые их вызывают. Операционная система загружает библиотеку DLL в область памяти приложения. Это делается либо при загрузке приложения (неявная компоновка), либо по запросу во время выполнения (явная компоновка). Библиотеки DLL также упрощают совместное использование функций и ресурсов различными исполняемыми файлами. Несколько приложений могут осуществлять одновременный доступ к содержимому одной копии библиотеки DLL в памяти.
Различия между динамической и статической компоновкой
При статической компоновке весь код объектов копируется из статической библиотеки в использующие их исполняемые файлы во время сборки. При динамической компоновке включаются только те сведения, которые позволяют Windows найти и загрузить библиотеку DLL, содержащую элемент данных или функцию, во время выполнения. При создании библиотеки DLL также создается библиотека импорта, содержащая эту информацию. При сборке исполняемого файла, который вызывает библиотеку DLL, компоновщик использует экспортированные символы в библиотеке импорта, чтобы сохранить эти сведения для загрузчика Windows. Когда загрузчик загружает библиотеку DLL, она сопоставляется с областью памяти приложения. Для выполнения операций инициализации, необходимых библиотеке DLL, вызывается специальная функция DllMain из библиотеки 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.
Функции FreeLibrary и AfxFreeLibrary
Описывается использование функций FreeLibrary и AfxFreeLibrary , когда модуль 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.
Версия библиотеки DLL MFC
Описывается механизм использования общих библиотек динамической компоновки MFCxx.dll и MFCxxD.dll (где x является номером версии MFC) с приложениями MFC и расширениями библиотек DLL MFC.
Библиотека DynLib предоставляет удобные средства для разработчиков, использующих межмодульное взаимодействие (EXEDLL, DLLDLL) в своих проектах, и значительно сокращает время и количество кода.
DynLib стала неотъемлемым инструментом разработки. Под катом делимся результатами.
Недостатки традиционного подхода к реализации DLL
- отсутствие возможности использовать пространства имен
- большое количество служебного кода, необходимого:
- при реализации динамической загрузки библиотек;
- при реализации межмодульного взаимодействия через классы, за счет использования декскрипторов (или иных неявных структур) и классов-оберток;
- при реализации механизмов возвращения ошибки, в случае, когда экспортируемые функции могут генерировать исключения.
Примеры использования DynLib
1. Использование обычной DLL
Задача. Динамически подключить и использовать библиотеку test_lib.dll, реализующую простые математические операции, с интерфейсом, представленным в заголовочном файле:
Решение. Необходимо написать следующий заголовочный файл и подключить его к проекту.
Препроцессор сгенерирует класс test::lib, выполняющий динамическую загрузку DLL и содержащий перечисленные функции sum, mul и epsilon. Для подключения DLL к приложению необходимо включить представленный заголовочный файл test_lib.hpp в исходный код. Далее следует создать объект класса test::lib. Доступ к экспортируемых функциям DLL возможен через '.' или '->'.
2. Создание библиотеки calculator.dll
Задача. Написать библиотеку calculator.dll, которая должна вычислять сумму, произведение двух чисел и значение квадратного корня. Динамически загрузить библиотеку и вызвать каждую функцию.
Решение
4. Реализация библиотеки shapes.dll. Использование интерфейсов.
Задача. Создать библиотеку shapes.dll по работе с геометрическими фигурами (квадрат, прямоугольник, круг). Все фигуры должны поддерживать общий интерфейс, через который можно узнать координаты центра фигуры.
Решение
Как подключить библиотеку
Библиотека поставляется в виде заголовочных файлов. Никаких .lib и .dll не требуется. Для подключения требуется добавить следующую директиву:
Элементы библиотеки
Многие классы и макросы библиотеки DynLib могут использоваться самостоятельно и отдельно друг от друга.
DL_BLOCK
Служит контейнером для всех остальных макросов.
DL_NS_BLOCK
Служит контейнером для всех остальных макросов. Создает пространства имен для класса.
Макросы, которые описаны ниже кроме DL_EXPORT, должны быть помещены в DL_BLOCK или DL_NS_BLOCK
DL_C_LIBRARY
Назначение макроса — предоставить пользователю готовый класс, реализующий динамическую загрузку DLL и автоматический импорт функций. Макрос представлен как:
- lib_class — имя класса, реализацию которого генерирует библиотека DynLib;
- functions — перечисление функций, экспортируемых DLL. задается через список следующего формата
(ret_type, call, (name, import_name), arguments)- ret_type — тип возвращаемого функцией значения;
- call — формат вызова, например: __sdtcall, __cdecl и т.п.;
- name — имя функции (для пользователя);
- import_name — имя функции, заданной в таблице экспорта DLL, включая декорацию (если она есть). Если name и import_name совпадают, то import_name можно не указывать.
- arguments — список (тип аргумента, имя аргумента, = значение по умолчанию), задающий входные аргументы. Имя аргумента и значение по умолчанию можно не указывать.;
DL_RECORD
Макрос DL_RECORD генерирует упакованную структуру данных для использования в межмодульном взаимодействии. Дополнительно создается конструктор со всеми перечисленными в макросе аргументами.
DL_LIBRARY
- выступает в роли описания (документирования) интерфейса между EXE(DLL) и DLL;
- содержит необходимые структуры для автоматического экспорта функций библиотеки для разработчика;
- реализует класс, обеспечивающий загрузку DLL с заданным интерфейсом и предоставляющий доступ к экспортируемым функциям со стороны пользователя;
- обеспечивает корректное использование C++ исключений:
DL_EXPORT
- lib_class — полное имя класса, описывающего интерфейс взаимодействия (то имя класса, что использовалось в DL_LIBRARY);
- lib_impl_class — полное имя класса класса, РЕАЛИЗУЮЩЕГО функции, указанные в интерфейсе взаимодействия.
- Создать класс (структуру);
- Определить каждую функцию из интерфейса как статическую. Функции должны находиться в области видимости public:;
- Произвести экспорт функций, написав конструкцию DL_EXPORT(lib, impl).
DL_INTERFACE
Макрос позволяет описать интерфейс класса и предоставить пользователю класс-обертку для работы с ним. Реализация класса-обертки обеспечивает корректное использование C++ исключений: Класс-обертка, генерируемая данным макросом, имеет разделяемое владение объектом, реализующего данный интерфейс. Разделяемое владение обеспечивается механизмом подсчета ссылок, т.е. когда происходит копирование объектов класса-обертки, вызывается внутренняя функция для увеличения счетчика ссылок, при уничтожении — внутренняя функция по уменьшению счетчика ссылок. При достижении счетчиком значения 0 происходит автоматическое удаление объекта. Доступ к методам интерфейса осуществляется через '.' или '->'.
Библиотека DynLib гарантирует безопасное использование классов-интерфейсов на границе EXE(DLL)DLL- interface_class — имя класса, реализацию которого генерирует библиотека DynLib;
- methods — перечисление функций, описывающих интерфейс класса,
dl::shared
- динамическое создание объекта класса T с аргументами, переданными в конструкторе;
- добавление счетчика ссылок и обеспечение разделяемого владения (подобно boost(std)::shared_ptr);
- неявное приведение к объекту класса, генерируемого макросом DL_INTERFACE.
Примеры использования dl::shared представлены ниже:
dl::ref
Функция библиотеки, позволяющая привести любой объект к объекту класса-интерфейса, объявленному через DL_INTERFACE, с идентичным набором методов. Обычно такое поведение необходимо, когда имеется функция, принимающая в качестве аргумента класс-интерфейс, а ему следует передать объект, размещенный в стеке.
Использовать функцию dl::ref нужно с осторожностью, поскольку объекты классов-интерфейсов, в этом случае не будут владеть переданными объектами, а управление временем жизни объекта и его использованием через классы-интерфейсы ложится на пользователя. Копирование объектов классов-интерфейсов, ссылающих на объекты, переданные через dl::ref, разрешено и вполне корректно (поскольку счетчика ссылок нет, то и изменять нечего — объекты классы-интерфейсов знают как здесь корректно работать).Передо мной возникла задача написать загрузчик библиотек, имеющий возможность предоставить какие-то интерфейсные функции внешней динамической библиотеке. Решение должно быть максимально кроссплатформенно (как минимум, работать на Linux и Windows). Загружаться должны библиотеки, написанные на различных языках программирования, поддерживающих создание динамических библиотек. В качестве примера были выбраны языки C и Pascal.
Решение
Основной загрузчик библиотек написан на языке C. Для того, чтобы загружаемые библиотеки имели возможность использовать функции основной программы, основная программа разделена на 2 части: на основной и подгружаемый модули. Основной модуль нужен просто для запуска программы, подгружаемый модуль — это также динамическая библиотека, связываемая с основным модулем во время его запуска. В качестве компиляторов были выбраны gcc (MinGW для Windows) и fpc.
Здесь будет приведён упрощённый пример программы, позволяющий разобраться в данном вопросе и учить первокурсников писать модули к своей программе (в школе часто преподают именно Pascal).Загрузчик библиотек
main.c
А это модуль, отвечающий за загрузку динамических библиотек, который сам вынесен в динамическую библиотеку для того, чтобы подгружаемые библиотеки имели возможность использовать предоставляемые им функции:loader.c
Заголовочные файлы
Это всё была реализация, а теперь заголовочные файлы.
Вот интерфейс модуля, загружающего динамические библиотеки, для основного модуля:loader.h
Вот интерфейс загрузчика для загружаемых им динамических библиотек (перечень функций, которые динамические библиотеки могут использовать в основной программе):functions.h
Как видно, здесь всего одна функция printString для примера.
Компиляция загрузчика
Пример недистрибутивной компиляции (в случае Windows в опции компилятора просто нужно добавить -DWIN32):
Дистрибутивная компиляция от недистрибутивной отличается тем, что в дистрибутивном случае динамические библиотеки ищутся в /usr/lib и имеют вид lib$(NAME).so.$(VERSION), в случае недистрибутивной компиляции они называются lib$(NAME).so, а ищутся в каталоге запуска программы.
Теперь посмотрим, что у нас получилось после компиляции:
Тут видим, что функции, отмечаемые как U ищутся во внешних динамических библиотеках, а функции, отмечаемые как T предоставляются модулем. Это бинарный интерфейс программы (ABI).
Динамические библиотеки
Библиотека на языке C
Здесь везде окружение extern «C» <> нужно для того, чтобы нашу программу можно было компилировать при помощи C++-компилятора, такого, как g++. Просто в C++ можно объявлять функции с одним и тем же именем, но с разной сигнатурой, соответственно в этом случае используется так называемая декорация имён функций, то есть сигнатура функций записывается в ABI. Окружение extern «C» <> нужно для того, чтобы не использовалась эта декорация (тем более, что эта декорация зависит от используемого компилятора).
Компиляция
Если мы уберём в нашем модуле окружение extern «C» <> и скомпилируем при помощи g++ вместо gcc, то увидим следующее:
То есть, как и ожидалось, ABI библиотеки изменился, теперь наш загрузчик не сможет увидеть функцию run в этой библиотеке:
Библиотека на языке Pascal
Как мы увидели выше, для того, чтобы наш загрузчик видел функции в динамических библиотеках, созданных компилятором C++, пришлось дополнять наш код вставками extern «C» <>, это притом, что C/C++-компиляторы и языки родственные. Что уж говорить про компилятор FreePascal совершенно другого языка — Pascal? Естественно, что и тут без дополнительных телодвижений не обойтись.
Для начала нам нужно научиться использовать экспортированные в C функции для динамических библиотек. Вот пример аналогичного C/C++ заголовочного файла на языке Pascal:
func.pas
Вот пример самого модуля на языке Pascal:modul.pas
Компиляция
Смотрим ABI получившейся библиотеки:
Как видим, ничего лишнего, однако настораживает предупреждение ld во время компиляции. Находим в гугле возможную причину предупреждения, это связано с компиляцией без PIC (Position Independent Code — код не привязан к физическому адресу), однако в man fpc находим, что наша опция -Cg должна генерировать PIC-код, что само по себе странно, видимо fpc не выполняет своих обещаний, либо я делаю что-то не так.
Теперь попробуем убрать в нашем заголовочном файле кусок name 'printString' и посмотрим, что выдаст компилятор теперь:
Как видно, декорация в FreePascal совсем другого рода, чем в g++ и тоже присутствует.
При запуске с этим модулем получаем:А с правильным модулем получаем:
Вот и всё, своей задачи — использование динамических библиотек, написанных на различных языках — мы достигли, и наш код работает как под Linux, так и под Windows (у самого Mac'а нет, поэтому не смотрел, как там с этим). Кроме того, загруженные библиотеки имеют возможность использовать предоставляемые в основной программе функции для взаимодействия с ней (то есть являются плагинами основной программы).
Сам загрузчик может выполняться в отдельном процессе, чтобы эти плагины не смогли сделать ничего лишнего с основной программой. Соответственно, основная программа может быть написана на любом другом удобном языке программирования (например, на Java или на том же C++).
Metastock – наверное, самая известная программа для технического анализа рынка. Данная программа может подключать внешние библиотеки DLL, написанные пользователями для создания своих торговых стратегий, используя полную мощь традиционных языков программирования, таких как C или Паскаль.
Занявшись поиском в интернете, с удивлением обнаружил полное отсутствие информации по данной теме. Единственная статья, которая оказалась достойной внимания: “Что такое Metastock Developer's Kit?” (mdk), где описывается коротенький пример на Delphi, там же можно скачать MDK.
В данной статье я постараюсь заполнить этот пробел и описать процесс создания библиотеки внешних функций Metastock (MSX DLL) по шагам на языке C/C++. Все примеры писались в среде Visual Studio 2010 (часть2, часть3).
Немного теории
Функции, которые реализованы в MSX DLL ведут себя точно также как и стандартные встроенные функции Metastock’а. Все функции MSX DLL возвращают массив данных. Каждая внешняя функция имеет уникальное имя.
ExtFml(«DLL Name.Function Name»,arg1,…,argn), где
arg1…argn – аргументы функции.
Каждый аргумент может быть одним из четырех типов:
• Массивы данных (например, Open, High, Low, Close, и т.д., или результаты другой функции)
• Числовые константы (например, 5, -5, 20.55 и т.д.)
• Строковые константы (например, “Hello Woodpecker” и др.)
• Индивидуальные наборы (например, Simple, Triangular, и т.д.)
Как их определять и использовать мы рассмотрим позже, на конкретных примерах.Функции, определенные в MSX DLL делятся на две категории:
• Функции инициализации
• Функции расчета (или внешние функции).
Функции инициализации вызываются MetaStock’ом во время запуска, чтобы определить, какие внешние функции доступны и какие аргументы они требуют. Функции расчета доступны для пользователей MetaStock’а. Все функции ссылаются на структуры данных определенных в файле MSXStruc.h. Этот файл необходим для компиляции нашей DLL.
Прежде чем писать свои функции необходимо прописать несколько служебных MSX-функций (функции инициализации), для того что бы Mетосток мог общаться с вашей DLL. Их четыре:
• MSXInfo — обязательная функция. Всегда вызывается во время инициализации и проверяет, является ли наша DLL MSX DLL и возвращает основную информацию о ней (авторство, количество наших функций, версия).
• MSXNthFunction — обязательная функция. Вызывается один раз для каждой функции указанной MSXInfo и нумерует, начиная с нуля наши функции. Здесь прописываются имена функций (c учетом регистра), их дескриптор и количество аргументов у каждой.
• MSXNthArg – эта функция обязательна, только если у наших функций есть аргументы. Вызывается во время инициализации для каждого аргумента наших функций.
• MSXNthCustomString – эта функция обязательна, только если у наших функций есть custom-аргументы.Для начала теории достаточно, приступим к написанию первой DLL. В этом примере будет показано, как вывести массив цен, дату и время в наш индикатор.
Пишем код
Открываем VS 2010. Первым делом создадим пустой проект.
File->New->Project->Other Languages->Visual C++->Win32->Win32 Console Application.
Задаем имя нашей библиотеки (пусть UsePrice) и нажимаем OK.Открывается Win32 Application Wizard.
Next-> и ставим галочки на DLL и Empty project, нажимаем Finish. Пустой проект создан. Добавим в него три файла UsePrice.cpp, UsePrice.def, MSXStruc.h. Правой кнопкой по проекту Add->New Item…, выбираем файл с соответствующим расширением и задаем ему соответствующее имя. Нажимаем Add.
В файле UsePrice.cpp пишем наш код.Заголовки для библиотечных функций C
Обратим внимание, что в данном операторе отсутствует точка с запятой. Между
идентификатором и последовательностью символов может быть любое число
пробелов. Макрос завершается только переходом на новую строку.Например, если необходимо использовать TRUE для значения 1, a FALSE для 0,
то можно объявить следующие два макроса:В результате, если компилятор обнаружит в тексте программы TRUE или FALSE, то
он заменит их на 1 и 0 соответственно.
В нашем случае если в коде встречается DLL_EXPORT, то выполняется макросextern «C» __declspec(dllexport)
Чтобы нормально спрягать код на C++ с C, где name mangling отсутствует, введено
extern «C», которое отключает этот механизм у экспортируемых имен переменных/функций.
extern «C» обозначает использование простой генерации сигнатуры функции
(в стиле языка С) при получении объектных файлов. В частности, это запрещает
компилятору C++ производить «декорацию» имени функции дополнительными
символами при экспорте в DLL.
Атрибут класса хранения dllexport — это специфическое для Microsoft расширение
языков C и C++. Его можно использовать для экспорта функций, данных и объектов
в библиотеку DLL.
__declspec( dllexport ) declarator
Этот атрибут явно определяют интерфейс DLL для ее клиента, который может быть
исполняемым файлом или другой библиотекой DLL. Объявление функций как
dllexport позволяет обходиться без файла определения модуля (DEF), по крайней
мере, в отношении спецификации экспортированных функций./*
— Шаг 3 – Функции инициализацииПрежде чем писать свои функции необходимо прописать несколько служебных
MSX-функций, для того, чтобы Mетосток мог общаться с нашей DLL.
В первом примере мы будем писать одну функцию без аргументов, поэтому
MSXNthArg и MSXNthCustomString нам не понадобятся.MSXInfo
Замените поле вашей
информацией об авторстве, а также установите количество функций = 1.
strncpy (strDest,strSource,count) — копирует символы одной строки в другую.
• strDest — строка назначения.
• strSource — иссходная строка.
• count — число символов для копирования.
strncpy требует обязательный заголовок .
На strncpy компилятор VS C++ выдает предупреждения, можно использовать
strncpy_s (проверял — все работает, но не могу сказать насколько это корректно).
MSXNthFunction
Значение, копируемое в a_sFuncDef->szFunctionName должно точно соответствовать
нашей функции, которую мы будете экспортировать, с учетом регистра.
Изменяемые значения:
• Name — имя нашей функции.
• Description — то, как она будет читаться в Metastock'е (в окне 'Paste Functions').
• Arguments — число аргументов нашей функции.
На strcpy компилятор VS C++ выдает предупреждения, можно использовать strcpy_s
(проверял — все работает, но не могу сказать насколько это корректно).
/*
— Шаг 4 – Наша функцияДалее в файле UsePrice.def введем следующий код:
В Visual Studio нажимаем Build -> Build Solution (F6) и получаем нашу DLL. Отправляем ее в папку ‘External Function DLLs’ в Metastock’e и можно пользоваться. Наш индикатор будет иметь следующий вид:
ExtFml(«UsePrice.Price»)
В следующей части мы рассмотрим нашу функцию, подключим аргументы, добавим исключения и выведем данные во внешнюю среду.
Читайте также: