 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Charles Geiser Guest
|
Posted: Fri Dec 05, 2003 5:08 pm Post subject: Delegated Interface |
|
|
To all interface experts,
can someone of you tell me why the following code snippet
throws an "Invalid pointer operation" exception on
destroying MyOb?
type
IShow = interface(IInterface)
['{5316C9A6-D3B2-4A4D-A5E8-FEFBF39E8613}']
procedure p1;
end;
TShowImpl = class(TAggregatedObject, IInterface, IShow)
public
procedure p1;
end;
TMyClass = class(TInterfacedObject, IInterface, IShow)
private
FShow: IShow;
public
constructor Create;
property Show: IShow read FShow implements IShow;
end;
var
MyObj : TMyClass;
implementation
{$R *.dfm}
procedure TShowImpl.p1;
begin
ShowMessage('P1 called');
end;
constructor TMyClass.Create;
begin
inherited create;
FShow := TShowImpl.Create(self);
end;
begin
MyObj := TMyClass.Create;
MyObj .Show.p1;
MyObj .Free; // <<< EInvalidPointer: Invalid pointer
operation
end.
|
|
| Back to top |
|
 |
Jamie Guest
|
Posted: Fri Dec 05, 2003 8:33 pm Post subject: Re: Delegated Interface |
|
|
if memory serves the local block does the freeing up on those types of
objects.
in other words you can consider them automative objects.
the code is in the background.
Charles Geiser wrote:
| Quote: | To all interface experts,
can someone of you tell me why the following code snippet throws an
"Invalid pointer operation" exception on destroying MyOb?
type
IShow = interface(IInterface)
['{5316C9A6-D3B2-4A4D-A5E8-FEFBF39E8613}']
procedure p1;
end;
TShowImpl = class(TAggregatedObject, IInterface, IShow)
public
procedure p1;
end;
TMyClass = class(TInterfacedObject, IInterface, IShow)
private
FShow: IShow;
public
constructor Create;
property Show: IShow read FShow implements IShow;
end;
var
MyObj : TMyClass;
implementation
{$R *.dfm}
procedure TShowImpl.p1;
begin
ShowMessage('P1 called');
end;
constructor TMyClass.Create;
begin
inherited create;
FShow := TShowImpl.Create(self);
end;
begin
MyObj := TMyClass.Create;
MyObj .Show.p1;
MyObj .Free; // <<< EInvalidPointer: Invalid pointer operation
end.
|
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Fri Dec 05, 2003 11:41 pm Post subject: Re: Delegated Interface |
|
|
"Charles Geiser" <charles.geiser (AT) dodeka (DOT) ch> wrote
| Quote: | To all interface experts,
can someone of you tell me why the following code snippet
throws an "Invalid pointer operation" exception on
destroying MyOb?
|
Because you have mixed lifetime control between the manual object
method and the reference-counted interface method.
TMyClass is created, used, and destroyed as an object, but it
derives from TInterfacedObject which self-destructs as soon as
the reference count drops to zero.
The encapsulated TShowImpl object derives from TAggregatedObject
which, and this is essential, reverse-delegates the IUnknown
methods (notably Release, where the freeing occurs) back to
the containing object.
First, TMyClass is instantiated. Its reference count ends as
zero, but through some very ugly compiler magic, it is not
destroyed. TInterfacedObject caters for exactly this case because
it also happens when a newly-created object is assigned to an
interface reference - the extraction of the interface reference
and the consequent raising of the reference count only occur
after the creation cycle for the object is complete.
Second, an interface method is called through an interface
reference. This raises the reference count of the delegate,
which is reverse-delegated to the outer object. When the call
returns, the reference count goes from one to zero and the
outer object is destroyed. (Since the destructor does not set
the delegate reference field to nil, that being the correct
thing to do to finalise an interface reference, I doubt that
it is destroyed as well.)
Third, a method is called on a dangling object reference. The
error message is consistent with that.
| Quote: | type
IShow = interface(IInterface)
['{5316C9A6-D3B2-4A4D-A5E8-FEFBF39E8613}']
procedure p1;
end;
TShowImpl = class(TAggregatedObject, IInterface, IShow)
public
procedure p1;
end;
TMyClass = class(TInterfacedObject, IInterface, IShow)
private
FShow: IShow;
public
constructor Create;
property Show: IShow read FShow implements IShow;
end;
var
MyObj : TMyClass;
implementation
{$R *.dfm}
procedure TShowImpl.p1;
begin
ShowMessage('P1 called');
end;
constructor TMyClass.Create;
begin
inherited create;
FShow := TShowImpl.Create(self);
end;
begin
MyObj := TMyClass.Create;
MyObj .Show.p1;
MyObj .Free; // <<< EInvalidPointer: Invalid pointer
operation
end.
|
So what did you do wrong, and what should you do instead?
The first thing you did wrong was starting with a form unit.
Just don't do that when a form is not what you want. Start
with an ordinary unit and start typing a class declaration.
The second thing was mixing lifetime control mechanisms. The
encapsulated object uses the interface rules but defers the
reference count used to the containing object. The containing
object uses the object rules, which are basically that you're
in charge of not fouling up. You either play by the same rules
as for the encapsulated object (because it shares your reference
count implementation), or you decouple them and override _Release
to _not_ free the object when the reference count reaches zero,
because you're not using that mechanism.
There's also the relatively small matter of not releasing the
delegate property interface reference in the containing object's
destructor. I say relatively small because it's "only" a memory
leak. Don't be fooled by the deceptively TComponent-ish appearance
of TShowImpl's constructor; while in a sense, its lifespan _is_
relegated to "Self", it's not by the same mechanism as implemented
in TComponent, and does require cleanup code. The cleanup code is
releasing the reference, to allow reference counting to work.
Incidentally, this also gives you a destructor to set a breakpoint
in (you don't have one now). This can be invaluable for debugging.
This subject requires a solid understanding of what happens, some
luck, and lots of learning from your mistakes. It's intricate, but
rewarding, and very, very fragile.
Groetjes,
Maarten Wiltink
|
|
| Back to top |
|
 |
Charles Geiser Guest
|
Posted: Sat Dec 06, 2003 10:44 am Post subject: Re: Delegated Interface |
|
|
Maarten,
thank you for your elaborate answer. The Delphi online help
where I looked first, was not much enlightening the subject.
Now with your answer I have a whole weekend to think about.
Do you know any introductory articles on Delphi interface
techniques?
Thanks
Charles
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Sat Dec 06, 2003 1:36 pm Post subject: Re: Delegated Interface |
|
|
"Charles Geiser" <charles.geiser (AT) dodeka (DOT) ch> wrote
| Quote: | Maarten,
thank you for your elaborate answer. The Delphi online help
where I looked first, was not much enlightening the subject.
Now with your answer I have a whole weekend to think about.
|
A weekend is not going to be enough.
| Quote: | Do you know any introductory articles on Delphi interface
techniques?
|
Basic (very basic) coverage is in the help files (of course) and
Mastering Delphi. There are bound to be other sources, but I tend
not to look very hard after finishing the help files, and to start
experimenting. I found out most everything I know about interfaces
the hard way. And yes, it _is_ hard.
Groetjes,
Maarten Wiltink
|
|
| Back to top |
|
 |
David Reeve Guest
|
Posted: Sun Dec 07, 2003 2:52 am Post subject: Re: Delegated Interface |
|
|
"Maarten Wiltink" <maarten (AT) kittensandcats (DOT) net> wrote
| Quote: | "Charles Geiser" <charles.geiser (AT) dodeka (DOT) ch> wrote in message
news:3fd1b2f5$1_1 (AT) news (DOT) bluewin.ch...
Maarten,
thank you for your elaborate answer. The Delphi online help
where I looked first, was not much enlightening the subject.
Now with your answer I have a whole weekend to think about.
A weekend is not going to be enough.
|
You can say that again. This stuff melts my brain.
| Quote: |
Do you know any introductory articles on Delphi interface
techniques?
Basic (very basic) coverage is in the help files (of course) and
Mastering Delphi. There are bound to be other sources, but I tend
not to look very hard after finishing the help files, and to start
experimenting. I found out most everything I know about interfaces
the hard way. And yes, it _is_ hard.
|
I find Eric Harmon's 'Delphi COM Programming' useful. He explains the
hazards of mixing the interface model with the object model pretty well.
However, I'm yet to get to the stage where I think, 'Hmm.... an interface is
just what I want here'. Its more a case of being able to pick my way through
the code the COM wizards drop and then hope for the best :-)
Dave
|
|
| Back to top |
|
 |
Charles Geiser Guest
|
Posted: Sun Dec 07, 2003 8:24 am Post subject: Re: Delegated Interface |
|
|
David Reeve wrote:
| Quote: | "Maarten Wiltink" <maarten (AT) kittensandcats (DOT) net> wrote in message
news:3fd1db4b$0$209$e4fe514c (AT) news (DOT) xs4all.nl...
"Charles Geiser" <charles.geiser (AT) dodeka (DOT) ch> wrote in message
news:3fd1b2f5$1_1 (AT) news (DOT) bluewin.ch...
A weekend is not going to be enough.
You can say that again. This stuff melts my brain.
|
Not very encouraging - but quite a challenge!
| Quote: | I find Eric Harmon's 'Delphi COM Programming' useful.
|
Thanks for the hint. Just in time for a christmas gift.
Charles
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Sun Dec 07, 2003 1:59 pm Post subject: Re: Delegated Interface |
|
|
"David Reeve" <dree4456 (AT) big-pond (DOT) net.au> wrote
<interfaces in general and delegated interfaces in particular>
| Quote: | I find Eric Harmon's 'Delphi COM Programming' useful. He explains
the hazards of mixing the interface model with the object model
pretty well. However, I'm yet to get to the stage where I think,
'Hmm.... an interface is just what I want here'. Its more a case
of being able to pick my way through the code the COM wizards drop
and then hope for the best
|
COM seems to be the subject that triggers most people to start using
interfaces. Not so for me; I wanted multiple inheritance. (I'm also
the guy who wrote Delphi for four years without ever touching a
relational database with it).
Multiple inheritance was what made me think "An interface[0] is just
what I want here", as I knew about the problems (and Delphi's plain
impossibility) with doing it by means of _multiple inheritance_.
That said, I didn't exactly find it easy, either. I only got things
to work in leaps and bounds, and some of the bounds were pretty small.
I suspect that these qualities (good or bad) of not learning things
through the common route, and learning them by conceptual research
and experimentation[1] rather than by following tutorials are _very_
characteristic of me.
Groetjes,
Maarten Wiltink
[0] Or, in more than one case, eight. Told you I'm nuts.
[1] That is, reading the help to learn not so much how as why, and
trying things out.
|
|
| Back to top |
|
 |
David Reeve Guest
|
Posted: Mon Dec 08, 2003 1:28 am Post subject: Re: Delegated Interface |
|
|
"Maarten Wiltink" <maarten (AT) kittensandcats (DOT) net> wrote
| Quote: | "David Reeve" <dree4456 (AT) big-pond (DOT) net.au> wrote in message
news:BBwAb.42468$aT.25145 (AT) news-server (DOT) bigpond.net.au...
[snip] |
| Quote: | Multiple inheritance was what made me think "An interface[0] is just
what I want here", as I knew about the problems (and Delphi's plain
impossibility) with doing it by means of _multiple inheritance_.
That said, I didn't exactly find it easy, either. I only got things
to work in leaps and bounds, and some of the bounds were pretty small.
I suspect that these qualities (good or bad) of not learning things
through the common route, and learning them by conceptual research
and experimentation[1] rather than by following tutorials are _very_
characteristic of me.
|
Yes I have tried to follow along with some of your postings on the
subject.... honest. I get all fired up, reopen my interface-play projects,
gain a little bit more wisdom, then retreat battered and bleeding from the
arena. :-)
Dave
|
|
| Back to top |
|
 |
Charles Geiser Guest
|
Posted: Mon Dec 08, 2003 4:12 pm Post subject: Re: Delegated Interface |
|
|
Now I have done some experiments with my code snippet and
found that memory is freed when I use
var
MyObj : IShow; // was TMyClass
and a class - not an interface for the interface property:
TMyClass = class(TInterfacedObject, IInterface, IShow)
private
FShow: TShowImpl;
public
constructor Create;
destructor Destroy; override;
property Show: TShowImpl read FShow implements IShow;
end;
and the destructor:
destructor Destroy;
begin
FShow.Free;
// FShow := nil; // works also
inherited;
end;
Freeing the object with MyObj := nil also frees the
interface object.
But when I use an interface for the interface property the
destructor is no longer called and i get a memory leak. Do
you have an explanation for that behaviour?
|
|
| Back to top |
|
 |
David Reeve Guest
|
Posted: Tue Dec 09, 2003 6:53 am Post subject: Re: Delegated Interface |
|
|
"Charles Geiser" <charles.geiser (AT) dodeka (DOT) ch> wrote
[snip]
| Quote: | But when I use an interface for the interface property the
destructor is no longer called and i get a memory leak. Do
you have an explanation for that behaviour?
|
Aaargh! now you've started me down the path of pain one more time
Check your declaration of the property carefully. I've dug out some of my
old experiments, and the code below doesn't leak as per MemProof ....
unit InterfaceImplemenationTest;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, comobj;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
IInterfaceA = interface
['{DCF11440-FE1A-11D6-95DF-0020182CAC9D}']
procedure DoA;
end;
TObjectA = class(TInterfacedObject, IInterfaceA)
protected
procedure DoA;
end;
IInterfaceB = interface
['{2CDE16A1-FE1C-11D6-95DF-0020182CAC9D}']
procedure DoB;
end;
TMultiObject = class(TInterfacedObject, IInterfaceA, IInterfaceB)
protected
FIntA: IInterfaceA;
procedure DoB;
public
constructor Create;
destructor Destroy; override;
property InfA: IInterfaceA read FIntA implements IInterfaceA;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{ TObjectA }
procedure TObjectA.DoA;
begin
ShowMessage('Hi from TObjectA.DoA');
end;
{ TMultiObject }
constructor TMultiObject.Create;
begin
inherited Create;
FIntA := TObjectA.Create;
end;
destructor TMultiObject.Destroy;
begin
FIntA := nil;
inherited Destroy;
end;
procedure TMultiObject.DoB;
begin
ShowMessage('Hi from TMultiObject.DoB');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
IA: IInterfaceA;
IB: IInterfaceB;
begin
IB := TMultiObject.Create;
IB.DoB;
IA := IB as IInterfaceA;
IA.DoA;
end;
end.
|
|
| Back to top |
|
 |
AlanGLLoyd Guest
|
Posted: Tue Dec 09, 2003 9:00 am Post subject: Re: Delegated Interface |
|
|
In article <3fd33241$0$219$e4fe514c (AT) news (DOT) xs4all.nl>, "Maarten Wiltink"
<maarten (AT) kittensandcats (DOT) net> writes:
| Quote: | I suspect that these qualities (good or bad) of not learning things
through the common route, and learning them by conceptual research
and experimentation[1] rather than by following tutorials are _very_
characteristic of me.
snip
[0] Or, in more than one case, eight. Told you I'm nuts.
[1] That is, reading the help to learn not so much how as why, and
trying things out.
|
Banging my drum again <g>. There are _very_ few books and tutorials (or help
files) which tell you why (ie the basic concepts), they nearly always describe
how things work, and one is left to infer _why_ they work - a slow process of
experimentation. Perhaps it's because the authors themselves do not have a
clear understanding of the basic concepts.
Or perhaps it's because (like Maarten) I want to know why things work, and I am
one of maybe a declining number who do (and maybe one of those who learn only
by knowing why).
Alan Lloyd
[email]alanglloyd (AT) aol (DOT) com[/email]
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Tue Dec 09, 2003 10:03 am Post subject: Re: Delegated Interface |
|
|
"David Reeve" <dree4456 (AT) big-pond (DOT) net.au> wrote
| Quote: | "Charles Geiser" <charles.geiser (AT) dodeka (DOT) ch> wrote in message
news:3fd4a2af_1 (AT) news (DOT) bluewin.ch...
But when I use an interface for the interface property the
destructor is no longer called and i get a memory leak. Do
you have an explanation for that behaviour?
Aaargh! now you've started me down the path of pain one more time
Check your declaration of the property carefully. I've dug out some
of my old experiments, and the code below doesn't leak as per
MemProof ....
|
Or for a quick and dirty approximation of MemProof, set breakpoints
in destructors and check that they're called.
Groetjes,
Maarten Wiltink
|
|
| Back to top |
|
 |
Maarten Wiltink Guest
|
Posted: Tue Dec 09, 2003 10:09 am Post subject: Re: Delegated Interface |
|
|
"Charles Geiser" <charles.geiser (AT) dodeka (DOT) ch> wrote
| Quote: | Now I have done some experiments with my code snippet and
found that memory is freed when I use
var
MyObj : IShow; // was TMyClass
|
This amounts to switching from object (manual) to interface
(reference counted auto) destruction for the outer object.
| Quote: | and a class - not an interface for the interface property:
TMyClass = class(TInterfacedObject, IInterface, IShow)
private
FShow: TShowImpl;
public
constructor Create;
destructor Destroy; override;
property Show: TShowImpl read FShow implements IShow;
end;
|
This makes an unrelated and as such dangerously confusing change
in the lifespan control of the encapsulated object. The link from
the containing object now no longer uses an interface reference,
and this influences reference counting.
| Quote: | and the destructor:
destructor Destroy;
begin
FShow.Free;
// FShow := nil; // works also
inherited;
end;
Freeing the object with MyObj := nil also frees the
interface object.
But when I use an interface for the interface property the
destructor is no longer called and i get a memory leak. Do
you have an explanation for that behaviour?
|
I assume your TShowImpl still derives from TAggregatedObject and
so still defers its reference counting to its containing object.
That gives the _containing_ object a base reference count of one:
its own link to its delegate. Paging Mr. Escher...
Groetjes,
Maarten Wiltink
|
|
| Back to top |
|
 |
David Reeve Guest
|
Posted: Tue Dec 09, 2003 11:26 am Post subject: Re: Delegated Interface |
|
|
"Maarten Wiltink" <maarten (AT) kittensandcats (DOT) net> wrote
[snip]
| Quote: | I assume your TShowImpl still derives from TAggregatedObject and
so still defers its reference counting to its containing object.
That gives the _containing_ object a base reference count of one:
its own link to its delegate. Paging Mr. Escher...
|
Whoops.... I missed that rather critical point...... if TShowImpl derives
from TAggregatedObject then there is a problem. As you say responsibility
for reference counting is passed back to the container. Eric Harmon has a
quite elaborate example of this type of hazard. Escher?.... at least he
delights as he befuddles.... this stuff just plain hurts. :-)
Dave
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|