BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Delegated Interface
Goto page 1, 2  Next
 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> comp.lang.pascal.delphi.misc
View previous topic :: View next topic  
Author Message
Charles Geiser
Guest





PostPosted: Fri Dec 05, 2003 5:08 pm    Post subject: Delegated Interface Reply with 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
Jamie
Guest





PostPosted: Fri Dec 05, 2003 8:33 pm    Post subject: Re: Delegated Interface Reply with quote



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





PostPosted: Fri Dec 05, 2003 11:41 pm    Post subject: Re: Delegated Interface Reply with quote



"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





PostPosted: Sat Dec 06, 2003 10:44 am    Post subject: Re: Delegated Interface Reply with 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.
Do you know any introductory articles on Delphi interface
techniques?

Thanks
Charles

Back to top
Maarten Wiltink
Guest





PostPosted: Sat Dec 06, 2003 1:36 pm    Post subject: Re: Delegated Interface Reply with quote

"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





PostPosted: Sun Dec 07, 2003 2:52 am    Post subject: Re: Delegated Interface Reply with quote

"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





PostPosted: Sun Dec 07, 2003 8:24 am    Post subject: Re: Delegated Interface Reply with quote



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





PostPosted: Sun Dec 07, 2003 1:59 pm    Post subject: Re: Delegated Interface Reply with quote

"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 Smile

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





PostPosted: Mon Dec 08, 2003 1:28 am    Post subject: Re: Delegated Interface Reply with quote


"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





PostPosted: Mon Dec 08, 2003 4:12 pm    Post subject: Re: Delegated Interface Reply with 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

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





PostPosted: Tue Dec 09, 2003 6:53 am    Post subject: Re: Delegated Interface Reply with quote

"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 Smile
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





PostPosted: Tue Dec 09, 2003 9:00 am    Post subject: Re: Delegated Interface Reply with quote

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





PostPosted: Tue Dec 09, 2003 10:03 am    Post subject: Re: Delegated Interface Reply with quote

"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 Smile
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





PostPosted: Tue Dec 09, 2003 10:09 am    Post subject: Re: Delegated Interface Reply with quote

"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





PostPosted: Tue Dec 09, 2003 11:26 am    Post subject: Re: Delegated Interface Reply with quote

"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
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> comp.lang.pascal.delphi.misc All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.