【问题标题】:Interface result delphi to c# - class define dll use in c#接口结果delphi到c# - 类在c#中定义dll使用
【发布时间】:2012-12-31 16:40:38
【问题描述】:

我在 dll 中定义了一个类。我通过接口导出这个类。我在其他 Delphi 项目中使用这个 dll 和类非常好,但我想在 c# 中使用这个类,但给我发送错误。

源 DLL:

type
  IMyDate = interface
    ['{D032F796-167D-4B0D-851D-2AEEA226646A}']
    Procedure Analyze_Date(var y:Integer; m,d:Integer); stdcall;
  end;

  TMyDate = Class(TInterfacedObject, IMyDate)
  Public
    Procedure Analyze_Date(var y:integer; m,d:Integer); stdcall;
  End;

procedure TMyDate.Analyze_Date(var y:Integer; m, d: Integer);
begin
  y:= m+d;
end;

Function MyDate:IMyDate; stdcall;
begin
  result:= TMyDate.Create;
end;

exports
  MyDate;

Delphi 示例中的单元使用:

Function MyDate:IMyDate; stdcall; external 'Project11';

implementation

procedure TForm3.Button1Click(Sender: TObject);
var
  md:IMyDate;
  var y,m,d:Integer;
begin
  md:= MyDate;
  y:=10;
  m:=20;
  d:=30;
  md.Analyze_Date(y,m,d);
  showMessage(intTostr(y));
end;

在c#中使用但报错:

//[Guid("D032F796-167D-4B0D-851D-2AEEA226646A")]
public interface IMyDate
{
    void Analyze_Date(ref int y, int m, int d); 
}

[DllImport(
"Project11.dll"
, EntryPoint = "MyDate"
, SetLastError = true
, CallingConvention = CallingConvention.StdCall
, CharSet = CharSet.Unicode)]
public static extern IMyDate MyDate(); 

private void button9_Click(object sender, EventArgs e)
{
  int y, m, d;
  IMyDate da;
  da = MyDate(); // get error this line
  y = 10;
  m = 20;
  d = 30;
  da.Analyze_Date(ref y, m, d);
}

更新

谢谢你的回答

我向接口和类添加了新函数,但我的函数只返回 false

我在delphi中的函数和过程也是stdcall,而C#中的接口是正常的!!!

IMyDate = interface
['{D032F796-167D-4B0D-851D-2AEEA226646A}']
Function testing():Boolean; stdcall;
end;

TMyDate = Class(TInterfacedObject, IMyDate)
Public
Function testing():Boolean; stdcall;
End;

function TMyDate.testing(): Boolean;
begin
result:= true;
end;

c#:

[ComImport, Guid("D032F796-167D-4B0D-851D-2AEEA226646A"),
 InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMyDate
{
    bool testing();        
}

[DllImport(
      "Project11.dll"
      , EntryPoint = "CreateMyDate"//"MyDate"
      , SetLastError = true
      , CallingConvention = CallingConvention.StdCall
      , CharSet = CharSet.Unicode)]    
public static extern void CreateMyDate(out IMyDate Date); 

    Boolean b;

    b = da.testing();  //this line only return false
    if (b)
    {
        MessageBox.Show("ffff");
    }

【问题讨论】:

  • 你能告诉我们错误吗?
  • 如果您需要帮助,请告诉我们错误...只是说...。

标签: c# delphi


【解决方案1】:

我看到的第一个问题是,Delphi 的函数返回值二进制接口与 C# 使用的不同(实际上是所有其他主要的 Windows 编译器)。

所以不要使用函数,而是使用out 参数。

procedure CreateMyDate(out Date: IMyDate); stdcall;
begin
  Date := TMyDate.Create;
end;

然后在 C# 上你可以像这样声明函数:

[DllImport(@"Project11.dll")]
public static extern void CreateMyDate(out IMyDate Date); 

请注意,我删除了您的DllImport 属性中的大量虚假参数。我猜你是随机添加的!

另一个问题是界面。它需要声明为支持IUnknown 的COM 接口。像这样:

[ComImport, Guid("D032F796-167D-4B0D-851D-2AEEA226646A"), 
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMyDate
{
    void Analyze_Date(ref int y, int m, int d); 
}

更新 1

更新问题的第一个问题是我在原始答案中未能找到的问题。声明 C# 接口的方式意味着编组器假定本机函数都返回 HRESULT COM 错误代码。如果 C# 方法有返回值,则它会隐式转换为 out 参数。在 Delphi 中有一种简单的方法可以实现这一点。您在接口中的每个方法上用safecall 替换stdcall,显然是在该接口的实现中。请注意,您不应更改 CreateMyDate 函数的调用约定。

所以你的 Delphi 代码应该是这样的:

IMyDate = interface
  ['{D032F796-167D-4B0D-851D-2AEEA226646A}']
  Function testing():Boolean; safecall;
end;

TMyDate = Class(TInterfacedObject, IMyDate)
Public
  Function testing():Boolean; safecall;
End;

更新问题中方法的另一个问题是 C# 将 bool 编组为 4 字节值,但 Delphi Boolean 只是一个字节。这个错误实际上是良性的,但是你可以通过使接口的两侧匹配来解决这个问题。例如:

[return: MarshalAs(UnmanagedType.U1)]
bool testing();        

我重复上面所说的。我从您的 DllImport 属性中删除了虚假参数。特别是不要使用SetLastError,因为你的代码没有设置 Win32 last 错误。你的DllImport 应该和我的回答完全一样。不要通过向属性添加不必要的额外参数来混淆事物。

更新 2

您在 cmets 中说您不能在 Delphi 端使用 safecall。这意味着您需要在接口的 C# 端抑制 HRESULT 签名转换。这是通过MethodImplOptions.PreserveSig 完成的。您需要将其应用于 C# 接口上的每个方法。

[ComImport, Guid("D032F796-167D-4B0D-851D-2AEEA226646A"), 
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMyDate
{
    [MethodImplAttribute(MethodImplOptions.PreserveSig)]
    void Analyze_Date(ref int y, int m, int d); 

    [MethodImplAttribute(MethodImplOptions.PreserveSig)]
    [return: MarshalAs(UnmanagedType.U1)]
    bool testing();  
}

该接口与声明如下的 Delphi 接口相匹配:

IMyDate = interface
  ['{D032F796-167D-4B0D-851D-2AEEA226646A}']
  procedure Analyze_Date(var y: Integer; m, d: Integer); stdcall;
  function testing(): Boolean; stdcall;
end;

【讨论】:

  • 您好,感谢您的回答和 cmets,但如果我想更改 Interface delphi for Exaple: IMyDate = interface({IUnknown}IInvokable) ['{D032F796-167D-4B0D-851D-2AEEA226646A}'] Analize_Date(变量 y:整数;m,d:整数);标准调用;结尾;并通过 Stdcall 定义过程,但我现在不知道如何在 C# 中定义此接口?
  • 我很惊讶地看到在 Delphi C# 互操作场景中混合了 P/Invoke 和 COM 调用。通常是其中之一。
  • @j。它允许您在没有完整 COM 的情况下公开 OOP 之类的东西。
  • @大卫。感谢您的澄清。尽管我不得不说,它仍然不了解混合与遵循处理此类问题的传统方法相比的好处。是性能相关还是我缺少的其他东西?
  • 谢谢,但我通过 [return: MarshalAs(UnmanagedType.U1)] bool testing(); 编辑源代码;但也返回 False 我在 delphi 中的 Interfce 必须是 Stdcall 并且不能使用我现在不使用的安全调用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-12
  • 2011-04-03
  • 2022-11-25
  • 2016-11-07
  • 1970-01-01
  • 2013-01-21
相关资源
最近更新 更多