使用Delphi调用C语言编写的动态链接库并传递字符串参数

3
我想调用Delphi XE5中的dll。
我花了一天左右谷歌搜索“从Delphi调用C DLL”,找到了很多页面,但都没有真正帮助我。
我已经收到了如何在VB中调用dll的示例:
Declare Function IxCommand Lib "IxxDLL.dll" (ByVal command As String, ByVal mailbox As String) As Integer

...

Sub Command1_Click ()
         Dim command As String * 135
         Dim mailbox As String * 135

         command = "move:a,1000"
         IxCommand( command, mailbox)
End Sub

在 VC 6.0 中调用 DLL:

#include "stdafx.h"

#include "windows.h"
#include "stdio.h"
#include "string.h"

typedef UINT (CALLBACK* LPFNIXDLLFUNC)(char *ixstr, char *mbstr);

int main(int argc, char* argv[])
{
    HINSTANCE hDLL;              // Handle to DLL
    LPFNIXDLLFUNC lpfnIxDllFunc; // Function pointer

    hDLL = LoadLibrary( "IxxDLL.dll" );

    if (hDLL == NULL) // Fails to load Indexer LPT
    {
        printf("Can't open IxxDLL.dll\n");
        exit(1);
    }
    else // Success opening DLL - get DLL function pointer
    {
        lpfnIxDllFunc = (LPFNIXDLLFUNC)GetProcAddress(hDLL, "IxCommand");
    }

    printf( "Type Indexer LPT command and press <Enter> to send\n" );
    printf( "Type \"exit\" and press <Enter> to quit\n\n" );

    while( 1 )
    {
        char ix_str[135];      // String to be sent to Indexer LPT
        char mailbox_str[135]; // Results from call into Indexer LPT

        gets( ix_str ); // Get the string from the console
        if( _stricmp( ix_str, "exit" ) == 0 ) // Leave this program if "exit"
            break;
        lpfnIxDllFunc( ix_str, mailbox_str ); // Otherwise call into Indexer LPT
        printf( "%s\n\n", mailbox_str );      // Print the results
    }

    FreeLibrary( hDLL );
    return 0;
}

我发现一个问题就是需要在调用DLL之前定义内存分配的大小,如上所示。

DLL在第一个参数中接收命令,并在第二个参数中返回结果文本。

以下是我生成的Delphi代码,试图调用DLL。我知道DLL已加载,因为它有一个显示屏幕。调用DLL时不会生成错误。您将看到我使用数组分配空间,然后将它们的位置分配给Pchar变量。我没有原始dll的头文件或源代码。您将看到我使用stdcall声明了外部函数,但我也尝试过cdecl而没有改变。

问题:arg2中返回的信息不是预期的文本字符串,而是一串被翻译成非英语字符(看起来像是中文)的字符串。

我猜测我没有发送正确的变量类型给dll。

问题:有谁能帮助我制定外部函数的声明并正确使用它,以便我按预期获取文本字符串?

请参见下文:

function IxCommand (command : PChar; mailbox : PChar) : Integer; stdcall; external 'IxxDLL.dll';

...

procedure TfrmXYZ.btn1Click(Sender: TObject);

var
  LocalResult : Integer;
  arg1,
  arg2        : PChar;


  ArrayStrCmd : array[0..134] of char;
  ArrayStrMbx : array[0..134] of char;

begin

  ArrayStrCmd := 'Accel?:a' + #0;
  ArrayStrMbx := '          ' + #0;
  arg1 := @ArrayStrCmd;
  arg2 := @ArrayStrMbx;


  LocalResult := IxCommand(arg1, arg2);

end;

2个回答

4
问题出在字符编码上。在 Delphi 2009+ 中,PCharPWideChar 的别名,但 DLL 使用的是 Ansi 字符串,所以您需要使用 PAnsiChar 而非 PChar
尝试这样做:
function IxCommand (command : PAnsiChar; mailbox : PAnsiChar) : Integer; stdcall; external 'IxxDLL.dll';

...

procedure TfrmXYZ.btn1Click(Sender: TObject);
var
  LocalResult : Integer;
  ArrayStrCmd : array[0..134] of AnsiChar;
  ArrayStrMbx : array[0..134] of AnsiChar;
begin
  ArrayStrCmd := 'Accel?:a' + #0;
  LocalResult := IxCommand(ArrayStrCmd, ArrayStrMbx);
end;

或者:

function IxCommand (command : PAnsiChar; mailbox : PAnsiChar) : Integer; stdcall; external 'IxxDLL.dll';

...

procedure TfrmXYZ.btn1Click(Sender: TObject);
var
  LocalResult : Integer;
  ArrayStrCmd,
  ArrayStrMbx : AnsiString;
begin
  SetLength(ArrayStrCmd, 135);
  StrPCopy(ArrayStrCmd, 'Accel?:a');
  SetLength(ArrayStrMbx, 135);
  LocalResult := IxCommand(PAnsiChar(arg1), PAnsiChar(arg2));
end;

AnsiString版本分配的缓冲区比具有固定长度数组的版本小。你应该在这里使用SetLength。而且不需要填充输出缓冲区。 - David Heffernan
这真的取决于DLL函数的性质以及其参数实际代表什么。但无论如何,我已经更新了我的示例。 - Remy Lebeau
我认为,我们应该以示例C代码作为模板进行工作,而不是提问者尝试的翻译。它分配长度为135的数组,并且不初始化邮箱的内容。 - David Heffernan

2

您正在使用的Delphi版本采用Unicode编码,其中Char 是16位的WideChar别名,PCharPWideChar的别名。

只需将Char替换为AnsiCharPChar替换为PAnsiChar即可。

function IxCommand(command, mailbox: PAnsiChar): Cardinal; 
  stdcall; external 'IxxDLL.dll';

返回值是UINT,在Delphi中对应Cardinal

你使用的调用代码过于复杂。我会这样做:

var
  retval: Cardinal;
  mailbox: array [0..134] of AnsiChar;
....
retval := IxCommand('Accel?:a', mailbox);
// check retval for errors

正如您所观察到的,存在缓冲区溢出的可能性。我不确定应该如何防范这种情况。如果库的文档存在的话,它可能会解释如何防范。


非常感谢您的快速和有用的回答。现在它已经按预期工作了。万岁! - Mark Skeels

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接