Search code examples
delphidelphi-xe5

call c dll from delphi with strings


I am trying to call a dll from Delphi XE5.

I have spent a day or so Googling for things like "Call C DLL from Delphi" and found a number of pages but nothing really helped me.

I have received examples of how to call the dll in VB:


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

Also calling the DLL in VC 6.0:


#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;
}

A complication I have noticed is the need to define the size of the memory allocation before calling the DLL, as shown above.

The dll takes a command in the first argument and returns result text in the second argument.

Here is the Delphi code I have generated to try and call the DLL. I know the dll loads because it has a splash screen that shows. No error is generated when I call the dll. You will see that I used arrays to allocate space and then assigned their locations to Pchar variables. I do not have any header file for the original dll, nor the source code. You will see I declared the external function using stdcall but I have also tried cdecl with no change.

The Problem: The information returned in arg2 is not the expected text string but a string of what translates as non-english characters (looks like chinese).

I am guessing I am not sending the dll the correct variable types.

The Question: Can anyone help me formulate the declaration of the external function and use it correctly, so that I get back the text strings as desired?

See below:


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;


Solution

  • The problem is character encodings. In Delphi 2009+, PChar is an alias for PWideChar, but the DLL is using Ansi character strings instead, so you need to use PAnsiChar instead of PChar.

    Try this:

    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;
    

    Alternatively:

    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;