如何使用Delphi中的“_pascal调用约定”调用dll?

时间:2009-10-07 09:36:50

标签: c++ delphi dll

我有路由程序RouteLogix中的dll RL6_dll.dll,用于计划卡车等。

现在我们想要使用Delphi 2007中的那个。 我们有一个用于dll的c ++标头和一个在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 个答案:

答案 0 :(得分:2)

任务标题提到了Pascal调用约定,但问题主体永远不会回到该主题。 DLL的文档是否说它使用Pascal调用约定?这是现在使用的非常罕见的调用约定。 (它在16位的日子里在Windows API中使用,虽然这些时候的一些标题今天仍然说PASCAL,但是已经重新定义了该宏以引用stdcall调用约定。)

你没有在{C}代码和Delphi代码中都显示DllFn的定义。在C中,我想它是一个包含函数调用约定的宏,所以去找那个定义来确认实际使用的是什么。在Delphi中,看起来你正在将它用作函数指针。我鼓励您在应用程序中使用与DLL相同的函数名称。它让所有参与者的生活更轻松 - 任何人都不会看到代码,并想知道真正调用的函数是什么。

如果确认DLL确实使用了Pascal调用约定,那么在Delphi中指定它就像将函数声明中的“stdcall”指令更改为“pascal”一样简单:

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

我还将PChar参数更改为使用PAnsiChar,因为现在某些版本的Delphi是Unicode,PChar类型可能意味着PWideChar,并且你没有不想在这里。

答案 1 :(得分:0)

您需要使用预分配的缓冲区调用该过程,并使用正确的声明,如下所示:

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;

修改

您修改过的问题仍然包含错误代码。你用

var
  s1: PChar;

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

这是错误的,因为您没有提供缓冲区,而是在代码段中提供指向字符串常量的指针。使用此功能会导致崩溃,只需尝试使用API​​函数GetWindowsDirectory()

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

运行此操作将导致ntddll.dll中的访问冲突,在代码区域中写入地址(例如$0044C6C0)。