C++ 如何使用“调用dll”_帕斯卡呼叫约定;来自德尔福?

C++ 如何使用“调用dll”_帕斯卡呼叫约定;来自德尔福?,c++,delphi,dll,C++,Delphi,Dll,我有一个来自路由程序RouteLogix的dll RL6_dll.dll,用于规划卡车等 现在我们想使用Delphi2007中的。 我们有一个用于DLL的C++头,它是一个在C++ +Builder中使用的工作例子。 下面是该文件中的一个示例: // Use this routine to find the directory where the data-xxx subdirectories // are expected. // char * vszBuf - address of a

我有一个来自路由程序RouteLogix的dll RL6_dll.dll,用于规划卡车等

现在我们想使用Delphi2007中的。 我们有一个用于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;
procedure TfrmRL6Xml.Button1Click(发送方:TObject);
变量
s1:PChar;
IntValue:整数;
RL6_GetLocalGeoDir:function(vszBuf:pchar;nBufSize:Integer):Integer;stdcall;
开始
句柄:=LoadLibrary('C:\Carp\RL6_app2\rl6dll\RL6_DLL.DLL');
如果句柄为0,则
开始
@DllFn:=GetProcAddress(句柄'rl6u PREINIT');
@RL6_GetLocalGeoDir:=GetProcAddress(句柄,'RL6_GetLocalGeoDir');
s1:='';
IntValue:=长度(s1);
RL6_GetLocalGeoDir(s1,IntValue);
showMessage(s1);
结束;
结束;

现在我期望s1包含一个字符串,但是函数似乎将IntValue作为字符串处理。似乎s1和IntValue参数是交换的。我们当然尝试过RL6_GetLocalGeoDir(IntValue,s1),但也没有成功。有没有关于如何调用它的建议?

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

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;
这是错误的,因为您没有提供缓冲区,而是在代码段中提供指向字符串常量的指针。使用此选项将导致崩溃,只需尝试使用API函数
GetWindowsDirectory()


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

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

您没有在C代码和Delphi代码中显示DllFn的定义。在C语言中,我想象它是一个包含函数调用约定的宏,所以去寻找那个定义来确认真正使用的是什么。在Delphi中,它看起来像是一个函数指针。我鼓励您在应用程序中使用与DLL相同的函数名。它使每个参与的人的生活都变得更容易——不再有人看代码,不知道真正调用的是什么函数

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


我还将
PChar
参数更改为使用
PAnsiChar
,因为现在Delphi的一些版本是Unicode,
PChar
类型可能意味着
PWideChar
,您不希望在这里使用它。

Oops,我把示例简化得太多了。这个例子现在被更正了。我认为它没有被更正,你为什么要在一个
PChar
上调用
Length()
?最好使用PAnsiChar和AnsiString,因为在C中,“char”是一个1字节的字符。
Length
对于调用
PChar
是完全有效的。首先将输入转换为临时字符串,但它将返回与从
StrLen
获得的值相同的值,只是当输入指针为null时,
StrLen
可以不定义。
var
  s1: PChar;

s1 := '                                                                                                                                        ';
IntValue := length (s1);
var
  P: PAnsiChar;
begin
  P := '                                                                                ';
  GetWindowsDirectory(P, 80);
end;
RL6_GetLocalGeoDir: procedure(vszBuf: PAnsiChar; nBufSize: Integer); pascal;