AnsiString возвращает значения из DLL-библиотеки Delphi 2007 в приложении Delphi 2009 - PullRequest
4 голосов
/ 25 июня 2009

У меня есть DLL, скомпилированная с D2007, в которой есть функции, возвращающие AnsiStrings.

Мое приложение скомпилировано в D2009. Когда он вызывает функции AnsiString, он возвращает мусор.

Я создал небольшое тестовое приложение / dll для эксперимента и обнаружил, что если приложение и dll скомпилированы с одной и той же версией Delphi (2007 или 2009), проблем нет. Но когда один компилируется в 2009 году, а другой в 2007 году, я получаю мусор.

Я пытался включить последнюю версию FastMM в оба проекта, но даже в этом случае приложение 2009 года не может читать AnsiStrings из dll 2007 года.

Есть идеи, что здесь происходит не так? Есть ли способ обойти это?

Ответы [ 6 ]

11 голосов
/ 25 июня 2009

Внутренняя структура AnsiStrings изменилась в период между Delphi 2007 и Delphi 2009. (Не расстраивайтесь; такая возможность присутствует со дня 1). Строка Delphi 2009 поддерживает число, указывающее, в какой кодовой странице находятся данные.

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

Если вызывающая сторона не знает, сколько байтов должно быть в буфере, у вас есть два варианта:

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

  • Дайте DLL выделить место для себя с помощью заранее определенного метода, доступного для вызывающей стороны, чтобы позже освободить буфер. DLL может либо экспортировать функцию для освобождения выделенных ей буферов, либо вы можете указать некоторую взаимно доступную функцию API для вызывающей стороны, например GlobalFree. Ваша DLL должна использовать соответствующий API распределения, такой как GlobalAlloc. (Не используйте встроенные в Delphi функции выделения памяти, такие как GetMem или New; нет никакой гарантии, что менеджер памяти вызывающей стороны будет знать, как вызывать Free или Dispose, даже если он записан в том же самом язык, даже если он написан с той же версией Delphi.)

Кроме того, эгоистично писать DLL, которая может использоваться только одним языком. Напишите свои библиотеки DLL в том же стиле, что и Windows API, и вы не ошибетесь.

2 голосов
/ 25 июня 2009

ОК, так что не пробовал, поэтому большой жирный отказ от ответственности ударил по этому.

В программе просмотра справки просмотрите тему (Unicode в RAD Stufio) ms-help: //embarcadero.rs2009/devcommon/unicodeinide_xml.html

.

Возвращая строку Delphi 2007 в Delphi 2009, вы должны получить две проблемы.

Во-первых, кодовая страница, упомянутая Робом. Вы можете установить это, объявив другую AnsiString и вызвав StringCodePage для новой AnsiString. Затем назначьте это старому AnsiString, вызвав SetCodePage. Это должно сработать, но если этого не произойдет, все еще есть надежда.

Вторая проблема - размер элемента, который будет чем-то совершенно безумным. Это должно быть 1, поэтому сделайте это 1. Проблема здесь в том, что нет функции SetElementSize, на которую можно опереться.

Попробуйте это:

var
  ElemSizeAddr: PWord; // Need a two-byte type
  BrokenAnsiString: AnsiString; // The patient we are trying to cure
...
  ElemSizeAddr := Pointer(PAnsiChar(BrokenAnsiString) - 10);
  ElemSizeAddr^ := 1; // The size of the element

Это должно сделать это!

Теперь, если вещь StringCodePage / SetCodePage не сработала, вы можете сделать то же, что и выше, изменив строку, в которой мы получаем адрес для вычета 12 вместо 10.

Он взломал все это, поэтому я люблю его.

В конечном итоге вам потребуется портировать эти библиотеки DLL, но это сделает порт более управляемым.

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

0 голосов
/ 05 июня 2012

Как быстрое решение здесь: если ваши фактические данные, которые вы возвращаете из dll в строке, не превышают 255 символов, вы можете изменить как in-dll, так и интерфейсные описания, чтобы использовать ShortString, которая будет работать независимо от 2007 / 2009 версия. Поскольку вы используете AnsiString уже в 2007 году без идентификатора кодовой страницы, Unicode не доставит вам никаких хлопот.

если вы идете по этому пути, все, что вам нужно сделать, это изменить объявления как:

function MyStringReturningFunction : ShortString ; external 'MyLibrary.dll';

(и в dll: function MyStringReturningFunction : ShortString; соответственно)

То же самое касается параметров ввода / вывода курса:

procedure MyStringTakingAndReturningFunction(s1:ShortString; var s2:ShortString); external 'MyLibrary.dll';

Должно быть проще, чем менять много кода. Но будьте осторожны, как я уже сказал, ваши данные не должны превышать 255 символов, так как это максимальный размер, который может содержать ShortString.

0 голосов
/ 09 июля 2009

Я согласен с Робом и Реми: обычные Dlls должны возвращать PAnsiChar AnsiStrings.

Если DLL работает нормально скомпилировано с D2009, почему просто не перестает ее компилировать с D2007 и начать компиляцию с D2009 раз и навсегда?

0 голосов
/ 09 июля 2009

Ваша DLL не должна возвращать значения AnsiString для начала. Единственный способ, который в первую очередь будет работать правильно, - это если бы DLL и EXE были скомпилированы с помощью модуля ShareMem, и даже тогда, только если они скомпилированы с одной и той же версией Delphi. Диспетчер памяти D2007 несовместим с диспетчером памяти D2009 (или любым другим кросс-версионным использованием диспетчеров памяти), AFAIK.

0 голосов
/ 26 июня 2009

Скорее всего, вам просто нужно преобразовать DLL в 2009. По словам Embarcadero, преобразование в 2009 является «простым» и совсем не займет у вас времени.

...