Delphi как подключить библиотеку dll к проекту

Обновлено: 04.07.2024

Я создаю пакет Delphi, используя OpenGL и расширение GLEW. Я скачал GLEW с официального сайта. Это был болезненный способ сделать его пригодным для использования с Embarcadero, но в конце концов мне это удалось. На данный момент я могу скомпилировать и использовать GLEW в любом проекте приложения C++ Builder или Delphi.

Однако я испытываю трудности при использовании GLEW в моем пакете Delphi. Поскольку GLEW — это DLL, я объявляю необходимые мне внешние функции, как в следующем примере:

Это хорошо работает, когда я компилирую проект приложения, поэтому я уверен, что объявление правильное. Я могу скомпилировать и связать свой пакет без проблем. Однако, когда я пытаюсь использовать его в демонстрационном проекте, я получаю следующую ошибку ссылки:

Значит, компоновщик не находит мои функции GLEW. Проблема в том, что я не знаю, как связать внешнюю DLL с пакетом Delphi. Когда я пытаюсь добавить DLL в раздел «Требуется», мой пакет больше не может быть скомпилирован, и я получаю следующую ошибку:

Я также пытался добавить glew32.lib в свой пакет и пытался связать GLEW с моим целевым проектом, но безуспешно.

Итак, как правильно связать внешнюю стандартную DLL, которая не является .dcp, в пакетах Embarcadero Delphi? Я помню, что я могу без проблем использовать эту DLL в любом проекте приложения Delphi, и у меня также есть соответствующий .lib, который уже преобразован для использования с компилятором Embarcadero.

ПРИМЕЧАНИЕ. Как было предложено в комментарии Дэвида Хеффернана, я попытался создать небольшой пакет, включающий внешнюю функцию для user32.dll. Это сработало хорошо. Я также попытался включить одну из моих внешних функций GLEW. Теперь дело в другом: пакет линкуется и собирается хорошо, и целевой проект тоже. Но когда exe запускается, я получаю сообщение об ошибке, в котором говорится, что на моем компьютере отсутствует glew32.dll. Итак, моя проблема в основном заключается в плохой установке библиотеки GLEW. Тем не менее, все мои другие проекты, использующие GLEW, работают хорошо, в том числе те, которые я создаю с помощью других компиляторов, например. Код::Блоки.

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

Человек, работающий за компьютером

Зарко Гайич имеет опыт работы с SQL и практические знания систем баз данных, таких как MS SQL Server, Firebird, Interbase и Oracle. Он также владеет XML, DHTML и JavaScript.

Библиотека динамической компоновки (DLL) — это набор подпрограмм (небольших программ), которые могут вызываться приложениями и другими библиотеками DLL. Как и модули, они содержат код или ресурсы, которые могут совместно использоваться несколькими приложениями.

Концепция библиотек DLL лежит в основе архитектуры Windows, и по большей части Windows представляет собой просто набор библиотек DLL.

С Delphi вы можете писать и использовать свои собственные библиотеки DLL и даже вызывать функции независимо от того, были ли они разработаны в других системах или разработчиками, например Visual Basic или C/C++.

Создание библиотеки динамических ссылок

В следующих нескольких строках показано, как создать простую DLL с помощью Delphi.

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

Если вы посмотрите на файл проекта любого приложения Delphi, вы увидите, что он начинается с зарезервированного слова program. Напротив, библиотеки DLL всегда начинаются с library, а затем с предложением uses для любых модулей. В этом примере следует процедура DllMessage, которая ничего не делает, кроме простого сообщения.

В конце исходного кода находится оператор exports, в котором перечислены подпрограммы, которые фактически экспортируются из библиотеки DLL таким образом, чтобы их можно было вызывать из другого приложения. Это означает, что у вас может быть, скажем, пять процедур в DLL, и только две из них (перечисленные в разделе exports) могут быть вызваны из внешней программы (оставшиеся три являются «подпроцедурами»). ").

Чтобы использовать эту DLL, мы должны скомпилировать ее, нажав Ctrl+F9. Это должно создать библиотеку DLL с именем SimpleMessageDLL.DLL в папке ваших проектов.

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

Чтобы импортировать процедуру, содержащуюся в DLL, можно использовать ключевое слово external в объявлении процедуры. Например, для показанной выше процедуры DllMessage объявление в вызывающем приложении будет выглядеть следующим образом:

Фактический вызов процедуры — это не что иное, как:

Весь код формы Delphi (имя: Form1) с кнопкой TButton (с именем Button1), которая вызывает функцию DLLMessage, выглядит примерно так:

Delphi позволяет создавать не только приложения, но и файлы DLL. Файл DLL (сокращение от динамической библиотеки) — это особый вид исполняемого файла, который не может быть запущен сам по себе, но экспортирует процедуры и функции (называемые «точками входа») для вызова других исполняемых файлов.

Создание DLL-файла с помощью Delphi [ ]

Создание DLL-файла в Delphi состоит из двух шагов:

Создание проекта библиотеки [ ]

В Delphi есть особый вид проекта для DLL-файла. Чтобы создать его, используйте пункт меню «Файл» -> «Создать» -> «Другое», чтобы открыть диалоговое окно, в котором вы можете выбрать тип проекта, который хотите создать. Есть один, который называется «Мастер DLL»; это то, что нам нужно здесь.

File New Other Dialog.PNG

Выберите его и нажмите OK, и Delphi автоматически создаст для вас новый проект файла DLL. Исходный код выглядит так:

Есть также комментарий об использовании строк и ShareMem, но пока мы можем его игнорировать.

На первый взгляд это выглядит как исходный код проекта для обычной консольной программы. Есть только одно отличие: оно начинается с ключевого слова библиотека, а не программа.

Еще одно отличие заключается в том, как он указан в обозревателе проекта. Там, где приложение имеет расширение «.exe», библиотека имеет расширение «.dll». Поэтому новый проект называется Project1.dll, а не обычный Project1.exe.

Сохраните новый проект, назвав его, например, "MyFirstLibrary".

Экспорт функций из библиотеки [ ]

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

Да, это не очень интересно, не так ли? Но эту функцию можно вызвать из любой программы Windows, которая захочет это сделать.

Примечание о соглашениях о вызовах [ ]

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

Давайте добавим к нашей функции соглашение о вызовах:

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

Сборка DLL-файла [ ​​]

Создание DLL-файла из этой библиотеки с исходным кодом так же просто, как создание исполняемого файла для обычного приложения Delphi: просто создайте его. В итоге вы получите файл с именем MyFirstLibrary.dll.

Просмотр внутри DLL-файла [ ​​]

Теперь файл DLL является двоичным файлом, и если вы, например. загрузите его в редактор, вы увидите много тарабарщины. Но есть инструменты, которые могут извлечь дополнительную информацию об этом, например Dependency Walker или Мастер PE-информации в GExperts. Если вы откроете файл DLL с помощью этого мастера, он сообщит вам следующее:

Информация PE для MyFirstLibrary.PNG

(И многое другое, что нас сейчас не волнует.)

Итак, это сработало, теперь у вас есть файл DLL, который экспортирует функцию AddIntegers.

Использование файла DLL [ ]

Итак, как теперь назвать этот DLL-файл? Просто: вы просто указываете компилятору, какой файл DLL загружать и какую функцию вызывать с какими параметрами.

Создание тестового проекта [ ]

Закройте проект библиотеки (Файл -> Закрыть все) и создайте новое консольное приложение (Файл -> Создать -> Другое + выберите "Консольное приложение").

Вы получите пустой проект консольного приложения, подобный этому:

Сохраните его как "MyLibraryTest"

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

Добавьте следующее объявление функции:

И вызовите эту функцию из основной программы:

Теперь ваш проект должен выглядеть так:

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

Вывод MyLibrary test.jpg

Разве это не интересно? Вы только что написали свой первый DLL-файл и вызвали его из тестовой программы! ;-)

Примечания об управлении памятью [ ]

Вы помните тот длинный комментарий, который Delphi автоматически добавила в проект библиотеки? Там говорилось что-то об управлении памятью, строках, ShareMem и прочем.

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

Например, Delphi автоматически выделяет и освобождает память для хранения ваших строк, знает, когда они больше не нужны и т. д. То же самое относится, например, к Visual Basic, но оба делают это по-разному. Таким образом, если бы вы передавали строку, выделенную Visual Basic, в файл DLL, написанный на Delphi, у вас были бы большие проблемы, потому что теперь оба попытались бы управлять строкой и залезли бы друг другу в волосы.

Передача строк [ ]

Поэтому, чтобы разрешить передачу строк между приложением и библиотекой DLL, необходимо проявлять особую осторожность. На самом деле, самый простой способ — просто не допускать этого вообще! Теперь я слышу, как вы думаете: "О чем этот парень говорит? Конечно, я хочу передать строки из моего приложения в файл DLL!"

Да, вы хотели бы, не так ли? Но обычно вы не хотите передавать строки, а хотите передать ряд символов, хранящихся в строке. Для этого вы используете то, что пришло из древних времен, когда люди использовали примитивные языки программирования, такие как C, которые не знали таких сложных структур данных, как строки (да, я знаю, вы, фанатики C. C может делать все, имея правильные библиотеки , Это должно быть шуткой.). В то время программисты застряли с чем-то, что называется строками с нулевым завершением или, скорее, «указателем на массив символов, который заканчивается специальным символом NUL». В Delphi это называется PChar.

Экспорт строковой функции [ ]

Теперь давайте снова загрузим проект нашей библиотеки и добавим в него новую функцию:

Заметили что-нибудь? Нет? Присмотрись? Еще ничего? Хорошо, я скажу вам: вы знаете, что делает функция длины, не так ли? Он возвращает количество символов, хранящихся в строке. Но что мы передаем ему? Каков объявленный тип параметра _s? Это PChar, а не String!

Добро пожаловать в Delphi: он автоматически преобразует PChar в строку при присвоении его строке или передаче функции, ожидающей строковый параметр.

Надеюсь, вы также заметили, что мы снова объявили нашу новую функцию как StdCall. Вы никогда не должны забывать явно добавлять соглашение о вызовах. Если вы столкнулись с нарушением прав доступа при вызове функции DLL, первое, что нужно проверить: используют ли ваша программа и файл DLL одни и те же соглашения о вызовах? Если нет, вы нашли проблему.

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

Сохраните изменения и скомпилируйте проект.

Вызов строковой функции [ ]

Теперь мы хотим вызвать эту новую функцию. Поэтому закройте проект библиотеки и снова откройте тестовый проект.

Добавьте следующее объявление функции:

и следующий код в основную программу:

Теперь ваша программа должна выглядеть так:

Опять заметили что-то? Нет? Присмотрись! Какой тип параметра мы передаем в CountChars? Да, это строковая константа! Снова приветствуем удобства Delphi: Delphi автоматически преобразует строковые константы в PChar, если вы передаете их функции, которая ожидает PChar.

Скомпилируйте и запустите вашу программу. Теперь вы должны получить еще одну строку вывода, говорящую «5», что, кстати, является количеством символов в строке «привет».

Было ли это болезненно? Технически вы не передавали строку, но для всех практических целей вы это сделали.

Теперь попробуем передать настоящую строку, а не строковую константу. Объявите допустимый s и назначьте ему свое имя:

Скомпилируйте и запустите его. Ой? ВТФ? Он не компилируется. Добро пожаловать в неудобства использования PChars! Delphi не конвертирует строки в PChar автоматически (не спрашивайте меня, почему). Вы должны явно указать ему сделать это путем приведения типов. Поэтому измените свой код на:

Скомпилируйте и запустите его. Теперь вы должны получить такой результат:

Другие подводные камни [ ]

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

Выравнивание массива и записи — еще одна неприятность при передаче параметров через границы DLL-файла. Из соображений производительности современные компиляторы упорядочивают все типы данных по адресам слово-двойное слово или даже четверное слово. Но если вы не знаете, что на самом деле используют компиляторы с обеих сторон, вы можете получить весьма своеобразные эффекты.Delphi позволяет объявлять массивы и записи упакованными, что означает, что выравнивание не выполняется, но другие языки программирования или компиляторы могут не иметь такой концепции. Delphi также позволяет принудительно выполнить выравнивание с помощью директивы компилятора $A или $Align, опять же, другие языки программирования или компиляторы могут не предоставить вам такую ​​возможность. Иногда приходится методом проб и ошибок выяснять, как структура передается вашей программе. Обычно лучше всего использовать типы данных с обеих сторон, длина которых кратна 4 байтам.

Поделиться памятью [ ]

Вернемся к длинному комментарию, который Delphi автоматически помещает в новый проект DLL-файла. Если ваш DLL-файл и ваша основная программа будут программами Delphi (но вы уверены, что так будет всегда?), вы можете последовать этому совету и включить модуль ShareMem в качестве первого модуля в раздел uses обоих, программа и DLL-файл. Если вы сделаете это, вы сможете успешно передавать строки и другие типы данных, автоматически управляемые памятью, между программой и файлом DLL. Но ни в коем случае нельзя передавать объекты! Причина в том, что даже если вы можете использовать одно и то же объявление класса как в программе, так и в DLL-файле, эти объекты не совпадают и могут отличаться друг от друга по мере развития вашего кода.

Для опытных пользователей рекомендуется использовать сторонний менеджер памяти под названием FastMM. Достаточно того, что Borland фактически использует его в качестве основного диспетчера памяти в Delphi 2006 и более поздних версиях. Если вы используете предыдущую версию, вы можете скачать ее бесплатно и использовать в каждом отдельном проекте. Большим преимуществом файлов DLL является то, что при использовании FastMM вы можете использовать стандартные строковые переменные в экспортируемых процедурах и функциях, не беспокоясь о ShareMem или распространяя что-либо еще с проектом или файлами DLL.

Учитесь в прямом эфире,
не выходя из дома
Начните немедленно!

+1 (678) 921-0644

Как создать и вызвать библиотеку динамической компоновки (DLL) в Delphi

Введение в библиотеку динамической компоновки (DLL)

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

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

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

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


Пример DLL

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

Наш пример DLL будет экспортировать следующие шесть функций преобразования. Каждая из функций будет принимать один параметр типа double и выводить результат типа double:

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

Чтобы начать новый проект DLL, нажмите «Файл», «Создать» и «Другое». В диалоговом окне "Новые элементы" выберите "Мастер DLL" и нажмите "ОК".

DLL с использованием Delphi

Создается следующий проект:


Как видите, этот мастер не так уж и много сделал. Но, пожалуйста, обратите внимание на важный комментарий, который он вставил для нас.

Для Win32, если у вас есть DLL, которая экспортирует функции, которые передают длинные строки или динамические массивы в качестве параметров или результатов функции, DLL и ее вызывающие приложения или DLL должны использовать модуль ShareMem, и он должен быть указан первым unit в предложении Uses как для библиотеки DLL, так и для исходного файла проекта (.DPR) вызывающего приложения.

Это также применимо, если вы выделяете память с помощью New или GetMem и освобождаете ее в другом модуле, вызывая Dispose или FreeMem.То же самое верно, если вы передаете строки, вложенные в записи или объекты (вместо того, чтобы передавать их напрямую). ShareMem — это модуль интерфейса для менеджера памяти BORLANDMM.DLL, который позволяет совместно использовать динамически выделяемую память. BORLANDMM.DLL должен быть развернут вместе с вашим приложением или DLL, если они используют ShareMem. В этом примере нам не понадобится ShareMem.

Ниже приведен полный код для «ConvertDLL.dll», который будет создан при компиляции библиотеки:

< Важное примечание об управлении памятью DLL: ShareMem должен быть
первым модулем в предложении USES вашей библиотеки И в предложении USES вашего проекта (выберите
Project-View Source), если ваша DLL экспортирует какие-либо процедуры или < br />функции, которые передают строки в качестве параметров или результатов функции. Это
применяется ко всем строкам, которые передаются в библиотеку DLL и из нее, даже к тем, которые
вложены в записи и классы. ShareMem — это модуль интерфейса для
диспетчера разделяемой памяти BORLNDMM.DLL, который необходимо развертывать вместе с
библиотекой DLL. Чтобы избежать использования BORLNDMM.DLL, передайте строковую информацию,
используя параметры PChar или ShortString. >

использует
SysUtils,
классы;

функция CelciusToFahrenheit(value: double): double; стандартный вызов;
начало
Результат := ((значение * 9) / 5) + 32;
конец;

функция FahrenheitToCelcius(value: double): double; стандартный вызов;
начало
Результат := ((значение - 32) * 5) / 9;
конец;

функция KilogramsToPounds(value: double): double; стандартный вызов;
начало
Результат := значение * 2.20462262184878;
конец;

функция PoundsToKilograms(Value: double): double; стандартный вызов;
начало
Результат := значение * 0,45359237;
конец;

функция KilometersToMiles(Value: double): double; стандартный вызов;
начало
Результат := значение * 0,621371192237334;
конец;

функция MilesToKilometers(Value: double): double; стандартный вызов;
начало
Результат := значение * 1.609344;
конец;

экспортирует
градусы Цельсия в фаренгейты,
градусы Фаренгейта в градусы Цельсия,
килограммы в фунты,
фунты в килограммы,
километры в мили,
мили в километры;

конец.


Вызов DLL

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

Ниже приведены объявления функций в демонстрационном приложении:

функция CelciusToFahrenheit(Value: double): double; стандартный вызов;
внешний 'ConvertDLL.dll';
функция FahrenheitToCelcius(Значение: double): double; стандартный вызов;
внешний 'ConvertDLL.dll';
функция KilogramsToPounds(Value: double): double; стандартный вызов;
внешний 'ConvertDLL.dll';
функция PoundsToKilograms(Value: double): double; стандартный вызов;
внешний 'ConvertDLL.dll';
функция KilometersToMiles(Value: double): double; стандартный вызов;
внешний 'ConvertDLL.dll';
функция MilesToKilometers(Value: double): double; стандартный вызов;
внешний 'ConvertDLL.dll';

Единственная новая вещь здесь — это добавление «внешней 'ConvertDLL.dll'. " В этом примере предполагается, что DLL находится по тому же пути, что и исполняемый файл. Если он не найден, вы получите сообщение об ошибке при попытке запустить приложение. Директива external импортирует подпрограммы из DLL.

Следующее является основной частью приложения:

Значение, введенное в элементе управления TEdit, преобразуется в тип Float и передается в качестве параметра соответствующей функции. Результат преобразуется обратно в строку типа и отображается в другом элементе управления TEdit. Остальной код предназначен для общей функциональности. Как вы на самом деле кодируете и отображаете это, конечно, зависит от вас. Это быстрый пример для демонстрации концепций.

Читайте также: