Получить возвращаемое значение строки из C DLL в Delphi - PullRequest
4 голосов
/ 26 октября 2008

У меня есть устаревшая DLL, написанная на C, которая содержит функцию, которая возвращает строку, и мне нужно получить доступ к этой функции из Delphi. Единственная информация, которую я имею о DLL - это объявление VB для доступа к функции:

Public Объявление функции DecryptStr Lib "strlib" (Str As String) как строка

Я безуспешно пробовал следующее:

Декларация:

function DecryptStr(s: PChar): PChar; cdecl; external 'strlib.dll';

Использование:

var
  p1, p2 : pchar;
begin
  GetMem( p1, 255 );
  StrPCopy( p2, 'some string to decrypt' );
  p1 := DecryptStr( p2 );
end;

Это последовательно приводит к аварийному завершению работы DLL с нарушением прав доступа. Я в растерянности.

Есть предложения?

Ответы [ 9 ]

5 голосов
/ 26 октября 2008

Попробуйте переписать свой тестовый код следующим образом:

var
  p1, p2 : pchar;
begin
  GetMem( p1, 255 ); // initialize
  GetMem( p2, 255 );
  StrPLCopy( p2, 'some string to decrypt', 255 ); // prevent buffer overrun
  StrPLCopy( p1, DecryptStr( p2 ), 255); // make a copy since dll will free its internal buffer
end;

Если при вызове DecryptStr все еще происходит сбой, внимательно прочитайте http://support.microsoft.com/kb/187912.

4 голосов
/ 26 октября 2008

p2 не инициализирован. StrPCopy копирует строку в произвольную ячейку памяти. И, скорее всего, соглашение о вызовах - это stdcall.

2 голосов
/ 26 октября 2008

Я предполагаю здесь, но вы уверены, что это cdecl? Если объявление VB не упоминает об этом, я бы предположил, что на самом деле это функция STDCALL (STDCALL довольно распространен в Windows, так как почти все его нативные API используют ее). Вызов функции одного соглашения о вызовах, как если бы он был другим соглашением о вызовах, может действительно испортить стек, что обычно приводит к сбою.

Также убедитесь, что строка является ANSI (LPSTR / LPCSTR) или UNICODE (LPWSTR / LPCWSTR). Я не знаю VB или Delphi, поэтому я не знаю, что каждый из них использует по умолчанию.

1 голос
/ 27 октября 2008

Как говорит Джозз, p2 (куда вы копируете свою строку) никогда не инициализируется в вашем примере.

Попробуйте вместо этого.

var
  p1, p2 : pchar;
begin
  GetMem( p2, 255 ); // allocate memory for 'some string...'
  StrPCopy( p2, 'some string to decrypt' );
  p1 := DecryptStr( p2 );
end;

Кроме того, память, выделенная при вызове Getmem (p1, ...), была бы утечка, потому что p1 была перезаписана функцией, возвращаемой из DecryptStr.

Однако меня немного беспокоит, что именно DecryptStr возвращает и кому принадлежит память, на которую указывает p1. Если он возвращает указатель на память, выделенную DLL, вам нужно быть осторожным, как эта память освобождается.

0 голосов
/ 18 марта 2013

Я добавляю свое решение, потому что я немного боролся с ним и не нашел его ни в одном из ответов.

Функция C ++ выглядит так:

int  __stdcall DoSomething(char * _name);

Чтобы заставить его работать в Delphi, я объявляю следующую функцию

function DoSomething(name: PAnsiChar): integer; stdcall; external 'somedll.dll';

А потом, когда я делаю вызов, у меня есть функция, которая выглядит следующим образом:

var s: PAnsiChar;
begin
  GetMem(s, 255);
  DoSomething(s);
  // s now contains the value returned from the C DLL
end;

Я пытался использовать PChar вместо PAnsiChar, но все, что я получаю взамен, - это мусор. Кроме того, если я объявляю функцию в Delphi с параметром, установленным в var , я получаю исключение при попытке прочитать ее.

Надеюсь, это кому-нибудь поможет ..

0 голосов
/ 27 октября 2008

Предположения о том, что строки должны быть "инициализированы", кажутся правильными. Это потому что C потребует, чтобы передаваемая строка заканчивалась нулем. Убедитесь, что символ в буфере сразу после конца текста является нулевым (# 0).

Почему вы предполагаете, что длина передаваемой строки ровно 255 символов? Вам нужно выделить Length (p1) + 1 байт - для символов в p1 и символа # 0 в конце.

Кроме того, ваш пример кода выглядит запутанным в отношении использования p1 и p2. Похоже, что p1 - это буфер, передаваемый C DLL, который вы выделяете, а p2 - возвращаемая строка, которую выделяет DLL. Но тогда код будет (обратите внимание на использование p1 и p2)

var
  p1, p2 : pchar;
begin
  GetMem( p1, 255 );
  StrPCopy( p1, 'some string to decrypt' );
  p2 := DecryptStr( p1 );
end;

Более точные имена переменных помогут вам сделать это более понятным.

0 голосов
/ 26 октября 2008

Была ли dll, написанная на Borland C или C ++ Builder, случайно с намерением использовать ее с Delphi? В этом случае он мог быть скомпилирован с использованием директивы pascal.

0 голосов
/ 26 октября 2008

Лучший способ в подобных ситуациях - отладить вашу программу и проверить стек до и после выполнения обратного вызова. Как знать, это может быть даже ошибка во внешней DLL?

Таким образом, вы легко поймете, как это исправить.

0 голосов
/ 26 октября 2008

Я согласен с CesarB, попробуйте объявить его с помощью директивы stdcall:

function DecryptStr(s: PChar): PChar; stdcall; external 'strlib.dll';

если это не сработает, опубликуйте объявление VB здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...