Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;

like image 533
Mark Skeels Avatar asked Dec 02 '25 15:12

Mark Skeels


2 Answers

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;
like image 73
Remy Lebeau Avatar answered Dec 04 '25 05:12

Remy Lebeau


You are using a Unicode version of Delphi for which Char is an alias to a 16 bit WideChar, and PChar is an alias to PWideChar.

Simply replace Char with AnsiChar and PChar with PAnsiChar.

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

The return value is UINT which maps to Cardinal in Delphi.

The calling code you have used is needlessly complex. I'd do it like this:

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

As you observe, there is scope for buffer overrun. I'm not sure how you are meant to guard against that. Documentation for the library, if it exists, would presumably explain how.

like image 37
David Heffernan Avatar answered Dec 04 '25 07:12

David Heffernan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!