Что нового

DllCall обрезает ANSI-строку по первому 0x00, как обойти?

sss

Продвинутый
Сообщения
332
Репутация
96
Здравствуйте!

Делаю небольшую DLL (на C++) для нужд скрипта. Функция в DLL принимает 2 строки и длину одной из них, возвращает одну модифицированную строку. Выяснилось, что DllCall отрезает строку при первом нахождении символа NULL (0x00), что, по идее, правильно. Но засада в том, что возвращаемая строка - в бинарном виде, и таких символов в ней довольно много...

Можно ли обойти эту засаду? Может, есть какие-то обертки, контейнеры - типа vector и string, или хотя бы просто массив символов, без правил ANSI-строк? В справке нашел только str и wstr...

Сейчас вызываю так:
Код:
DllCall("моя_dll.dll", "str", "функция", "str", "строка_1", "str", "строка_2", "USHORT", StringLen("строка_2"))


P.S: в библиотеке использую для хранения std::string
P.P.S: библиотека рабочая, если сделать ввод и вывод строки через файлы - все работает.

Заранее спасибо за ответы.
 

Belfigor

Модератор
Локальный модератор
Сообщения
3 597
Репутация
938
А если длл будет возвращать данные где все 0x00 заменены на другой уникальный символ или их комбинацию и дальнейшая обработка будет происходить в скрипте?
 

firex

AutoIT Гуру
Сообщения
943
Репутация
206
Sky-WaLkeR
Пускай твоя DLL возвращает строку в бинарном представлении. Т.е. HEX.

К примеру твоя длл вернула:
Код:
'000000'

И ты получаешь строку:
Код:
BinaryToString( '0x' & '000000' )
 
Автор
sss

sss

Продвинутый
Сообщения
332
Репутация
96
Спасибо за ответы.

Belfigor
Я уже думал на эту тему - уникального символа может и не быть, плюс игнорируемых (управляющих) символов много...

firex
Ну, я не совсем точно выразился... Возвращается строка, но символы в ней могут быть с кодами 0-255. Вот пример с живыми цифрами:
Код:
; функция должна вернуть {66, 77, 182, 255, 0, 0, 48, 49} (значения в DEC)
$ret = DllCall($dll, "str", "my_func", "str", $string_1, "str", $string_2, "USHORT", StringLen($string_2))
$result = $ret[0] ; возвращаемое значение
$test = StringLen($result) ; = 4

MsgBox(0, "", $result) ; выведет 4 символа
MsgBox(0, "", Binary($result)) ; выведет HEX-представление четырех символов, в данном случае 0x424DB6FF


То есть мне нужна даже не строка, а просто массив символов, а точнее - массив их цифровых кодов

Код:
MsgBox(0,'',BinaryToString(0x30000030)) ; выведет строку "0" - т.е. обрежет все NULL и последний символ "0"
 

AZJIO

Меценат
Меценат
Сообщения
2 752
Репутация
1 149
Sky-WaLkeR
Возвращаешь бинарный формат. Так делают все функции для работы с интернетом, есть ключ "Пересылать в бинарном виде". Это и есть выход из ситуации. Ваш вариант уже доказан вами же как не работающий. Символы передавать все, но по 0x00 делается обрезка, значит в текстовом формате бинарные данные никак не передать. Всё, просто забудь про это. Так же были случаи и с рег.выр. проблемы с 0x00 символом.
 
Автор
sss

sss

Продвинутый
Сообщения
332
Репутация
96
Я это понимаю, но только как это сделать? Что подставить в return type AutoIt-а? Что возвращать функцией? В DLL формируется массив кодов символов (string.c_str()), как правильно передать его в бинарном виде?
Пока единственный способ, которым удалось совершить эту передачу - через файл. DLL создает файл, AutoIt его читает в бинарном виде, удаляет файл. Но хотелось бы исключить промежуточное звено "файл" :smile:

Интересно, можно ли подключить к этому делу STDIN\STDOUT потоки?.. Пойду пробовать...

UPD: SciTE вывод с DLL показывает, но ConsoleRead возвращает пустоту, а для StdOutRead нужен хендл процесса... Видимо, надо ловить как-то в момент выполнения DllCall... Можно, конечно, сделать библиотеку в виде EXE и передавать параметры командной строкой, но это, мягко говоря, не солидно...

UPD2: нашел один из вариантов решения передачи потока STDOUT с DLL в AutoIt-скрипт - создавать дочерний AutoIt-скрипт, вызывать либу из него и родителем читать поток. Навеяно неким "The Kandie Man" с оф. форума, буду пробовать...
 

firex

AutoIT Гуру
Сообщения
943
Репутация
206
Sky-WaLkeR
Что тебе мешает возвращать тот же бинарный вариант, но только в строке?

А так, ставь тип int.
Я в том смысле, что будет удобнее передавать указатель на структуру (скажем byte[Count]), а сама библиотека уже запишет туда бинарное представление строки. В случае успеха возвращает 1, а данные ты получаешь путем DllStructGetData( Ptr, Index )
И не надейся таким образом получить саму строку, DllStructGetData ее то же обрежет.

Я как то давно создавал подобную тему, как сказал Yashied - 0x00 указывает на "конец" строки.
 
Автор
sss

sss

Продвинутый
Сообщения
332
Репутация
96
firex
Может, то, что в бинарном формате в строке тоже будут нули (0 = 0x00)? Или я чего-то не понимаю...

Так тип byte не вернет массив, он вернет только один элемент, если не ошибаюсь... Если можно, покажи небольшой пример (код функции в DLL и код ее вызова с AutoIt).

Идею уловил, спасибо, попробую... Но пока вариант с потоками больше симпатизирует...

Я как то давно создавал подобную тему, как сказал Yashied - 0x00 указывает на "конец" строки.
В том-то вся и проблема... Я это знаю, все ANSI-строки заканчиваются на NULL, и при встрече все дальнейшее обрубается...
 

firex

AutoIT Гуру
Сообщения
943
Репутация
206
Sky-WaLkeR [?]
Так тип byte не вернет массив, он вернет только один элемент, если не ошибаюсь... Если можно, покажи небольшой пример (код функции в DLL и код ее вызова с AutoIt).
Я обновил свой пост.
 
Автор
sss

sss

Продвинутый
Сообщения
332
Репутация
96
Так, маленький отчет.
Выявились два варианта:
1) передача строки через поток STDOUT.
Плюсы:
• Элементарно реализовать на стороне DLL
• Уже проверен и работает (нули спокойно проходят, HEX-значение получается правильное)
Минусы:
• Не самая элегантная реализация - AutoIt вызывает AutoIt, который вызывает DLL...

2) передача с помощью указателя на структуру (как подсказал firex)
Плюсы:
• Красиво и удобно, все в одном скрипте
Минус только один - пока не реализовал... Крайне редко работал с структурами в AutoIt, буду пробовать...
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 714
Нужно понять, что тип "str" и "wstr" однозначно работают с нуль-терминированными строками. Далее, DllCall() не передает саму строку в функцию, а создает временный буфер, помещает туда строку и передает адрес этого буфера. Обрезание происходит как раз на стадии помещения строки в буфер. К слову, ни одна из строковых функций AutoIt не переварит нулевой символ. Было бы лучше, если бы ты подробнее описал то, что делает DLL и что именно возвращает. И что представляют собой $string_1 и $string_2?

P.S.

Если нужен массив, то используй структуру вида "byte[n]".

Код:
; Буфер, в который функция должна поместить результат
$tData = DllStructCreate('byte[65535]')

; Функция заполняет структуру $tData и возвращает длину в байтах (первые два параметра, это адрес и длина буфера соответственно)
$Ret = DllCall($Dll, 'ushort', 'my_func', 'ptr', DllStructGetPtr($tData), 'ushort', DllStructGetSize($tData), 'str', $String_1, 'str', $String_2, 'ushort', StringLen($String_2))

; Обрезаем результат для вывода в консоль
$tTemp = DllStructCreate('byte[' & $Ret[0] & ']', DllStructGetPtr($tData))

ConsoleWrite(DllStructGetData($tTemp, 1) & @CR)
 
Автор
sss

sss

Продвинутый
Сообщения
332
Репутация
96
DLL - самописная, так что есть возможность вернуть что угодно, будь то структура, массив byte, массив символов aka ANSI-строка...

Библиотека получает строки (нормальные, без NULL), преобразует первую (вторая участвует в алгоритме преобразования) и возвращает эти измененные данные (которые могут содержать любые символы, то есть грубо говоря - массив byte со значениями элемента от 0 до 255) обратно в скрипт. Далее скрипт работает с ними уже как с бинарными данными.

Обрезание происходит как раз на стадии помещения строки в буфер
Этого я и боялся... В библиотеке храню строки в контейнере...
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 714
См. выше.
 
Автор
sss

sss

Продвинутый
Сообщения
332
Репутация
96
Спасибо за пример - все работает замечательно, нули проходят. И именно то, что было нужно - HEX, который можно переделать во что угодно...

Всем спасибо за помощь, тема решена.
 
Верх