我有路由程序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),但这也没有用。有什么建议怎么称呼它?
答案 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
)。