Author Topic: Exposing C# COM server events to Delphi client applications  (Read 331 times)

Slinky

  • Trusted Member
  • Guru
  • ******
  • Posts: 386
  • Gold Plated Slinky
    • View Profile
I have been tinkering with this for a few days, without any results. Here's the story, my current employer is using VS 2010 and C#. We have a client that uses Delphi 7. I'm the only one on my employer's team that knows anything about Delphi. So I'm tasked with creating a COM server out of our code. The type library exports the properties, functions, and procedures just fine. It's the events that are giving me grief.

So here's a simplified version of the C# code:

Code: [Select]
[Guid("3D205377-9130-442B-AB12-8056DD7A3192"),
ClassInterface(ClassInterfaceType.AutoDual)]
public class SILAgent : MarshalByRefObject
{
public delegate void CallArrived(object sender, CallRecievedEventArgs callData);

    public event CallArrived OnCallArrived;

public string IpAddress { get; set; }

public int Port { get; set; }
}

When I import the type library into Delphi it looks like this:

Code: [Select]
// *********************************************************************//
// Interface: _CallRecievedEventArgs
// Flags:     (4560) Hidden Dual NonExtensible OleAutomation Dispatchable
// GUID:      {373EA64B-BD3D-37EF-906E-CDB169F6B6E7}
// *********************************************************************//
  _CallRecievedEventArgs = interface(IDispatch)
    ['{373EA64B-BD3D-37EF-906E-CDB169F6B6E7}']
    function Get_ToString: WideString; safecall;
    function Equals(obj: OleVariant): WordBool; safecall;
    function GetHashCode: Integer; safecall;
    function GetType: _Type; safecall;
    function Get_PhoneNumber: WideString; safecall;
    procedure Set_PhoneNumber(const pRetVal: WideString); safecall;
    function Get_CallProgress: WideString; safecall;
    procedure Set_CallProgress(const pRetVal: WideString); safecall;
    function Get_UniqueId: WideString; safecall;
    procedure Set_UniqueId(const pRetVal: WideString); safecall;
    function Get_ProjectId: Int64; safecall;
    procedure Set_ProjectId(pRetVal: Int64); safecall;
    function Get_CallStatus: CallProgressStates; safecall;
    procedure Set_CallStatus(pRetVal: CallProgressStates); safecall;
    function Get_Disposition: WideString; safecall;
    procedure Set_Disposition(const pRetVal: WideString); safecall;
    function Get_InfoXML: WideString; safecall;
    procedure Set_InfoXML(const pRetVal: WideString); safecall;
    procedure ParseMessage(const message: WideString); safecall;
    property ToString: WideString read Get_ToString;
    property PhoneNumber: WideString read Get_PhoneNumber write Set_PhoneNumber;
    property CallProgress: WideString read Get_CallProgress write Set_CallProgress;
    property UniqueId: WideString read Get_UniqueId write Set_UniqueId;
    property ProjectId: Int64 read Get_ProjectId write Set_ProjectId;
    property CallStatus: CallProgressStates read Get_CallStatus write Set_CallStatus;
    property Disposition: WideString read Get_Disposition write Set_Disposition;
    property InfoXML: WideString read Get_InfoXML write Set_InfoXML;
  end;

// *********************************************************************//
// Interface: _CallArrived
// Flags:     (4432) Hidden Dual OleAutomation Dispatchable
// GUID:      {3901A611-72B3-3EB9-BCAA-B5FDCB29F782}
// *********************************************************************//
  _CallArrived = interface(IDispatch)
    ['{3901A611-72B3-3EB9-BCAA-B5FDCB29F782}']
  end;

// *********************************************************************//
// DispIntf:  _CallArrivedDisp
// Flags:     (4432) Hidden Dual OleAutomation Dispatchable
// GUID:      {3901A611-72B3-3EB9-BCAA-B5FDCB29F782}
// *********************************************************************//
  _CallArrivedDisp = dispinterface
    ['{3901A611-72B3-3EB9-BCAA-B5FDCB29F782}']
  end;

// *********************************************************************//
// Interface: _SILAgent
// Flags:     (4560) Hidden Dual NonExtensible OleAutomation Dispatchable
// GUID:      {3131BA8A-BFA2-3D7F-9813-904EA285B121}
// *********************************************************************//
  _SILAgent = interface(IDispatch)
    ['{3131BA8A-BFA2-3D7F-9813-904EA285B121}']
    procedure add_OnCallArrived(const value: _CallArrived); safecall;
    procedure remove_OnCallArrived(const value: _CallArrived); safecall;
    property IpAddress: WideString read Get_IpAddress write Set_IpAddress;
    property Port: Integer read Get_Port write Set_Port;
  end;

I did notice that the tlb import file has both an add and remove procedures. So in Delphi I would do something like this:

Code: [Select]
procedure TForm1.FormCreate(Sender: TObject);
begin

TestAgent := CoSILAgent.Create;
TestAgent.Init;
TestAgent.add_OnCallArrived();

end;

I just can't seem to figure out what should the parameter to add_OnCallArrived() should be. Should I create a procedure with the same signature as the C# delegate and assign it to add_OnCallArrived() as a parameter? Can somebody point me in the right direction?

Thanks!

The Gorn

  • Your agonizer, please. And be sure to keep the batteries charged!
  • Trusted Member
  • Wise Sage
  • ******
  • Posts: 14182
  • Gornix user
    • View Profile
Re: Exposing C# COM server events to Delphi client applications
« Reply #1 on: May 19, 2010, 03:58:42 pm »
Are there any examples of a COM server in the source examples and demos that ship with Delphi? When I run into an advanced topic like this I usually try to locate the simplest possible example of what I need to do. Generally my breakthroughs in uncharted waters like this depend on finding someone's code example on the net and emulating how they are doing things.

Doesn't Delphi have variant data types for interfacing to COM? I'd expect any argument list within Delphi to be comprised of variants.

Otherwise, phewwww... Delphi 7 is eight years old.
Gornix is protected by the GPL. *

* Gorn Public License. Duplication by inferior sentient species prohibited.


Slinky

  • Trusted Member
  • Guru
  • ******
  • Posts: 386
  • Gold Plated Slinky
    • View Profile
Re: Exposing C# COM server events to Delphi client applications
« Reply #2 on: May 19, 2010, 04:12:06 pm »
I'm using a trial version of Delphi 2010, as I can't find my Delphi 7 CDs. I didn't see any sample folders.

I've tried quite a few examples, from Google searches, that say they work, but those haven't worked, meaning that I can't either see the events in the type library files, or the events won't connect. I've seen plenty of C# code, but there's no equivalent Delphi code to see this in action. Meh.  :o

Slinky

  • Trusted Member
  • Guru
  • ******
  • Posts: 386
  • Gold Plated Slinky
    • View Profile
Re: Exposing C# COM server events to Delphi client applications
« Reply #3 on: May 24, 2010, 05:23:05 pm »
I finally got this resolved after much trial and error. There were 2 things that I needed to change on the C# code.

1) [ClassInterface(ClassInterfaceType.None)] needed to be changed to [ClassInterface(ClassInterfaceType.AutoDual)]

2) The class that is the source of the events needs to inherit from MarshalByRefObject. This helps if there is any threading done in the source class.

I only needed one thing on the Delphi side. I needed to make sure to have the "Generate Component Wrapper" checkbox checked. This is what will actually build the event scaffolding on Delphi's side.

This is how you do it in Delphi 7:

 1. Select from the menu Project -> Import Type Library
 2. Make sure that "Generate Component Wrapper" is checked
 3. Select the COM class from the list
 4. Click on the "Add Unit" button

The new unit will have the definitions of your COM events.

The Gorn

  • Your agonizer, please. And be sure to keep the batteries charged!
  • Trusted Member
  • Wise Sage
  • ******
  • Posts: 14182
  • Gornix user
    • View Profile
Great going, Slinky
« Reply #4 on: May 24, 2010, 07:17:56 pm »
The last time I did anything with COM and Delphi, I was dealing with Delphi's import wizard that created the wrapper VCL component for you. That is what I drew a blank from in your initial description - I thought "why should he write event procedures, he has real Delphi events in the wrapper, doesn't he?"

I've been away from that environment for so long that I really didn't even know how to describe how to start rolling.
Gornix is protected by the GPL. *

* Gorn Public License. Duplication by inferior sentient species prohibited.



Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf