2

This is the invoke code

unit CY;

interface
uses windows;
type

 TMember_Rd = Record
    iID: integer;
    sCode: Array[0..255] of char;
    sName: Array[0..255] of char;
  end;

  PTMember_Rd = ^TMember_Rd;

function GetMember(sStore, sMachine: PChar; const sTrack, sCode, sVerify: PChar; 
  Member: PTMember_Rd; sError: PChar): Integer; stdcall; external 'Interface.dll';

implementation

end.

I need to call a GetMember function and to fill the data into the Member, and if there is any error I can get the message from sError.

How do I call this function?

I am not familliar with Delphi; I think my mistake is in the pointer.

Following is my c# code is worked

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace JFTest
{
    public class JFService
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct PTMember_Rd
        {
            public Int32 id;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string sCode;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string sName;
        }

        [DllImport("Interface.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public static extern int GetMember(StringBuilder sStore, StringBuilder sMachine, StringBuilder sTrack, StringBuilder sCode, StringBuilder sVerify, ref PTMember_Rd Member, [MarshalAs(UnmanagedType.LPStr)] string sError);

    }
}

And I don't have other side code, the dll is customer send to me and they don't have source code.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Please show the other side of the interface. I presume there is some C code. Also, please tell us what version of Delphi so that we can tell whether `Char` is one byte wide, or two bytes wide. – David Heffernan Apr 24 '13 at 16:19
  • I just know this interface is worked in other program, and the sError will return unicode, I use delphi 2010 develop it. Thanks David. – user2316439 Apr 24 '13 at 16:24
  • 1
    This is an interop question. You have two sides of an interface that are not matching. You cannot expect us to answer if you only show one side of the interface. Show the other side. Also provide the documentation for the parameters. For the pointer paramerers, who is responsible for allocating memory. Which parameters are input, which are output? – David Heffernan Apr 24 '13 at 16:26
  • 3
    You tagged this `pinvoke`. P/Invoke is for calling native code from .Net, but you've also tagged this `c` and `delphi`. The Delphi code is obviously native, and C code would be native, so is P/Invoke actually involved in this question at all? – Rob Kennedy Apr 24 '13 at 16:28
  • David, the Member and sError is output other parameters is input. – user2316439 Apr 24 '13 at 17:04
  • Your p/invoke is incomplete. You did not show how you called `GetMember` What's more it looks wrong. `StringBuilder` is used when the native code needs to pass text data to the caller. And `string` is used when the managed code passes text to the native code. So it looks like all of those string parameters are the wrong way round. Since the C# code specifies `CharSet.Ansi` you must use `AnsiChar` and `PAnsiChar` in the Delphi code. This is still an incomplete question. – David Heffernan Apr 24 '13 at 17:04
  • You say "the Member and sError is output other parameters is input" but the p/invoke code disagrees with you. – David Heffernan Apr 24 '13 at 17:05

1 Answers1

1

I think your p/invoke code is wrong. You say that Member and sError are output parameters, and the other parameters are input. In which case the p/invoke should be:

[DllImport("Interface.dll", CharSet=CharSet.Ansi)]
public static extern int GetMember(
    string sStore, 
    string sMachine, 
    string sTrack, 
    string sCode, 
    string sVerify, 
    ref PTMember_Rd Member, 
    StringBuilder sError
);

Assuming this is correct, the Delphi should look like this:

type
  TMember_Rd = Record
    iID: integer;
    sCode: Array[0..255] of AnsiChar;
    sName: Array[0..255] of AnsiChar;
  end;

function GetMember(
  sStore: PAnsiChar;
  sMachine: PAnsiChar;
  sTrack: PAnsiChar;
  sCode: PAnsiChar;
  sVerify: PAnsiChar;
  out Member: TMember_Rd;
  sError: PAnsiChar
): Integer; stdcall; external 'Interface.dll';

where sStore, sMachine etc. are string variables that specify the input parameters.

We need to use one byte AnsiChar and PAnsiChar. Your code in the question uses the two byte UTF-16 Char and PChar.

You would call it like this:

var
  sError: AnsiString;
  Member: TMember_Rd;
  retval: Integer;
....
SetLength(sError, 256);
retval := GetMember(
  PAnsiChar(AnsiString(sStore)),
  PAnsiChar(AnsiString(sMachine)),
  PAnsiChar(AnsiString(sTrack)),
  PAnsiChar(AnsiString(sCode)),
  PAnsiChar(AnsiString(sVerify)),
  Member,
  PAnsiChar(AnsiString(sError))
);

I don't know how large the sError string needs to be made. If you don't have any documentation, you can't find out. Better make it large and hope for the best. If you don't make it large enough, and the function attempts to write to that buffer, then you have buffer overrun.

I've assumed that the Member parameter has pure out semantics since that's what you said. If you need to pass data in using some or all of the fields, change it to var and initialise the record accordingly.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • @user You asked the question, got this answer, and then went quiet. Are you still interested in the question? Are you still stuck. Feedback would be nice. – David Heffernan Apr 26 '13 at 06:40