Как создать dll файл

Динамические библиотеки для начинающих

Как создать dll файл
(статья была опубликована в журнале “Программист”)

В наше время Windows-разработчик шагу не может ступить без динамических библиотек (Dynamic Link Library – DLL); а перед начинающими программистами, желающими разобраться в предмете, встает масса вопросов:
 

    1. как эффективно использовать чужие DLL?
    2. как создать свою собственную?
    3. какие способы загрузки DLL существуют, и чем они отличаются?
    4. как загружать ресурсы из DLL?

Обо всем этом (и многом другом) рассказывает настоящая глава. Материал рассчитан на пользователей Microsoft Visual C++, а поклонникам других языков и компиляторов придется разбираться с ключами компиляции приведенных примеров самостоятельно.
 

Создание собственной DLL

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

Другое дело – если какие-то функции используются несколькими приложениями. Тогда, поместив их в одну DLL, мы избавимся от дублирования кода и сократим общий объем приложений – и на диске, и в оперативной памяти.

Можно выносить в DLL и редко используемые функции отдельного приложения; например, немногие пользователи текстового редактора используют в документах формулы и диаграммы – так зачем же соответствующим функциям впустую “отъедать” память?
 

Загрузившему DLL процессу доступны не все ее функции, а лишь явно предоставляемые самой DLL для “внешнего мира” – т. н. экспортируемые. Функции, предназначенные сугубо для “внутреннего” пользования, экспортировать бессмысленно (хотя и не запрещено).

Чем больше функций экспортирует DLL – тем медленнее она загружается; поэтому к проектированию интерфейса (способа взаимодействия DLL с вызывающим кодом) следует отнестись повнимательнее. Хороший интерфейс интуитивно понятен программисту, немногословен и элегантен: как говорится, ни добавить, ни отнять.

Строгих рекомендаций на этот счет дать невозможно – умение приходит с опытом
 

Для экспортирования функции из DLL – перед ее описанием следует указать ключевое слово __declspec(dllexport), как показано в следующем примере:
 

// myfirstdll.c#include // Ключевое слово __declspec(dllexport)// делает функцию экспортируемой

__declspec(dllexport) void Demo(char *str)

{ // Выводим на экран переданную функции Demo строку printf(str);}

Листинг 10 Демонстрация экспорта функции из DLL
 

Для компиляции этого примера в режиме командной строки можно запустить компилятор Microsoft Visual Studio: “cl.exe myfirstdll.c /LD“. Ключ “/LD” указывает линкеру, что требуется получить именно DLL.
 

Для сборки DLL из интегрированной оболочки Microsoft Visual Studio – при создании нового проекта нужно выбрать пункт “Win32 Dynamics Link Library“, затем “An Empty DLL project“; потом перейти к закладке “File View” окна “Workspace” – и, выбрав правой клавишей мыши папку “Source Files“, добавить в проект новый файл (“Add Files to Folder“). Компиляция осуществляется как обычно (“Build” ( “Build”).
 

Если все прошло успешно – в текущей директории (или в директории Release\Debug при компиляции из оболочки) появится новый файл – “MyFirstDLL.dll”. Давайте заглянем в него через “микроскоп” – утилиту dumpbin, входящую в штатную поставку SDK и Microsoft Visual Studio: “dumpbin /EXPORTS MyFirstDLL.dll“. Ответ программы в несколько сокращенно виде должен выглядеть так:
 

Section contains the following exports for myfirst.dll 0 characteristics 0.00 version 1 ordinal base 1 number of functions 1 number of names

1 0 00001000 Demo

Получилось! Созданная нами DLL действительно экспортирует функцию “Demo” – остается только разобраться, как ее вызывать

Вызов функций из DLL

Существует два способа загрузки DLL: с явной и неявной компоновкой.
 

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

При запуске такого файла загрузчик операционной системы анализирует секцию импорта и подключает все указанные библиотеки.

Ввиду своей простоты этот способ пользуется большой популярностью; но простота – простотой, а неявной компоновке присущи определенные недостатки и ограничения:
 

  1. все подключенные DLL загружаются всегда, даже если в течение всего сеанса работы программа ни разу не обратится ни к одной из них;
  2. если хотя бы одна из требуемых DLL отсутствует (или DLL не экспортирует хотя бы одной требуемой функции) – загрузка исполняемого файла прерывается сообщением “Dynamic link library could not be found” (или что-то в этом роде) – даже если отсутствие этой DLL некритично для исполнения программы. Например, текстовой редактор мог бы вполне работать и в минимальной комплектации – без модуля печати, вывода таблиц, графиков, формул и прочих второстепенных компонентов, но если эти DLL загружаются неявной компоновкой – хочешь не хочешь, придется “тянуть” их за собой.
  3. поиск DLL происходит в следующем порядке: в каталоге, содержащем вызывающий файл; в текущем каталоге процесса; в системном каталоге %Windows%System%; в основном каталоге %Windows%; в каталогах, указанных в переменной PATH. Задать другой путь поиска невозможно (вернее – возможно, но для этого потребуется вносить изменения в системный реестр, и эти изменения окажут влияние на все процессы, исполняющиеся в системе – что не есть хорошо).

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

Зато явная компоновка позволяет подгружать DLL по мере необходимости и дает программисту возможность самостоятельно обрабатывать ситуации с отсутствием DLL. Можно пойти и дальше – не задавать имя DLL в программе явно, а сканировать такой-то каталог на предмет наличия динамических библиотек и подключать все найденные к приложению.

Именно так работает механизм поддержки plug-in’ов в популярном файл-менеджере FAR (да и не только в нем).
 

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

Загрузка DLL с неявной компоновкой

Чтобы вызвать функцию из DLL, ее необходимо объявить в вызывающем коде – либо как external (т. е. как обычную внешнюю функцию), либо предварить ключевым словом __declspec(dllimport).

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

Например, функция “Demo” из созданной нами библиотеки – “MyFirstDll” вызывается так:
 

// ImplictDll.c // Объявляем внешнюю функцию Demo

__declspec(dllimport) void Demo(char *str);

main() { // Вызываем функцию Demo из DLL Demo(“Hello, World!”); }

Листинг 11 Демонстрация вызова функции из DLL неявной компоновкой
 

Из командной строки данный пример компилируется так: “cl.exe ImplictDll.c myfirstdll.lib“, где “myfirstdll.lib” – имя библиотеки, автоматически сформированной компоновщиком при создании нашей DLL.
 

Разумеется, “чужие” DLL не всегда поставляются вместе с сопутствующими библиотеками, но их можно легко изготовить самостоятельно! На этот случай предусмотрена специальная утилита implib, поставляемая вместе с компилятором, и вызываемая так: “implib.exe Имя_файла _создаваемой_библиотеки Имя_DLL“.
 

В нашем случае – не будь у нас файла “MyFirstDLL.lib“, его пришлось бы получить так: “implib.exe MyFirstDLL.lib MyFirstDLL.dll“. Со всеми стандартными DLL, входящими в состав Windows, эту операцию проделывать не нужно, т.к. необходимые библиотеки распространяются вместе с самим компилятором.
 

Для подключения библиотеки в интегрированной среде Microsoft Visual Studio – в меню “Project” выберите пункт “Project Settings“, в открывшемся диалоговом окне перейдите к закладке “Link” и допишите имя библиотеки в конец строки “Object/Library Modules“, отделив ее от остальных символом пробела.
 

Если все прошло успешно, появится новый файл “ImplictDll.exe“, который, будучи запущенным, горделиво выведет на экран “Hello, Word!“. Это означает, что наша DLL подключена и успешно работает.
 

Заглянем внутрь: как это происходит? Запустим “dumpbin /IMPORTS ImplictDll.exe” ипосмотрим, что нам сообщит программа:
 

File Type: EXECUTABLE IMAGE Section contains the following imports:

myfirstdll.dll

404090 Import Address Table 4044C8 Import Name Table 0 time date stamp 0 Index of first forwarder reference

0 Demo

KERNEL32.dll 404000 Import Address Table 404438 Import Name Table 0 time date stamp 0 Index of first forwarder reference 19B HeapCreate 2BF VirtualFree CA GetCommandLineA 174 GetVersion 7D ExitProcess 29E TerminateProcess F7 GetCurrentProcess

Вот она – “Myfirstdll.dll” (в тексте выделена жирным шрифтом), и вот функция “Demo“, а кроме нее – обнаруживает свое присутствие библиотека KERNEL32.

DLL – она необходима RTL-коду (Run Time Library – библиотека времени исполнения), насильно помещенному компилятором в наше приложение.

RTL-код обеспечивает работу с динамической памятью (heap), считывает аргументы командной строки, проверяет версию Windows и многое-многое другое! Отсюда и появляются в таблице импорта функции HeapCreate, GetCommandLine, GetVersion и т.д. Так что – не удивляйтесь, увидев “левый” импорт в своем приложении!
 

Проследить, как именно происходит загрузка DLL, можно с помощью отладчика. Общепризнанный лидер – это, конечно, SoftIce от NuMega, но для наших экспериментов вполне сойдет и штатный отладчик Microsoft Visual Studio. Откомпилировав нашу вызывающую программу, нажмем для пошагового прогона приложения
 

Оппаньки! Не успело еще выполниться ни строчки кода, как в окне “output” отладчика появились следующие строки, свидетельствующие о загрузке внешних DLL: NTDLL.DLL, MyFirstDll.dll и Kernel32.dll.

Так и должно быть – при неявной компоновке динамические библиотеки подключаются сразу же при загрузке файла, задолго до выполнения функции main!

Loaded 'C:\WINNT\System32 dll.dll', no matching symbolic information found.Loaded 'F:\ARTICLE\PRG\DLL.files\myfirstdll.

dll', no matching symbolic information found.Loaded 'C:\WINNT\system32\kernel32.dll', no matching symbolic information found.

Явную загрузку динамических библиотек осуществляет функция HINSTANCE LoadLibrary(LPCTSTR lpLibFileName) или ее расширенный аналог HINSTANCE LoadLibraryEx(LPCTSTR lpLibFileName, HANDLE hFile, DWORD dwFlags).
 

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

Передав его функции FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) – мы получим указатель на функцию lpProcName, экспортируемую данной DLL. При возникновении ошибки обе функции возвращают NULL.

После завершения работы с динамической библиотекой ее следует освободить вызовом функции BOOL FreeLibrary

Источник: https://xydan.livejournal.com/4508.html

Как за 10 минут написать DLL библиотеку для MQL5 и обмениваться данными?

Как создать dll файл

Так уж сложилось, что сейчас мало кто из разработчиков помнит, как написать простую 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 файлов напрямую в каталог '…\MQL5\Libraries' клиентского терминала. Это сэкономит много времени в последующей работе:

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

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

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

#pragma once #include “targetver.h” #define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers #include #define _DLLAPI extern “C” __declspec(dllexport)

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

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

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

_DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2)   { return(0);   }

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

#import “MQL5DLLSamples.dll” int  fnCalculateSpeed(int &res1,double &res2); #import    speed=fnCalculateSpeed(res_int,res_double);

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

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

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

_DLLAPI int fnCalculateSpeed(int &res1,double &res2)   { return(0);   }

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

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

  1. Прием и передача простых переменныхС простыми переменными все просто – их можно передавать по значению или по ссылке через &._DLLAPI int __stdcall fnCalculateSpeed(int &res1,double &res2)   {    int    res_int=0;    double res_double=0.0;    int    start=GetTickCount();    for(int i=0;i

Источник: https://www.mql5.com/ru/articles/18

Создаем и подключаем DLL

Как создать 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). Будет создан проект, содержащий следующее:

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) { return 1; }

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

Для экспорта и импорта из DLL необходимо использовать моди фикаторы __export и __import соответсвенно. Но в C++ Builder можно использовать новое ключевое слово __delspec() с параметрами dllexport и dllimport соответсвенно.

Сами понимаете, что для того чтобы эспортировать функции из библиотеки еужен один заголовочный файл с описаниями _delspec(dllexport) для экспортируемых функций, для импорта функций в приложение вам необходимо будет поставить анологичный заголовочный файл но с _delspec(dllimport) описаниями, что достаточно неудобно. Эта проблема решается легко: добавте в заголовочный файл библиотеки следующее:

#if defined(BUILD_DLL) # define DLL_EXP __declspec(dllexport) #else # if defined(BUILD_APP) # define DLL_EXP __declspec(dllimport) # else # define DLL_EXP # endif #endif

в исходном файле DLL напишите #define BUILD_DLL, а вместо __declspec(dllexport) пишите DLL_EXP. При написании программы добавте строчку #define BUILD_APP, и просто подключите заголовочный файл DLL.

Пример DLL: файл P.cpp

//————————————————————————— #define BUILD_DLL #include #include “p.h” #pragma hdrstop //————————————————————————— // Important note about DLL memory management when your DLL uses the // static version of the RunTime Library: // // If your DLL exports any functions that pass String objects (or structs/ // classes containing nested Strings) as parameter or function results, // you will need to add the library MEMMGR.LIB to both the DLL project and // any other projects that use the DLL. You will also need to use MEMMGR.LIB // if any other projects which use the DLL will be perfomring new or delete // operations on any non-TObject-derived classes which are exported from the // DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling // EXE's to use the BORLNDMM.DLL as their memory manager. In these cases, // the file BORLNDMM.DLL should be deployed along with your DLL. // To avoid using BORLNDMM.DLL, pass string information using “char *” or // ShortString parameters. // // If your DLL uses the dynamic version of the RTL, you do not need to // explicitly add MEMMGR.LIB as this will be done implicitly for you //————————————————————————- int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) { return 1; } //————————————————————————- void Message(char *s) { i=10; Application->MessageBox(s,”From DLL”,IDOK); }

Файл P.h

#if defined(BUILD_DLL) # define DLL_EXP __declspec(dllexport) #else # if defined(BUILD_APP) # define DLL_EXP __declspec(dllimport) # else # define DLL_EXP # endif #endif DLL_EXP void Message(char *s); DLL_EXP int i;

Скомпилируйте проект.

Если вы нажмете Run то после завершенния построения будет выдано сообщение что данная программа не можнт быть исполнена (естественно). Теперь напишем вызывающую программу. Втомже каталоге создайде новый проект (File / New Application) в форму поместите одну кнопку и создай обработчик события OnClick. Ваш исполняемый файл должен представлять собой слдующее:

//————————————————————————— #include #define BUILD_APP #pragma hdrstop #include “p.h” #include “Unit1.h” #include //————————————————————————— #pragma package(smart_init) #pragma resource “*.dfm” TForm1 *Form1; //————————————————————————- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //————————————————————————- void __fastcall TForm1::Button1Click(TObject *Sender) { char c[10]; Message(“roma”); for( ; i>0;i–) { sprintf(c,”Example %d”,i ); Application->MessageBox(“Example of using DLL variable”,(char*)c,IDOK); } } //————————————————————————-

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

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

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

Давайте рассмотрим на примере, как производится динамическая загрузка. Создайте новый прокт DLL и внесите в него следующее:
extern “C” void __export Message(char *s) { Application->MessageBox(s,”From DLL”,IDOK); }

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

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

//————————————————————————— #include #pragma hdrstop #include “Unit11.h” #include //————————————————————————— #pragma package(smart_init) #pragma resource “*.dfm” TForm1 *Form1; //————————————————————————— __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //————————————————————————— void __fastcall TForm1::Button1Click(TObject *Sender) { void (__stdcall *Message)(char *s); HINSTANCE dllp = LoadLibrary(“p.dll”); if (dllp) { Message= (void(__stdcall *) (char*)) GetProcAddress(dllp, “_Message”); if (Message) Message(“Hi From Dinamic DLL”); } FreeLibrary(dllp); } //—————————————————————————

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

  • 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);– выгрузка библиотеки из памяти.

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

Источник: http://www.codenet.ru/progr/bcb/dll.php

Поделиться:
Нет комментариев

    Добавить комментарий

    Ваш e-mail не будет опубликован. Все поля обязательны для заполнения.