khoshghadam
khoshghadam

Reputation: 89

Interface result delphi to c# - class define dll use in c#

I have a class define in dll. I export this class by interface. I use this dll and class in other Delphi project very good but I want use this class in c# but send error for me.

Source 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;

Unit use in Delphi example:

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;

Use in c# but get error:

//[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);
}

Update

thank you for answer

i add new function to Interface and class but my function only return false

also my function and procedure in delphi is stdcall and interface in C# is normaly !!!

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");
    }

Upvotes: 0

Views: 2560

Answers (1)

David Heffernan
David Heffernan

Reputation: 613461

The first issue I see is that Delphi's binary interface for function return values is different from that used by C# (and indeed every other major Windows compiler).

So instead of using a function, use an out parameter.

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

Then on the C# you can declare the function like this:

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

Note that I removed a load of spurious parameters to your DllImport attribute. I guess you were adding them at random!

The other issue is the interface. It needs to be declared as being a COM interface that supports IUnknown. Like this:

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

Update 1

The first problem with the updated question is something I failed to pick up in my original answer. The way the C# interface is declared means that that the marshaller assumes that the native functions all return HRESULT COM error codes. If the C# method has a return value then it is implicitly converted into an out parameter. There is a simple way to achieve this in Delphi. You replace stdcall with safecall on each method in the interface, and obviously in the implementation of that interface. Note that you should not change the calling convention of the CreateMyDate function.

So your Delphi code should be like this:

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

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

Another problem with the method in updated question is that C# marshals bool as a 4 byte value, but Delphi Boolean is just a single byte. This error is actually benign, but you can fix the problem by making the two sides of the interface match. For example:

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

And I repeat what I said above. I removed spurious parameters from your DllImport attribute. In particular don't use SetLastError because your code is not setting the Win32 last error. Your DllImport should be exactly as per my answer. Don't confuse things by adding needless extra parameters to the attribute.

Update 2

You say in comments that you can't use safecall on the Delphi side. Which means that you need to suppress the HRESULT signature transformation on the C# side of the interface. That's done with MethodImplOptions.PreserveSig. You need to apply it to each method on your C# interface.

[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();  
}

This interface matches up with a Delphi interface declared like this:

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

Upvotes: 5

Related Questions