我在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);
}
更新
谢谢你的回答
我向Interface和class添加了新函数,但我的函数只返回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");
}
答案 0 :(得分:5)
我看到的第一个问题是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#接口的方式意味着marshaller假定本机函数都返回HRESULT
个COM错误代码。如果C#方法具有返回值,则它将隐式转换为out
参数。在Delphi中有一种简单的方法可以实现这一点。您可以在接口中的每个方法上用stdcall
替换safecall
,显然在该接口的实现中。请注意,您不应更改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最后一个错误。您的DllImport
应该完全符合我的回答。不要通过向属性添加不必要的额外参数来混淆事物。
更新2
你在评论中说你不能在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;