Rick cf
Rick cf

Reputation: 134

Error in trying to call a DELPHI XE2 DLL from VS2013 C# program

I am trying to use Pinvoke from a C# VS2013 program to access an existing DelphiXE2 dll program. The Delphi program takes an xml file and an IB database file and updates the database according to the xmlfile. The DelphiXE2 dll program is successfully being called from another Delphi program. In C# am trying to invoke the GETxml program. I am getting an {"External component has thrown an exception."} error.

I need a syntax check on what I am trying to do as I am new to C# and DELPHI.

C#

[DllImport("MSA.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, EntryPoint = "GetXML")]

extern static int GetXML([MarshalAs(UnmanagedType.LPStr)] string a, [MarshalAs(UnmanagedType.LPStr)] string b, Boolean c);

int retval = 0;
retval = GetXML(txtPath.Text.ToString().Trim(), txtCabFile.Text.ToString().Trim(), false);

Existing Delphi program: (I noticed that the database open is not occurring in this dll function. In fact it is occurring in the calling Delphi program before the call to GetXML. Not sure if this is a problem or not since I am calling from C# program.)

  function GetXML(DatabasePath: pChar; OFileName: pChar; Silent: Boolean = True): Integer; stdcall;
  var
    xmlDatabase: TIBDatabase;
    xmlTransaction: TIBTransaction;
    objXML: TXML;
  begin
  Result := -1;
  if FileExists(oFilename) then
  begin
  objXML := nil;
  xmlDatabase := nil;
 try
  xmlDatabase := TIBDatabase.Create(nil);
  xmlTransaction := TIBTransaction.Create(xmlDatabase);
  xmlTransaction.DefaultDatabase := xmlDatabase;

  xmlDatabase.DatabaseName := DatabasePath;
  xmlDatabase.Params.Add('user_name=SYSTASS');
  xmlDatabase.Params.Add('password=pswdf88');
  xmlDatabase.LoginPrompt := False;
  xmlDatabase.DefaultTransaction := xmlTransaction;
  xmlDatabase.Connected := True;
  xmlTransaction.StartTransaction;
  try
    objXML := TXML.Create(XMLDatabase, IsSecGDB(XMLDatabase)); //uses IBQUERY to start transaction
    objXML.XMLIntoDB(oFilename, Silent);
    xmlTransaction.Commit;
    result := 1;
  except
    on e: Exception do
    begin
      xmlTransaction.Rollback;
      raise Exception.Create('GetXML Exception: ' + e.Message);
    end;
  end;
finally
  FreeAndNil(objXML);
  if xmlDatabase <> nil then
    xmlDatabase.Close;
  FreeAndNil(xmlDatabase);
end;

end else begin raise Exception.Create('Filename ' + oFilename + ' parameter was not found.'); end; end;

Upvotes: 1

Views: 342

Answers (1)

David Heffernan
David Heffernan

Reputation: 612794

First of all, let's look at the mis-match across the two sides of the interop boundary. You are passing ANSI strings from C#, but the Delphi code expects UTF-16.

[DllImport("MSA.dll", CallingConvention = CallingConvention.StdCall, 
    CharSet = CharSet.Unicode, EntryPoint = "GetXML")]
extern static int GetXML(
    [MarshalAs(UnmanagedType.LPStr)] 
    string a, 
    [MarshalAs(UnmanagedType.LPStr)] 
    string b, 
    Boolean c
);

Although you specified CharSet.Unicode you then went on and told the marshaller explicitly to marshal as PAnsiChar by using UnmanagedType.LPStr. For UTF-16 you would use UnmanagedType.LPWStr

However, your p/invoke is needlessly complex. I would write your p/invoke like this:

[DllImport("MSA.dll", CharSet = CharSet.Unicode)]
extern static int GetXML(string a, string b, bool c);

Note that CallingConvention.StdCall is the default. There's no need to specify EntryPoint here since it just repeats the function name. And you do not need the MarshalAs attributes since you specified CharSet.Unicode.


Now, a far bigger problem that you face is that you throw a native Delphi exception across the interop boundary. That's why the p/invoke layer objected and said:

External component has thrown an exception

Throwing exceptions across this interop boundary is an absolute no-no. Stop doing that. You'll have to handle errors the old-school way with error codes.

Upvotes: 2

Related Questions