2

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

Kate Gregory
  • 18,808
  • 8
  • 56
  • 85
Alexus
  • 436
  • 1
  • 6
  • 19

2 Answers2

4

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?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thank you for your answer. Now it works fine! :) I was confused using Delphi pointers. For future investigations I need to remember that there is no diffenerce between languages in pointers using :) Thank you – Alexus Sep 04 '12 at 04:18
  • Main reason why pure pointers, but not Interface pointers is using Win32API to transfer pointers from one library to another (dont ask why, just customer desire) – Alexus Sep 04 '12 at 04:48
1

Delphi fully supports COM/ActiveX, regardless of where the pointer is coming from.

Q: What version of Delphi are you using?

SUGGESTION:

1) Create a C# test client to test your ProcPreviewEndArgs.CreateNew() method. See if it works.

2) Review this article:

  • http://delphi.about.com/library/weekly/aa121404a.htm

    Change your Delphi code to use COM/ActiveX classes and GUIDS (as opposed to Delphi pointers). Make it work the same as the C# test client you got working in Step 1. Make sure you're doing all the "basics", including calling CoInitialize() somewhere in your Delphi .exe before you start using COM/ActiveX.

3) Please consider if you need COM/ActiveX at all. Perhaps your design can use PInvoke without COM/ActiveX?

paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • Thank you for your reply. Yes, its totally easier to use your way but in my case I need to send pointer via Windows messaging queue (WndProc and so on), where in lParam Im trying to send pointer – Alexus Sep 03 '12 at 20:11
  • 2
    @Alexus I hope you realise that you cannot send pointers across processes. In other words, the recipient of the message cannot live in a different process. – David Heffernan Sep 03 '12 at 20:29