На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
В этом разделе можно создавать темы, которые относятся к поколению 32-битных компиляторов.
Здесь решаются вопросы портирования кода из старого доброго Турбо Паскаля в FPC, TMT, VP, GPC компиляторы, а также особенностей программирования на них для Windows/Linux и других ОС.
Указывайте тип компилятора, его версию, а также платформу (Windows/Linux/..) компиляции, другими словами, Target.
Модераторы: volvo877
  
> Как экспортировать строку? , 32-битная Windows
    Здравствуйте,
    вопрос у меня такой: пишу DLL, из которой будет экспортироваться функция, возвращающая текстовую информацию. Хочу посоветоваться, как бы это сделать, чтобы библиотеку было удобно импортировать на различных ЯП (вероятнее всего на VBA).
    Заранее благодарен.
      Так как это принято в WinAPI - передавать в функцию указатель на буфер строки pAnsiChar и длину этого буфера
      Пример
      ExpandedWrap disabled
        function GetSystemDirectoryA(
          lpBuffer:pAnsiChar;  // buffer for system directory
          uSize:integer        // size of directory buffer
          ):integer; stdcall;
        Спасибо за ответ,
        но ИМХО, что не очень удобно — лучше чтоб функция возвращала уже готовую строку, я думаю
        в виде tRetType=array[0..254] of char с заполненными нулями последними символами.
        Но не помню, соответствует ли такой тип Бейсиковскому типу string фиксированной длины.
          Цитата AGRY-DILETANT @
          но ИМХО, что не очень удобно — лучше чтоб функция возвращала уже готовую строку

          :D
          Во-первых, что значит "возвращала" ? Через результат функции можно вернуть только указатель на строку, т.е. тот же pChar. Во-вторых, указатель на что ты собираешься возвращать ? Из dll можно сравнительно безопасно вернуть указатель только на константную строку. А с динамическим выделением памяти возникают проблемы, т.к. dll и приложение могут использовать разные менеджеры дин.памяти. Даже в случае если и приложение и dll написаны на одном языке, то для передачи динамических строк нужно позаботиться о том, чтобы они юзали один и тот же менеджер памяти - например в дельфи это делается через uses ShareMem и использованием общей библиотеки BorlandMM.dll. И как же ты собираешься свою dll "импортировать на различных ЯП (вероятнее всего на VBA)" ?!

          Добавлено
          Тогда уж нужно юзать универсальный COM\OLE тип строки - WideString = OleString = BSTR = String VBA. Эти строки всегда выделяются\освобождаются системными ф-ми SysAllocString и т.п.
            Для передачи строки достаточно передать указатель на массив символов. Саму длину строки можно определить исходя из позиции нулевого терминатора строки.
            Так, для Ansi-строки окончанием будет служить #0, а для Unicode UTF-16-строки - #0#0. Это универсальный способ.
              Цитата Romtek @
              Для передачи строки достаточно передать указатель на массив символов. Саму длину строки можно определить исходя из позиции нулевого терминатора строки

              Это ты о чем ? Если в dll передавать указатель на буфер (как в WinAPI) для записи, то обязательно нужно передавать и его размер, чтобы знать сколько в него можно записать символов, не боясь переполнения. Если же речь идет о возврате "собственной" строки из dll, то ес-но достаточно указателя, но тогда нужно решить вопрос о безопасной межмодульной работе с дин.памятью
                Цитата leo @
                Это ты о чем ?
                Автор спросил об "экспорте" строки - я отвечаю. Для экспорта не обязательно указывать её размер. Желательно, но не обязательно.

                Цитата leo @
                Из dll можно сравнительно безопасно вернуть указатель только на константную строку.
                А какие сценарии "небезопасной" передачи строки возможны? Одной из задач менеджера памяти библиотеки является слежение за выходом за границы данных. Правильным подходом является выделение и освобождение памяти внутренних переменных библиотеки своими процедурами. Если модуль библиотеки не следит за нарушениями границ, то такой менеджер попросту не выполняет своих функций.
                Сообщение отредактировано: Romtek -
                  То есть вариант
                  Экспорт
                  ExpandedWrap disabled
                    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


                  Импорт
                  ExpandedWrap disabled
                    type
                    tReturnedString=Array[0..254]of char
                     
                    function GetName(sourceName:shortstring):tReturnedString; external 'connector.dll' name 'GetName';


                  Может работать не для всех компиляторов?
                    Цитата AGRY-DILETANT @
                    Может работать не для всех компиляторов?

                    Ты о каких компиляторах говоришь ? В С\С++ можно объявить идентичный массив, но функции не могут возвращать массив напрямую - только в составе структуры. А в VB\VBA я вообще не знаю, во-первых, можно ли объявить массив таким образом (если через Type, то он также будет членом структуры, а не сам по себе), и во-вторых, совпадают ли массивы VB по структуре с простыми массивами паскаля и C, т.к. по всей видимости в VB юзается COM\OLE-совместимый SafeArray. И к тому же если как-то исхитриться передать именно массив, то VB придется еще "помучится" с его преобразованием к String. Так что нужно еще 10 раз подумать насчет того, что более "удобно\не_удобно" ;)
                      1. Для универсальной работы с разными компиляторами нужно в качестве параметров передавать указатели на структуры данных. В данном случае - указатель на открытый массив символов: PChar (на Delphi).

                      Соответственно, при работе над данными нужно следить за тем, чтобы резервирование памяти и её освобождение под строки происходило в пределах одного модуля.
                      Есть компиляторы языков, у которых присутствует сборка мусора (как Компонентный Паскаль, C#, ...) и в таких данных присутствуют ещё дополнительные мета-данные, хранящие служебную информацию. Для языков, на которых написаны такие модули библиотек, требуется указывать что данный тип данных не требует дополнительных проверок сборщиком мусора.

                      2. Нужно указывать тип соглашения вызова stdcall для DLL и для описания импортирующего модуля. Иначе может произойти ошибка вызова. По умолчанию в FPC используется Register call, в Delphi - кажется, FastCall. Это зависит от компилятора.

                      ExpandedWrap disabled
                        procedure SomeProc (s_in: PChar; var s_out: PChar); stdcall;
                        Цитата Romtek @
                        В данном случае - указатель на открытый массив символов: PChar (на Delphi)

                        Если юзать generic-тип PChar, то могут возникнуть проблемы с Ansi\Unicode, поэтому лучше явно указывать PAnsiChar или PWideChar

                        Цитата AGRY-DILETANT @
                        чтобы библиотеку было удобно импортировать на различных ЯП (вероятнее всего на VBA).

                        Если вероятнее всего только на VBA, то лучше юзать WideString, которая по сути и есть String VBA. Если же иметь в виду и С\С++, то для них проще и привычнее апишный подход с передачей указателя на буфер и размера. Если таких функций в dll будет не так много, то можно и оба варианта предусмотреть - с BSTR (= WideString) и с PAnsiChar
                          Н-да, не вышло как-то с Бейсиком :(
                          Экспоритровал для Borland-Паскалей тип shortString, а для остальных - посимвольный экспорт с необходимостью сборки строки "на месте" в "функции-оболочке".

                          Всем большое спасибо.
                            Цитата AGRY-DILETANT @
                            для остальных - посимвольный экспорт с необходимостью сборки строки "на месте" в "функции-оболочке"

                            Интересно, что это за "посимвольный экспорт с необходимостью сборки" ?!

                            Чудишь ты, брат. Удобство - понятие относительное и во многом определяется привычками\традициями. Тебе (судя по твоему "дилетантскому" нику ;)) винапишный подход кажется неудобным, а тем, кто работает с апи - это вполне привычно и нормально, т.к. в Си и паскале вообще нет никаких проблем с использованием массивов Char, а в бэйсике хоть и нужны некоторые доп.действия, тем не менее тоже все хорошо известно и юзается при импорте ф-й АПИ:

                            ExpandedWrap disabled
                              //--- объявление в 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);
                            ExpandedWrap disabled
                              //--- использование в С ----
                              //int __stdcall GetName(char* InName, char* OutName, int OutLen);
                                char buf[256];
                                GetName("Hello, world!",buf,sizeof(buf));
                            ExpandedWrap disabled
                              '--- использование в 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 см. тут (на англицком), а кратенько по русски, например тут
                            Сообщение отредактировано: leo -
                            0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                            0 пользователей:


                            Рейтинг@Mail.ru
                            [ Script execution time: 0,0457 ]   [ 16 queries used ]   [ Generated: 3.05.24, 17:44 GMT ]