Reputation: 134
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
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