我正试图使用C#VS2013程序中的Pinvoke来访问现有的DelphiXE2 dll程序。Delphi程序获取一个xml文件和一个IB数据库文件,并根据xml文件更新数据库。从另一个Delphi程序成功调用DelphiXE2 dll程序。在C#中,我试图调用GETxml程序。我收到一个{"外部组件引发异常。"}错误。
我刚接触C#和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);
现有的Delphi程序:(我注意到数据库打开没有发生在这个dll函数中。事实上,它发生在调用GetXML之前的调用Delphi程序中。不确定这是否是一个问题,因为我是从C#程序调用的。)
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;
完其他的开始引发异常。Create(找不到"Filename"+oFilename+"参数。");终止终止
首先,让我们看看interop边界两侧的不匹配。您正在从C#传递ANSI字符串,但Delphi代码需要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
);
尽管您指定了CharSet.Unicode
,但随后继续并明确告诉编组器使用UnmanagedType.LPStr
编组为PAnsiChar
。对于UTF-16,您将使用UnmanagedType.LPWStr
但是,p/invoke是不必要的复杂。我会这样写你的p/invoke:
[DllImport("MSA.dll", CharSet = CharSet.Unicode)]
extern static int GetXML(string a, string b, bool c);
请注意,CallingConvention.StdCall
是默认值。这里不需要指定EntryPoint
,因为它只是重复函数名称。并且由于指定了CharSet.Unicode
,因此不需要MarshalAs
属性。
现在,您面临的一个更大的问题是,在interop边界上抛出一个本地Delphi异常。这就是为什么p/invoke层反对并说:
外部组件引发异常
在这个互操作边界上抛出异常是绝对不允许的。别那样做了。你将不得不用老式的错误代码来处理错误。