Search code examples
c#delphidelphi-xe2com-interop

C# COM => pointer => delphi interface


Couple of days I cant make solution to solve my problem. I have interface and class in C# which visible as COM server. And I have Delphi application where I need to access this interface. But in my case I need in C# side to return 32 bit pointer to interface and at Delphi side - to convert pointer to interface. What is done? c# side. All attributes written well, source code is cutted because original class is much bigger, but idea is understandable:

[ComVisible(true)]
[CLSCompliant(true)]
[Guid("C1A57BD0-AAA2-4AB8-BA2F-ADFA04275AD5")]
[ClassInterface(ClassInterfaceType.None),
 ComSourceInterfaces(typeof(IProcPreviewEndArgs))]
public class ProcPreviewEndArgs : IProcPreviewEndArgs
{

   [ComVisible(true), PreserveSig]
   public IntPtr CreateNew()
   {
       var obj = new ProcPreviewEndArgs();
       GC.SuppressFinalize(obj);
       return Marshal.GetComInterfaceForObject(obj, typeof (IProcPreviewEndArgs));
   }
}

delphi side:

type
  PIProcPreviewEndArgs = ^IProcPreviewEndArgs;

 Index, Res: Integer;
 ppa : IProcPreviewEndArgs;
 pppa : PIProcPreviewEndArgs;

  ppa := CoProcPreviewEndArgs.Create;   // Creates COM object via Delphi services
  ppa.CreateNew(Res);                   // Creates object of the same type, returns as Ret (Integer)
  pppa := PIProcPreviewEndArgs(Res);    // Trying to cast Intereger to interface
  pppa^.CreateNew(Res); // just to test calling posibility

On pppa^ (getting instance by pointer) ACCESS VIOLATION exception is thrown. pointer Res is not null. It is about 60 000 000. Basicly it is kernel space area.

UPD

[CLSCompliant(true), ComVisible(true),
 InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
 Guid("10572D64-B612-468F-86B3-D12F0B6E3CD2")]
public interface IProcPreviewEndArgs
{
    IntPtr CreateNew();
    IntPtr Get(int aMin, int aMax, uint cMin, uint cMax);
    void ToAngle(int x, int y, int w, int h, out int nx, out int ny, out int nw, out int nh);
    IntPtr GetImage();
}

Delphi (generated by Delphi and working well when using standard Delphi methods to manipulate COM objects):

 IProcPreviewEndArgs = interface(IUnknown)
    ['{10572D64-B612-468F-86B3-D12F0B6E3CD2}']
    function CreateNew(out pRetVal: Integer): HResult; stdcall;
    function Get(aMin: Integer; aMax: Integer; cMin: LongWord; cMax: LongWord; out pRetVal: Integer): HResult; stdcall;
    function ToAngle(x: Integer; y: Integer; w: Integer; h: Integer; out nx: Integer; 
                     out ny: Integer; out nw: Integer; out nh: Integer): HResult; stdcall;
    function GetImage(out pRetVal: Integer): HResult; stdcall;
  end;

Delphi version: Delphi XE2


Solution

  • I believe that CreateNew returns IProcPreviewEndArgs rather than ^IProcPreviewEndArgs. So your code should read:

    var
      ppa1, ppa2: IProcPreviewEndArgs;
    ....
    ppa1 := CoProcPreviewEndArgs.Create;   
    ppa1.CreateNew(Res);                   
    ppa2 := IProcPreviewEndArgs(Res);    
    ppa2.CreateNew(Res); 
    

    If I were you I'd declare those Integer return values that map to C# IntPtr as Pointer in Delphi. That way your code will work if ever you compile a 64 bit version.

    I'd also contemplate using safecall rather than stdcall and so get the compiler to convert any errors from HRESULT to Delphi exception.

    IProcPreviewEndArgs = interface(IUnknown)
      ['{10572D64-B612-468F-86B3-D12F0B6E3CD2}']
      function CreateNew: Pointer; safecall;
      function Get(aMin: Integer; aMax: Integer; cMin: LongWord; cMax: LongWord): Pointer; safecall
      procedure ToAngle(x: Integer; y: Integer; w: Integer; h: Integer; out nx: Integer; 
                        out ny: Integer; out nw: Integer; out nh: Integer); safecall;
      function GetImage: Pointer; safecall;
    end;
    

    I also wonder why you have to return untyped pointers in, for example, CreateNew. Why can't you declare that function to return IProcPreviewEndArgs on both sides?