Delphi передать строку из dll

Обновлено: 05.07.2024

Использование DLL (динамически подключаемая библиотека) широко распространено в программировании Windows. DLL на самом деле часть кода исполняемого файла с расширением DLL. Любая программа может вызывать DLL.

Преимущество DLL заключается в следующем:

  • Повторное использование кода.
  • Совместное использование кода приложениями.
  • Разделение кода.
  • Улучшение потребления ресурсов в Windows.

Создание DLL

В меню File выберите пункт New -> Other . В диалоговом окне на вкладке New выберите DLL Wizard. Автоматически будет создан модуль – пустой шаблон будущей DLL.

Синтаксис DLL

Для того, чтобы построить DLL, выберите Project -> Build Имя_проекта.

Видимость функций и процедур

Функции и процедуры могут быть локальными и экспортируемыми из DLL.

Локальные

Локальные функции и процедуры могут быть использованы внутри DLL. Они видны только внутри библиотеки и ни одна программа не может их вызывать извне.

Экспортируемые

Экспортируемые функции и процедуры могут быть использованы за пределами DLL. Другие программы могут вызывать такие функции и процедуры.

Исходный код выше использует экспортируемую функцию. Имя функции следует за зарезервированным словом Exports.

Загрузка DLL

В Delphi есть два вида загрузки DLL:

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

Статическая загрузка

При запуске приложения загружается автоматически. Она остается в памяти на протяжении выполнения программы. Очень просто использовать. Просто добавьте слово external после объявления функции или процедуры.

Если DLL не будет найден, программа будет продолжать работать.

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

DLL загружается в память по мере необходимости. Ее реализация более сложная, потому что Вы сами должны загружать и выгружать ее из памяти. Память используется более экономно, поэтому приложение работает быстрее. Программист сам должен следить, чтобы все работало правильно. Для этого нужно:

  • Объявить тип описываемой функции или процедуры.
  • Загрузить библиотеку в память.
  • Получить адрес функции или процедуры в памяти.
  • Вызвать функцию или процедуру.
  • Выгрузить библиотеку из памяти.

Объявление типа, описывающего функцию

Загрузка библиотеки

Получаем указатель на функцию

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

Выгрузка библиотеки из памяти

Динамический вызов DLL требует больше работы, но легче управлять ресурсами в памяти. Если Вы должны использовать DLL в пределах программы, тогда предпочтительнее статическая загрузка. Не забывайте использовать блок try…except и try…finally, чтобы избежать ошибок во время выполнения программы.

Экспорт строк

Созданная DLL с использованием Delphi, может быть использована и в программах, написанных на других языках программирования. По этой причине мы не можем использовать любой тип данных. Типы, которые существуют в Delphi могут отсутствовать в других языках. Желательно использовать собственных типы данных из Linux или Windows. Наша DLL может быть использована другим программистом, который использует другой язык программирования.

Можно использовать строки и динамические массивы в DLL, написанной в Delphi, но для этого нужно подключить модуль ShareMem в раздел uses в DLL и программе, которая будет ее использовать. Кроме того, это объявление должно быть первым в разделе uses каждого файла проекта.

Типов string, как мы знаем, С, С++ и других языках не существует, поэтому желательно использовать вместо них PChar.

При создании проекта DLL, автоматически генерируемый средой комментарий предупреждает, что передавать между приложением и библиотекой параметры типа String так просто нельзя, и предлагает объявлять ShareMem первым в списке юнитов проекта либо использовать типы PChar или ShortString. Постараюсь подробнее описать, из-за чего возникает такая необходимость, и какие тут есть проблемы, а также методы их решения, однако все тонкости типа String рассматриваться не будут.

Суть проблемы:
1. Приложение и библиотека содержат свой собственный экземпляр менеджера памяти, который представляет собой структуры данных, хранящие информацию о выделенных и свободных блоках памяти, и набор функций для выделения/освобождения памяти, которые с этими данными работают.
2. Менеджер памяти не может освободить или перераспределить блок, который он не выделял, так как в его записях нет информации об этом блоке.
3. При работе с типом String неявно происходит интенсивная работа с менеджером памяти, компилятор автоматически вставляет код для выделения, перераспределения и освобождения памяти под строку, а сама переменная типа String является лишь указателем на память, где строка хранится. Для работы с памятью используются функции, предоставляемые менеджером. В результате при передаче String-ов между приложением и библиотекой зачастую получается, что одна из сторон имеет дело с памятью, информации о которой нет в записях менеджера. Первое же обращение к менеджеру памяти в этом случае приведет к исключению.

Методы решения:
1. Передавать ShortString или String[x] вместо String. ShortString - это обыкновенный статический массив (максимальная длина - 255 символов). Динамическая память не используется, неявных обращений к менеджеру памяти нет. Недостаток – ограничение длины строки 255 символами.

2. Передавать PChar, но если планируется передавать String под видом PChar, есть некоторые оговорки. Нужно помнить, что String – это финализируемый тип, память под строку автоматически освобождается при потери последней ссылки на строку, а когда мы переходим к PChar компилятор перестает следить за ссылками. Вот так, например, в экспортной функции DLL делать нельзя:

s:=DateTimeToStr(Now); // Тут память под строку выделяется
Когда придет время использовать указатель, возвращенный MyFunc, если повезет, то он все еще будет указывать на корректный буфер строки, однако не факт, в этой области памяти могут уже быть совсем другие данные или память может быть возвращена системе (в этом случае получим AV).
Однако нижеприведенный код, будет работать (если конечно приложение не захочет использовать полученный указатель после выгрузки библиотеки с помощью FreeLibrary):

3. Передавать WideString. Очень простой вариант, работать с WideString так же просто, как и со String, за памятью следить не нужно, она автоматически выделяется и освобождается. Однако при работе с этим типом дельфийский менеджер памяти не используется, используются системные функции. Единственный недостаток в том, что при присвоении String-у WideString-а и наоборот происходит автоматически перекодировка ANSI<->UNICODE. При передаче String-ов между библиотекой и приложением в виде WideString будет тратиться процессорное время на перекодировку в UNICODE и обратно.

Народ, как правильно передавать строки из длл в основную программу?

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

Вот что сам наваял после безумно долгих мучений:

Здесь правильно используется память?

Возьмем например API функцию GetWindowText, одним из параметров в ней является переменная типа PChar (т.е. ссылка на Char'ы). Тогда каким образом функция вызванная из dll и получив этот указатель, сможет выделить место для этой переменной в области памяти основной программы так, чтобы после выгрузки dll переменная и ее значение еще были живы и вообще, как dll может управлять чужой областью памяти в этом случае? Как вообще работает эта API и подобные ей, в них даже не @PChar передается как у меня, а вообще просто PChar (тела подобных функций не нашел)?

Если можно, пожалуйста, приведите простейший код передачи строк из dll в основную прогу и обратно.

Последний раз редактировалось sprofxx; 02.04.2010 в 03:49 . вообще то вы должны выделить память для нее, и вашим указателем на то куда запишет функцию нужные данные и есть тот указатель что вы передаете функцию, то есть вы выделяете память, и вы в этой памяти получаете результат

так же внутрь функции передается размер вашей области памяти(точнее то число символов, которая наша область может в себя принять)

Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.

Здесь как я понимаю грубейшая ошибка. Мы создаем константу в пределах памяти dll и возвращаем на нее указатель, который присваивается указателю в основной программе, т.е. они начинают указывать на одну и ту же область памяти, которая находится в пределах dll! Указатель, возвращаемый функцией, указывает на область памяти, которая считается свободной — после того как значение строки ('Текст из dll') вышла за пределы области видимости, память, которую занимала эта строка, освободилась. Таким образом, выгрузив dll, освобождается память на которую указывает указатель в основной программе и значение теряется, что приводит к исключениям. Это можно проверить выгрузив dll до вывода значения переменной в лейбл. Или я не понимаю чего то?

Вопрос по GetWindowText остается открытым, интересно как именно функция заполняет переданный ей буфер.

Наде из dll передать в апликуху длинныи стринг как Var аргумент через функцию.
Пока тестирую всё собранное под Delphi XE, и там тож неполучается.

вот функция в dll-е:

Вот тут под кнопккои вызиваю из тест апликухи:

procedure TForm61.Button1Click(Sender: TObject);
Type
T_Test = Function(var q :PAnsiChar):Boolean; StdCall;
Var _Test :T_Test ;
q :PAnsiChar;
clientHandle :Thandle;
begin
clientHandle := LoadLibrary('p:\Melisa\xdiclient.dll');
@_Test := GetProcAddress(clientHandle, '_Test');
_Test(q);
showmessage( strpas(q));
FreeLibrary(clientHandle)

И чёто некатить :(
Как понемаю, то sharemem мне изпользовать нельзя, а то borlndmm.dll отличаются
в Delphi7 и XE и стринги передовать надо как PAnsiChar или PChar, но недогоняю
почему вот ето неработает .
Help плиз кто рубит фишку в етом деле :)

В dll-e я выделяю память:

GetMem(q, length(s) + 1);

а освобождаь мне уже какбы в апликухе где dll вызивался, правильно ?

FreeMem(q, length(strpas(q)));
или
FreeMemory(q);

Стринг передаётся, но чтото както неправильно освобождаю похоже . :(

Ок, попробую :)
Из dll передаю строку в вызиваюшую апликухy, в dll - выделяю
память под ето дело:

Function _Test(var q :PAnsiChar): Boolean; Export; StdCall;
var i :integer;
s :string;
Begin
s := '';

GetMem(q, length(s) + 1);

Вопрос - как мне и где правильно освободить ету память после того
как из dll-а получил етот стринг иразобрался с ним ?
В вызиваюшеи апликацие пробую и FreeMem и FreeMemory, но
ето дело валит апликацию :(

Так кодик то такои маленькии :)
Ок, следуюшии раз попитаюсь сформатировать :)

Юзай просто WideString. Оно и кросс-языково, и кросс-модульно, и памятью само управляет, и конвертируется в/из String само, и мозги не парит. Красота, короче.

А как я в главном приложении могу выделить память если я незнаю
какую длину стринга dll мне вернёт ?
Или после получения моего стринга вызивать другую функцию
которая в самом dll-е освободит ету память ?

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