尝试从 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 时出错的主要内容,如果未能解决你的问题,请参考以下文章

Delphi程序怎么调用C#写的dll类库

VS2013 C# 调用 cognex 的QuickBuild做程序时发生一个错误

从 C# 调用非托管 C++ VS 6.0 MFC dll

VS2013创建C#控制台程序,调试错误,尝试运行项目时出错:拒绝访问

从 c# 调用 C++ 代码

从 C# 代码调用 delphi DLL 函数