Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.138.204.208] |
|
Сообщ.
#1
,
|
|
|
Здравствуйте,
вопрос у меня такой: пишу DLL, из которой будет экспортироваться функция, возвращающая текстовую информацию. Хочу посоветоваться, как бы это сделать, чтобы библиотеку было удобно импортировать на различных ЯП (вероятнее всего на VBA). Заранее благодарен. |
Сообщ.
#2
,
|
|
|
Так как это принято в WinAPI - передавать в функцию указатель на буфер строки pAnsiChar и длину этого буфера
Пример function GetSystemDirectoryA( lpBuffer:pAnsiChar; // buffer for system directory uSize:integer // size of directory buffer ):integer; stdcall; |
Сообщ.
#3
,
|
|
|
Спасибо за ответ,
но ИМХО, что не очень удобно — лучше чтоб функция возвращала уже готовую строку, я думаю в виде tRetType=array[0..254] of char с заполненными нулями последними символами. Но не помню, соответствует ли такой тип Бейсиковскому типу string фиксированной длины. |
Сообщ.
#4
,
|
|
|
Цитата AGRY-DILETANT @ но ИМХО, что не очень удобно — лучше чтоб функция возвращала уже готовую строку Во-первых, что значит "возвращала" ? Через результат функции можно вернуть только указатель на строку, т.е. тот же pChar. Во-вторых, указатель на что ты собираешься возвращать ? Из dll можно сравнительно безопасно вернуть указатель только на константную строку. А с динамическим выделением памяти возникают проблемы, т.к. dll и приложение могут использовать разные менеджеры дин.памяти. Даже в случае если и приложение и dll написаны на одном языке, то для передачи динамических строк нужно позаботиться о том, чтобы они юзали один и тот же менеджер памяти - например в дельфи это делается через uses ShareMem и использованием общей библиотеки BorlandMM.dll. И как же ты собираешься свою dll "импортировать на различных ЯП (вероятнее всего на VBA)" ?! Добавлено Тогда уж нужно юзать универсальный COM\OLE тип строки - WideString = OleString = BSTR = String VBA. Эти строки всегда выделяются\освобождаются системными ф-ми SysAllocString и т.п. |
Сообщ.
#5
,
|
|
|
Для передачи строки достаточно передать указатель на массив символов. Саму длину строки можно определить исходя из позиции нулевого терминатора строки.
Так, для Ansi-строки окончанием будет служить #0, а для Unicode UTF-16-строки - #0#0. Это универсальный способ. |
Сообщ.
#6
,
|
|
|
Цитата Romtek @ Для передачи строки достаточно передать указатель на массив символов. Саму длину строки можно определить исходя из позиции нулевого терминатора строки Это ты о чем ? Если в dll передавать указатель на буфер (как в WinAPI) для записи, то обязательно нужно передавать и его размер, чтобы знать сколько в него можно записать символов, не боясь переполнения. Если же речь идет о возврате "собственной" строки из dll, то ес-но достаточно указателя, но тогда нужно решить вопрос о безопасной межмодульной работе с дин.памятью |
Сообщ.
#7
,
|
|
|
Цитата leo @ Автор спросил об "экспорте" строки - я отвечаю. Для экспорта не обязательно указывать её размер. Желательно, но не обязательно.Это ты о чем ? Цитата leo @ А какие сценарии "небезопасной" передачи строки возможны? Одной из задач менеджера памяти библиотеки является слежение за выходом за границы данных. Правильным подходом является выделение и освобождение памяти внутренних переменных библиотеки своими процедурами. Если модуль библиотеки не следит за нарушениями границ, то такой менеджер попросту не выполняет своих функций. Из dll можно сравнительно безопасно вернуть указатель только на константную строку. |
Сообщ.
#8
,
|
|
|
То есть вариант
Экспорт type tReturnedString=Array[0..254]of char; function GetName(sourceName:shortstring):tReturnedString;stdcall; var bfString:tReturnedString; begin fillChar(bfstring,255,#0); Move(SourceName[1],bfString,byte(sourceName[0])); GetName:=bfString; end Импорт type tReturnedString=Array[0..254]of char function GetName(sourceName:shortstring):tReturnedString; external 'connector.dll' name 'GetName'; Может работать не для всех компиляторов? |
Сообщ.
#9
,
|
|
|
Цитата AGRY-DILETANT @ Может работать не для всех компиляторов? Ты о каких компиляторах говоришь ? В С\С++ можно объявить идентичный массив, но функции не могут возвращать массив напрямую - только в составе структуры. А в VB\VBA я вообще не знаю, во-первых, можно ли объявить массив таким образом (если через Type, то он также будет членом структуры, а не сам по себе), и во-вторых, совпадают ли массивы VB по структуре с простыми массивами паскаля и C, т.к. по всей видимости в VB юзается COM\OLE-совместимый SafeArray. И к тому же если как-то исхитриться передать именно массив, то VB придется еще "помучится" с его преобразованием к String. Так что нужно еще 10 раз подумать насчет того, что более "удобно\не_удобно" |
Сообщ.
#10
,
|
|
|
1. Для универсальной работы с разными компиляторами нужно в качестве параметров передавать указатели на структуры данных. В данном случае - указатель на открытый массив символов: PChar (на Delphi).
Соответственно, при работе над данными нужно следить за тем, чтобы резервирование памяти и её освобождение под строки происходило в пределах одного модуля. Есть компиляторы языков, у которых присутствует сборка мусора (как Компонентный Паскаль, C#, ...) и в таких данных присутствуют ещё дополнительные мета-данные, хранящие служебную информацию. Для языков, на которых написаны такие модули библиотек, требуется указывать что данный тип данных не требует дополнительных проверок сборщиком мусора. 2. Нужно указывать тип соглашения вызова stdcall для DLL и для описания импортирующего модуля. Иначе может произойти ошибка вызова. По умолчанию в FPC используется Register call, в Delphi - кажется, FastCall. Это зависит от компилятора. procedure SomeProc (s_in: PChar; var s_out: PChar); stdcall; |
Сообщ.
#11
,
|
|
|
Цитата Romtek @ В данном случае - указатель на открытый массив символов: PChar (на Delphi) Если юзать generic-тип PChar, то могут возникнуть проблемы с Ansi\Unicode, поэтому лучше явно указывать PAnsiChar или PWideChar Цитата AGRY-DILETANT @ чтобы библиотеку было удобно импортировать на различных ЯП (вероятнее всего на VBA). Если вероятнее всего только на VBA, то лучше юзать WideString, которая по сути и есть String VBA. Если же иметь в виду и С\С++, то для них проще и привычнее апишный подход с передачей указателя на буфер и размера. Если таких функций в dll будет не так много, то можно и оба варианта предусмотреть - с BSTR (= WideString) и с PAnsiChar |
Сообщ.
#12
,
|
|
|
Н-да, не вышло как-то с Бейсиком
Экспоритровал для Borland-Паскалей тип shortString, а для остальных - посимвольный экспорт с необходимостью сборки строки "на месте" в "функции-оболочке". Всем большое спасибо. |
Сообщ.
#13
,
|
|
|
Цитата AGRY-DILETANT @ для остальных - посимвольный экспорт с необходимостью сборки строки "на месте" в "функции-оболочке" Интересно, что это за "посимвольный экспорт с необходимостью сборки" ?! Чудишь ты, брат. Удобство - понятие относительное и во многом определяется привычками\традициями. Тебе (судя по твоему "дилетантскому" нику ) винапишный подход кажется неудобным, а тем, кто работает с апи - это вполне привычно и нормально, т.к. в Си и паскале вообще нет никаких проблем с использованием массивов Char, а в бэйсике хоть и нужны некоторые доп.действия, тем не менее тоже все хорошо известно и юзается при импорте ф-й АПИ: //--- объявление в dll --- function GetName(InName,OutName:pAnsiChar;OutLen:integer):integer; stdcall; begin StrLCopy(OutName,InName,OutLen-1); GetName:=StrLen(OutName); end; //--- использование в паскале --- //вариант 1 var buf:array[0..255] of AnsiChar; GetName('Hello, world!',buf,SizeOf(buf)); //вариант 2 var s:ShortString; byte(s[0]):=GetName('Hello, world!',@s[1],255); //--- использование в С ---- //int __stdcall GetName(char* InName, char* OutName, int OutLen); char buf[256]; GetName("Hello, world!",buf,sizeof(buf)); '--- использование в VB --- 'Public Declare Function GetName Lib "XXX.dll" (ByVal InStr As String, ByVal OutStr As String, _ OutLen As Long) As Long Dim s As String, len As Long s = String$(255,0) 'выделение памяти под строку len = GetName("Hello, world!",s,255) s = Left$(s,len) 'установка длины строки Добавлено PS: В VB реализована спец.поддержка передачи строк в\из dll - если строка передается ByVal, то она из юникодовского формата BSTR преобразуется в массив ANSI-символов, а затем при возврате из функции - обратно в BSTR (однако при этом длина строки остается неизменной, поэтому и нужна установка длины через Left$). Подробное описание структуры и использования строк в VB см. тут (на англицком), а кратенько по русски, например тут |