0

I've found the answer but negative to what I'm trying to do:

Passing method pointer from C# to Delphi DLL

Is there a possibility of call a delegate of object from Delphi in C#? Because all of Delphi callers from the example above are static and exported. I am using OleVariant and would like to invoke a caller from Delphi object in C# using GetDelegateForFunctionPointer. But when I try, debugger in Delphi event break in that method and then I get "Attempted to read or write protected memory".

Delphi code:

TOnEvent = procedure() of object;
TOnEventStdCall = procedure() of object; stdcall;

TSimpleClass = class
  FUserControl: OleVariant;
  FOnEventHandler : TOnEvent;
  FOnEventStdCallHandler : TOnEventStdCall;
  constructor Create();  
  procedure ProcOnEventStdCall(); stdcall;
end;    
...
constructor TSimpleClass.Create();
begin
  FHost := TJclClrHost.Create('v4.0.30319');
  FHost.Start;
  FUserControl := FHost.DefaultAppDomain.CreateInstance('CSharpLibrary','CSharpLibrary.Init').Unwrap;
  FOnEventHandler := nil;
  FOnEventStdCallHandler := ProcOnEventStdCall;
  FUserControl.SetEvent(LongInt(@FOnEventStdCallHandler), LongInt(self));
end;

procedure TSimpleClass.ProcOnEventStdCall; stdcall;
begin
    ShowMessage('Invoked');
    if Assigned(FOnEventHandler) then 
      FOnEventHandler();               // !!! exception here
end;

and C# code:

 struct TMethod
 {
    IntPtr pMethod;
    IntPtr pInstance;  
 }
 [UnmanagedFunctionPointer(CallingConvention.StdCall)]
 public delegate void TEvent(IntPtr instance);
 private TOnEventStdCall _delphiHandler;

 public void SetEvent(Int32 method, Int32 instance)
 {
      m.pMethod = new IntPtr(method);
      m.pInstance = new IntPtr(instance);
      _delphiHandler += (TEvent)Marshal.GetDelegateForFunctionPointer(m.pMethod, typeof(TEvent));
 }
...
_delphiHandler.Invoke(m.pInstance);

The similar question I believe is, but didn't get an answer:

c# equivalent of pascal/delphi procedure of object

Mike Lowery
  • 2,630
  • 4
  • 34
  • 44
  • Not enough detail here, we need a [mcve]. However, the call to SetDocumentCompleatedEvent can't do anything useful since it is passed only half of the method, namely the code but not the data. – David Heffernan Aug 31 '21 at 06:00
  • I've corected the question and will try to prepare the appropriate executable piece of code. I'm using JCL to execute C# code in Delphi and try to cut all the unnecessary code lines. – Paweł Kępiński Aug 31 '21 at 06:21
  • Still got the same problem. You only pass half of the method. A method is two pointers. You pass one only. – David Heffernan Aug 31 '21 at 06:29
  • I don't understand what you wrote. Which method has two pointers? There is two methods. One "SetEvent" pass the pointer to the procedure in Delphi which I want to invoke in C#. And the seccond is the invoking procedure without parameters. – Paweł Kępiński Aug 31 '21 at 06:38
  • @PawełKępiński `FUserControl.SetEvent` is meant - what else? As per the first comment you nowhere pass the method's data, only its code. – AmigoJack Aug 31 '21 at 07:09
  • @PawełKępiński As stated [here](https://docwiki.embarcadero.com/RADStudio/Sydney/en/Internal_Data_Formats_(Delphi)#Procedural_Types): "A method pointer is stored as a pointer to the entry point of a method, followed by a pointer to an object." – Olivier Aug 31 '21 at 07:16
  • Ok. I understand it like I am passing a pointer in a wrong way in Delphi code. Could I ask for a propriate implementation in Delphi ? – Paweł Kępiński Aug 31 '21 at 07:24
  • `SetEvent()` should receive 2 pointers: the method address and the object address. Internally, a method is a function that takes an object as first parameter. So `TSimpleClass.ProcOnEvent()` is actually a function that takes an object of type `TSimpleClass` as first parameter. – Olivier Aug 31 '21 at 08:06
  • Ok. Work with the ShowMessage. But can't fire normal caller in delphi. Maybe something wrong. – Paweł Kępiński Aug 31 '21 at 08:49
  • You should pass `Self` (which is already a pointer), not `@Self`. – Olivier Aug 31 '21 at 09:21
  • I think, it works well. Thanks. – Paweł Kępiński Aug 31 '21 at 11:17

1 Answers1

0

I haven't tested the code. In Delphi, however, it would have to go as follows:

type
  TOnEvent = procedure() of object;
  TSimpleClass = class
     FUserControl: OleVariant;
     constructor Create();
     procedure ProcOnEvent();
  end;

  procedure EventStdCall(AObj: TObject); stdcall;
  var
    r   : TMethod;
    func: TOnEvent;
  begin
    r.Data := AObj;
    r.Code := @TSimpleClass.ProcOnEvent;
    func   := TOnEvent(r);
    func(); //Execute
  end;

  constructor TSimpleClass.Create();
  begin
    FHost := TJclClrHost.Create('v4.0.30319');
    FHost.Start;
    FUserControl := FHost.DefaultAppDomain.CreateInstance('CSharpLibrary','CSharpLibrary.Init').Unwrap;
    FUserControl.SetEvent(LongInt(@EventStdCall), LongInt(Self));
 end;

 procedure TSimpleClass.ProcOnEvent;
 begin
   ShowMessage('Invoked');
 end;

For "Self", you can use Int32 in C #. This also applies to the parameter of the TObject type in the EventStdCall method,

USauter
  • 295
  • 1
  • 9
  • Ok. Olivier also give me the advice that I've notive in your example - "LongInt(Self)" - now my example work - and looks preaty pure. Thanks for advice. – Paweł Kępiński Aug 31 '21 at 09:26