尝试从 VS2013 C# 程序调用 DELPHI XE2 DLL 时出错
Posted
技术标签:
【中文标题】尝试从 VS2013 C# 程序调用 DELPHI XE2 DLL 时出错【英文标题】:Error in trying to call a DELPHI XE2 DLL from VS2013 C# program 【发布时间】:2013-12-16 13:27:09 【问题描述】:我正在尝试使用来自 C# VS2013 程序的 Pinvoke 来访问现有的 DelphiXE2 dll 程序。 Delphi 程序获取一个 xml 文件和一个 IB 数据库文件,并根据 xml 文件更新数据库。 DelphiXE2 dll 程序被另一个 Delphi 程序成功调用。在 C# 中,我试图调用 GETxml 程序。我收到 "External component has throw an exception." 错误。
由于我是 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;
结束 别的 开始 raise Exception.Create('Filename ' + oFilename + ' 未找到参数。'); 结尾; 结束;
【问题讨论】:
为什么写int retval = 0; retval = GetXML(...);
?当然你的意思是int retval = GetXML(...);
是的,改了。谢谢。
【参考方案1】:
首先,让我们看一下互操作边界两侧的不匹配情况。您从 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
,因为它只是重复了函数名。而且您不需要 MarshalAs
属性,因为您指定了 CharSet.Unicode
。
现在,您面临的一个更大的问题是跨互操作边界抛出原生 Delphi 异常。所以p/invoke层反对说:
外部组件抛出异常
在这个互操作边界上抛出异常是绝对不能的。别那样做。您必须使用错误代码以老式方式处理错误。
【讨论】:
天哪,它成功了!大卫你是最棒的。感谢您教我并帮助我理解我想要做什么。至于跨互操作边界的异常,我确实看到了这个问题,需要向 Delphi 应用程序的经理提出。以上是关于尝试从 VS2013 C# 程序调用 DELPHI XE2 DLL 时出错的主要内容,如果未能解决你的问题,请参考以下文章
VS2013 C# 调用 cognex 的QuickBuild做程序时发生一个错误