COMMENTS
Hi there. The article was of exceptional use for me. However, I would also like to see the Delphi code for creating com object, calling the c# code, etc. Maybe You might post the complete example.
E.g. I tried several types of calls before Delphi library stopped my headache with its "The system cannot find the file specified" messages. I finally stopped with this (Delphi):
procedure TFormPranesimai.CallNETCOM;
var
FMyComObject : IDogEvents;
begin
OleCheck(CoCreateInstance(
Class_Dog,
nil,
CLSCTX_ALL,
IDogEvents,
FMyComObject));
Dog.Bark;
end;
However I'm not sure it's the right way.
Andy,
You don't need to do anything convoluted on Delphi's side. I outlined the steps on Delphi's side above. It's the C# side that makes you jump through hoops.
This code setup only works for Delphi 7. It won't work for anything earlier than that. I briefly tested it with Delphi 2010, and it seemed to work. Delphi is very picky about COM signatures. The C# code above will not work with Delphi 6.
If you encounter problems, it usually has to do with how the C# COM code was composed.
Hi there. I also noticed, that Your c# class is not inherited from IDogEvents interface. Is that as intended?
When i try not inheriting(implementing) the interface on c#, Delhpi side throws "Interface not supported" messages during runtime.
However, if I implement(inherit) interface, c# refuses to compile, says that class does not implements interface members (Bark Howl Eat), when I define them as events in class. It wants to see functions. It only works, when I define Bark, Howl and Eat in class as functions, not events.
"I also noticed, that Your c# class is not inherited from IDogEvents interface. Is that as intended?"
Correct. Notice the ComSourceInterfaces attribute on the Dog class. This is what actually "wires" up the interface to the class.
"However, if I implement(inherit) interface, c# refuses to compile, says that class does not implements interface members (Bark Howl Eat), when I define them as events in class. It wants to see functions. It only works, when I define Bark, Howl and Eat in class as functions, not events."
Correct. This is the only way I've found this to work. That's why I laid out a full C# example. I just copy and pasted the example code above into VS2010, and it compiled the first time.
One thing that might not be clear (as I didn't see it in the post above), is that you call the three methods in the C# class, from Delphi, in order to invoke\fire the events. These would look like this in your Delphi code:
TestDog.MakeDogBark; - Will fire the Bark event
TestDog.MakeDogEat; - Will fire the Eat event
TestDog.MakeDogHowl; - Will fire the Howl event
Notice the definitions for 3 buttons in the Delphi code:
procedure btnBarkClick(Sender: TObject);
procedure btnHowlClick(Sender: TObject);
procedure btnEatClick(Sender: TObject);
These are where I call the MakeDogXXX functions/methods. Sorry if that wasn't clear.
COM programming is weird. Remember this setup only works for Delphi 7. You are on your own if you are using a different version, as I don't have experience with other versions and their COM idiosyncrasies.
Hi Hector,
Thanks so much for posing this, it has helped a lot. One quick question, might be dumb, but what is the code you are using to create the TestDog object on from create? I can't seem to figure out how to initialize the object. (I'm not a Delphi programer, but I need to write a Delphi example for our SDK).
Any help would be great.
John,
I don't have Delphi installed anymore, but I believe you do object instantiation like this:
TestDog := TDog.Create;
Hi,
I followed the steps described in your great article. I am close to make it work, but there is a problem: my event handlers from the delphi win32 client application do not get called.
I am doing this:
TestLib := TTest.Create(nil);
TestLib.Connect;
TestLib.OnOperationCompleted := lfOnOperationCompleted;
TestLib.OnOperationCompletedNoArgs := lfOnOperationCompletedNoArgs;
I have the handlers:
procedure TForm1.lfOnOperationCompleted(ASender: TObject; const mess: WideString);
begin
ShowMessage(mess);
end;
procedure TForm1.lfOnOperationCompletedNoArgs(ASender: TObject);
begin
ShowMessage('lfOnOperationCompletedNoArgs');
end;
Please let me know I am missing.
Please post delphi client code.
I'm on the road, and will be back home on Monday, the 24th. I'll look at this then.
Hi,
thank you very much for taking the time to answer.
I'll be waiting. I would really appreciate your help as this issue seems to be a show stopper for my application.
Best regards,
m
Arrrrggghhhh! I don't have Delphi installed anymore, nor do I know where I put the install CD. In the meantime, make sure that the signatures for the events match the signature of the handler. Events with no parameters will have the following signature; procedure Barked(Sender: TObject); Events with parameters will have a very different signature. I remember fighting with this.
Ok, I meant to say the signature will look like this; FOnBark: TNotifyEvent; TNotifyEvent translates into TObject in the handlers.
Here is the complete Delphi Code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComDog_tlb;
type
TForm1 = class(TForm)
Memo1: TMemo;
ButtonBark: TButton;
ButtonHowl: TButton;
ButtonEat: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ButtonBarkClick(Sender: TObject);
procedure ButtonHowlClick(Sender: TObject);
procedure ButtonEatClick(Sender: TObject);
private
{ Private declarations }
protected
procedure Barked(Sender: TObject);
procedure Howled(Sender: TObject);
procedure Ate(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
TestDog: TDog;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TestDog := TDog.Create(self);
TestDog.OnBark := Barked;
TestDog.OnHowl := Howled;
TestDog.OnEat := Ate;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if (TestDog <> nil) then
TestDog.Free;
end;
procedure TForm1.ButtonBarkClick(Sender: TObject);
begin
TestDog.MakeDogBark;
end;
procedure TForm1.ButtonEatClick(Sender: TObject);
begin
TestDog.MakeDogEat;
end;
procedure TForm1.ButtonHowlClick(Sender: TObject);
begin
TestDog.MakeDogHowl;
end;
procedure TForm1.Barked(Sender: TObject);
begin
Memo1.Lines.Add('Barked');
end;
procedure TForm1.Howled(Sender: TObject);
begin
Memo1.Lines.Add('Howled');
end;
procedure TForm1.Ate(Sender: TObject);
begin
Memo1.Lines.Add('Ate');
end;
end.
Valuable article! My personal hair-pulling and banging-head-against-the-wall moment:
1. Events that originated in Delphi code worked fine (Delphi -> C# COM -> Delphi)
2. Events that originated in C# code where not routed to Delphi (C# app -> C# COM -> Delphi)
I solved it by descending the class Dog from ServicedComponent:
using System.EnterpriseServices;
public class Dog : ServicedComponent
{
}
Other articles advice to descend from MarshalByRefObject, but that did not work for me.