0

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");
    }
2
  • Can you tell us the error? Commented Dec 31, 2012 at 16:43
  • Might be nice to tell us the error... just sayin'... if you want help. Commented Dec 31, 2012 at 16:46

1 Answer 1

5

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;
Sign up to request clarification or add additional context in comments.

15 Comments

hi thanks for answer and comments but if I whant change Interface delphi for Exaple: IMyDate = interface({IUnknown}IInvokable) ['{D032F796-167D-4B0D-851D-2AEEA226646A}'] Analize_Date(var y:integer; m,d:Integer); stdcall; end; and define procedure by Stdcall but i dont now how Define this Interface in C#?
I'm surprised to see mixing P/Invoke and COM invocations in a Delphi C# interop scenario. Usually it's one or the other.
@j. It allows you to expose an OOP like thing without full blown COM.
@David. Thanks for clarifying. Though I have to say, it still don't understand the benefits of the mixe vs. following traditional ways of handling this type of issue. Is it performance related or something else I'm missing?
thanks but i edit source by [return: MarshalAs(UnmanagedType.U1)] bool testing(); but return False also my Interfce in delphi must be Stdcall and cannot use safecall i dont now
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.