Как вызвать dll с "соглашением о вызовах _pascal" из Delphi? - PullRequest
0 голосов
/ 07 октября 2009

У меня есть dll RL6_dll.dll из программы маршрутизации RouteLogix, которая используется для планирования грузовиков и т. Д.

Теперь мы хотим использовать это с Delphi 2007. У нас есть заголовок c ++ для dll и рабочий пример, который использует его в C ++ - Builder.

Вот пример из этого файла:

// Use this routine to find the directory where the data-xxx subdirectories
// are expected.
// char * vszBuf    - address of a character array to receive the (null-terminated) path.
// int    nBufSize  - is the size of the array
//                   (internally we allow paths up to 256 characters long)

DllFn(void) RL6_GetLocalGeoDir(char *vszBuf, int nBufSize);

Моя попытка от Delphi:

procedure TfrmRL6Xml.Button1Click(Sender: TObject);
var
  s1: PChar;
  IntValue : Integer;
  RL6_GetLocalGeoDir: function(vszBuf: pchar; nBufSize: Integer): integer; stdcall;
begin
  handle := LoadLibrary('C:\Carp\RL6_app2\rl6dll\RL6_DLL.dll');
  if handle <> 0 then
  begin
      @DllFn := GetProcAddress(handle, 'RL6_PREINIT');
      @RL6_GetLocalGeoDir := GetProcAddress(handle, 'RL6_GETLOCALGEODIR');

      s1 := '                                                                                                                                        ';
      IntValue := length (s1);
      RL6_GetLocalGeoDir (s1, IntValue);
      showMessage(s1);
  end;
end;

Так что теперь я ожидаю, что s1 содержит строку, но вместо этого функции, кажется, обрабатывают IntValue как строку. Похоже, что параметры s1 и IntValue обмениваются. Мы, конечно, пробовали RL6_GetLocalGeoDir (IntValue, s1), но это тоже не сработало. Есть предложения как это назвать?

Ответы [ 2 ]

2 голосов
/ 07 октября 2009

В названии квеста упоминается соглашение о вызовах Паскаля, но тело вопроса никогда не возвращается к этой теме. В документации к DLL говорится, что она использует соглашение о вызовах Pascal? В наши дни это очень редкое соглашение о вызовах. (Он использовался в Windows API в 16-разрядные дни, и хотя некоторые заголовки тех времен все еще говорят PASCAL сегодня, этот макрос был переопределен для ссылки на соглашение о вызовах stdcall.)

Вы не показали определение DllFn - ни в коде C, ни в коде Delphi. В Си я представляю, что это макрос, который включает соглашение о вызове функции, поэтому найдите это определение, чтобы подтвердить, что на самом деле используется. В Delphi похоже, что вы используете его как указатель на функцию. Я рекомендую вам использовать те же имена функций в вашем приложении, что и DLL. Это облегчает жизнь всем участникам - никто больше не смотрит на код и не задумывается, какая функция на самом деле вызывается.

Если вы подтвердите, что DLL действительно использует соглашение о вызовах Pascal, то указать его в Delphi так же просто, как изменить директиву stdcall в объявлении функции на «pascal»:

RL6_GetLocalGeoDir: procedure(vszBuf: PAnsiChar; nBufSize: Integer); pascal;

Я также изменил аргумент PChar, чтобы использовать PAnsiChar, потому что теперь, когда некоторые версии Delphi являются Unicode, тип PChar может означать PWideChar, и вы не хотите этого здесь.

0 голосов
/ 07 октября 2009

Вам необходимо вызвать процедуру с предварительно выделенным буфером и с правильным объявлением, например так:

procedure TfrmRL6Xml.Button1Click(Sender: TObject);
var
  s: AnsiString;
  IntValue : Integer;
  RL6_GetLocalGeoDir: procedure(vszBuf: PAnsiChar; nBufSize: Integer); stdcall;
begin
  handle := LoadLibrary('C:\Carp\RL6_app2\rl6dll\RL6_DLL.dll');
  if handle <> 0 then
  begin
      @DllFn := GetProcAddress(handle, 'RL6_PREINIT');
      @RL6_GetLocalGeoDir := GetProcAddress(handle, 'RL6_GETLOCALGEODIR');

      IntValue := 256;
      SetLength(s, IntValue);
      RL6_GetLocalGeoDir (PAnsiChar(s), IntValue);
      s := PAnsiChar(s);
      showMessage(s);
  end;
end;

Edit:

Ваш измененный вопрос все еще содержит неверный код. Вы используете

var
  s1: PChar;

s1 := '                                                                                                                                        ';
IntValue := length (s1);

Это просто неверно, так как вы предоставляете не буфер, а указатель на строковую константу в сегменте кода. Использование этого приведет к сбоям, попробуйте, например, функцию API GetWindowsDirectory():

var
  P: PAnsiChar;
begin
  P := '                                                                                ';
  GetWindowsDirectory(P, 80);
end;

Выполнение этого приведет к нарушению доступа в ntddll.dll, записи адреса в области кода (например, $0044C6C0).

...