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 

How to maintain interface based objects

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OLE Automation
View previous topic :: View next topic  
Author Message
Ian Boyd
Guest





PostPosted: Thu Aug 24, 2006 7:07 pm    Post subject: How to maintain interface based objects Reply with quote



Consider an interface:

IBottle = interface(IUnknown)
function Get_Capacity: Long; safecall;
function Get_CurrentVolume: Long; safecall;
property Capacity: Long read Get_Capacity;
property CurrentVolume: Long read Get_CurrentVolume;
end;

And the object that implements it:

TBottle = class(TComObject)
private
FCapacity: Long;
FVolume: Long;
protected
procedure Sip;

{ IBottle }
function Get_Capacity: Long;
function Get_Volume: Long;
end;

function TBottle.Get_Capacity: Long;
begin
Result := FCapacity;
end;

function TBottle.Get_Volume: Long;
begin
Result := FVolume;
end;

procedure TBottle.Sip;
begin
FVolueme := FVolume -25;
if FVolume < 0 then
FVolume := 0;
end;


And someone creates a bottle by calling
var
Bottle: IBottle;
begin
Bottle := TBottle.Create;
FBottleList.Add(Bottle);
end;


How do i update FCapacity and FVolume of the bottle?
You cannot override the constructor of a TComObject, you have to put any
initialization code in Initialize, but you still aren't allowed to pass
parameters to Initialize.

i would ideally like to use
var
Bottle: TBottle;
begin
Bottle := TBottle.Create;
Bottle.FCapacity := 325; //mL
Bottle.FVolume := 127; //mL
FBottleList.Add(Bottle as IBottle);
end;

and later i can update the values by using:

var
intf: IBottle;
Bottle: TBottle;
begin
intf := FBottleList.Items[3];
Bottle := TBottle(intf);

Bottle.Volume := 315; //mL
Bottle.Sip;
end;

But this is, aside from being ill-advised and wrong, can lead to objects
being freed when you don't expect.


i could do something horribly disgusting and painful like

ISecretBottle = interface(IBottle)
procedure Set_Volume;
procedure Set_Capacity;
procedure Sip;
end;


TBottle = class(TObject, IBottle, ISecretBottleInterface)
private
{ ISecretBottle }
procedure Set_Volume;
procedure Set_Capacity;
procedure Sip;
protected
{ IBottle }
function Get_Capacity: Long;
function Get_Volume: Long;
end;


But that's really impractical.

So how am i supposed to maintain the private values in an object that
exposes those values through a read-only interface, and i am not allowed to
talk to the VCL object anymore?
Back to top
Ian Boyd
Guest





PostPosted: Thu Aug 24, 2006 7:16 pm    Post subject: Re: How to maintain interface based objects Reply with quote



Quote:
And the object that implements it:

TBottle = class(TComObject)
private
FCapacity: Long;
FVolume: Long;
protected
procedure Sip;

{ IBottle }
function Get_Capacity: Long;
function Get_Volume: Long;
end;

Another reason why i can't just create another private interface to maintain
the object state. There can be dozens of data members and methods, most of
which are not automatable types.
Back to top
Chris Cheney
Guest





PostPosted: Fri Aug 25, 2006 12:03 am    Post subject: Re: How to maintain interface based objects Reply with quote



"Ian Boyd" <ian.borlandnews010 (AT) avatopia (DOT) com> wrote in
news:44edb4d6$1 (AT) newsgroups (DOT) borland.com:

Quote:

And the object that implements it:

TBottle = class(TComObject)
private
FCapacity: Long;
FVolume: Long;
protected
procedure Sip;

{ IBottle }
function Get_Capacity: Long;
function Get_Volume: Long;
end;

Another reason why i can't just create another private interface to
maintain the object state. There can be dozens of data members and
methods, most of which are not automatable types.

An interface (e.g. IBottle) provides an, er, interface that is independent
of the implementation.

You have provided one particular implementation of that interface by your
TBottle object. It seems as though you wish to do implementation-dependent
things whilst maintaining the IBottle interface. That's fine - you can do
those implementation-dependent things using TBottle - just because IBottle
exists doesn't mean that you have to forget about TBottle (though users of
IBottle won't necessarily know anything about TBottle). As you have already
realised, you might also provide another interface to do those
implementation-dependent things.

You, as the programmer of the object that is providing the IBottle
interface, have to make the decisions on how you are going to implement the
things that you want to do.

BTW re "which are not automatable types", this may be stating the obvious:
there is a difference between COM and Automation.

--
For e-mail address, remove the XXs
Back to top
John Carlyle-Clarke
Guest





PostPosted: Fri Aug 25, 2006 12:19 am    Post subject: Re: How to maintain interface based objects Reply with quote

Chris Cheney <XXChris.CheneyXX (AT) tesco (DOT) net> wrote in
news:Xns9829CC186A637ChrisCheneytesconet (AT) 207 (DOT) 105.83.66:

Quote:
"Ian Boyd" <ian.borlandnews010 (AT) avatopia (DOT) com> wrote in
news:44edb4d6$1 (AT) newsgroups (DOT) borland.com:


Another reason why i can't just create another private interface
to maintain the object state. There can be dozens of data members
and methods, most of which are not automatable types.


BTW re "which are not automatable types", this may be stating the
obvious: there is a difference between COM and Automation.


Indeed, and an automatable COM object can have non-automation
compatible interfaces that are only used within the Delphi program
without any of the problems associated with mixing interface and object
references. The interface is genuinely secret because it is not
declared in the type library.

I'm assuming that you (Ian) _are_ talking about automation objects here
because you mention automation compatibility. It goes without saying
that interfaces themselves as a language feature do not depend on COM
_or_ automation.
Back to top
Ian Boyd
Guest





PostPosted: Fri Aug 25, 2006 12:39 am    Post subject: Re: How to maintain interface based objects Reply with quote

Quote:
just because IBottle exists doesn't mean that you have to forget about
TBottle.

The problem is that object variables do not use reference counts.

var
Bottle: TBottle;
intf: IBottle;
begin
Bottle := TBottle.Create;

intf := Bottle as IBottle; //Delphi increments reference count by one
CallSomeFunctionThatUsesIBottle(intf); //Delph increments count and then
decrements count

intf := nil; //Delphi decrements count by one - object destroyed

Bottle.FVolume := 327; //Trying to access freed object. Acess violation.
end;

So, in response to your statement "just because IBottle exists doesn't mean
you have to forget about TBottle":
You do have to forget about TBottle; else you will be losing objects.


Quote:
You, as the programmer of the object that is providing the IBottle
interface, have to make the decisions on how you are going to implement
the
things that you want to do.

This is fine, i'm okay with only the things available through the interface
being available through the interface - that's the only interface i want the
"outside" to have to my object. Problem is trying to talk to an object
through an object reference and though an interface reference.


Quote:
BTW re "which are not automatable types", this may be stating the obvious:
there is a difference between COM and Automation.

Since i'm writing a service, my service is unable to load my COM dll's
"in-process" due to then being in different apartments. So the classes in my
dll, and the interfaces i give to the objects in the dll need to be
marshalled by COM - hence the automation. It originally was simply COM, but
that it's being redone as a service, it needs type libraries and "automation
compatible types"
Back to top
Ian Boyd
Guest





PostPosted: Fri Aug 25, 2006 12:44 am    Post subject: Re: How to maintain interface based objects Reply with quote

Quote:
I'm assuming that you (Ian) _are_ talking about automation objects here
because you mention automation compatibility. It goes without saying
that interfaces themselves as a language feature do not depend on COM
_or_ automation.

Yes. However in this case, my interfaces are available from classes exported
from a Dll. And the standard way of creating objects that are implemented
inside a DLL is to use COM. What's more, the person calling COM's
LoadLibrary+GetProcAddress+DllGetClassObject are in an apartment
incompatible with the interfaces exported by the classes in the dll; and so
COM is now trying to marshall the interface across apartment boundaries;
requiring that i make a type library and use automatically marshallable data
types.
Back to top
Ian Boyd
Guest





PostPosted: Fri Aug 25, 2006 12:52 am    Post subject: Re: How to maintain interface based objects Reply with quote

Quote:
var
Bottle: TBottle;
intf: IBottle;
begin
Bottle := TBottle.Create;

intf := Bottle as IBottle; //Delphi increments reference count by one
CallSomeFunctionThatUsesIBottle(intf); //Delph increments and then
decrements ref count
intf := nil; //Delphi decrements count by one - object destroyed

Bottle.FVolume := 327; //Trying to access freed object. Acess
violation.
end;


i guess it would have been clearer if i made my original post with these 5
lines of code. How do YOU get around this problem?

If i have a gigantic object, and i want to simply pass an interface to a
very limited view of that object: how do i maintain the object as a TObject,
and pass around interfaces on that object, without running into reference
counting pitfalls?

i could try to override ObjAddRef/ObjRelease and prevent reference counting.
i'm just worried that there might be a situation i haven't thought of where
that would cause problems. i suppose if someone could tell me straight out
that:


"As long as you can guarantee that you will maintain the object lifetime
through your object reference, you will have no need for reference counted
object lifetime, and not only can you, but you MUST disable the internal
object reference counting lifetime mechanism. You just have to guarantee
that all reference counts really are gone before you .Free the object.

Alternativly, you could maintain a TObject reference, and simultaneously
maintain an IUnknown reference. Then you're free to reference the object as
much as you want using TObject, and then when you want to get destroy the
object, nil the object and the interface references. The reference count
finally decrementing to zero will destroy the object for you."


That entire paragraph seems reasonable, especially part#2. What does
everyone/anyone else think?
Back to top
Patrick Kursawe
Guest





PostPosted: Fri Aug 25, 2006 8:12 am    Post subject: Re: How to maintain interface based objects Reply with quote

Ian Boyd wrote:
Quote:
Consider an interface:

IBottle = interface(IUnknown)
function Get_Capacity: Long; safecall;
function Get_CurrentVolume: Long; safecall;
property Capacity: Long read Get_Capacity;
property CurrentVolume: Long read Get_CurrentVolume;
end;

And the object that implements it:

TBottle = class(TComObject)
[...]
How do i update FCapacity and FVolume of the bottle?
You cannot override the constructor of a TComObject, you have to put any
initialization code in Initialize, but you still aren't allowed to pass
parameters to Initialize.
[...]


If you only need to set parameters on initialization and don't directly
create instances of this object from COM clients but have methods of other
objects which return IBottle there's a very easy way out:

Don't inherit from TComObject but from TAutoIntfObject.

In other cases, just introduce an interface that inherits from IBottle but
which is not in the type library.

IBottleExt = interface(IBottle)
[...]

and put everything there you need from within your program.

HTH,

Patrick
Back to top
Ian Boyd
Guest





PostPosted: Fri Aug 25, 2006 6:15 pm    Post subject: Re: How to maintain interface based objects Reply with quote

Quote:
If you only need to set parameters on initialization and don't directly
create instances of this object from COM clients but have methods of other
objects which return IBottle there's a very easy way out:

Don't inherit from TComObject but from TAutoIntfObject.

What does TAutoIntfObject get me? It's still reference counted.


Quote:
In other cases, just introduce an interface that inherits from IBottle but
which is not in the type library.

IBottleExt = interface(IBottle)
[...]

and put everything there you need from within your program.

Ugh. That's not the answer i wanted to hear. i wanted the easy way out.
Back to top
John Carlyle-Clarke
Guest





PostPosted: Fri Aug 25, 2006 8:27 pm    Post subject: Re: How to maintain interface based objects Reply with quote

"Ian Boyd" <ian.borlandnews010 (AT) avatopia (DOT) com> wrote in
news:44ee0197$1 (AT) newsgroups (DOT) borland.com:

Quote:
I'm assuming that you (Ian) _are_ talking about automation
objects here because you mention automation compatibility. It
goes without saying that interfaces themselves as a language
feature do not depend on COM _or_ automation.

Yes. However in this case, my interfaces are available from
classes exported from a Dll. And the standard way of creating
objects that are implemented inside a DLL is to use COM. What's
more, the person calling COM's
LoadLibrary+GetProcAddress+DllGetClassObject are in an apartment
incompatible with the interfaces exported by the classes in the
dll; and so COM is now trying to marshall the interface across
apartment boundaries; requiring that i make a type library and use
automatically marshallable data types.

I guess in theory you can do custom marshalling, implementing
IMarshall, but I've never tried it :)

Do you need to access your private interface from the host EXE, or just
within the DLL?
Back to top
Ian Boyd
Guest





PostPosted: Fri Aug 25, 2006 10:40 pm    Post subject: Re: How to maintain interface based objects Reply with quote

Quote:
I guess in theory you can do custom marshalling, implementing
IMarshall, but I've never tried it Smile

Never gonna write custom marshallers. That's why if my TObject has some
interface, i want the interface to be defined in a type library with
automatable types.

Quote:
Do you need to access your private interface from the host EXE, or just
within the DLL?

The problem is that i have a full object inside my host, but i need to pass
an interface containing 3 methods to the object in a dll.

TMyJunk = class(TObject, IMyJunk)
public
Socket: TWinSock;
CurrentUserGUID: TGUID;
dc: HDC;
PreviousMouseCursor: TCursor;
CurrentUsername: string;

procedure Assign(Source: TMyObject);
procedure WriteToSocket(s: string); overload;
procedure WriteToSocket(s: WideString); overload;
procedure WriteToSocket(guid: TGUID); overload;
procedure WriteToSocket(dc: HDC); overload;
function GetCurrentDatabaseConnection: TADOConnection;
function EtcEtcYouGetTheIdea: Boolean;
functon ALotOfNonAutomatableThings: Boolean;

{ IMyJunk }
procedure Write(Value: WideString); safecall;
procedure BinaryWrite(Value: OleVariant); safecall; //variant array of
bytes
function GetCurrentUsername: WideString;
end;


i need to pass to the DLL an IMyJunk interface. The three methods in IMyJunk
is all that the interface exposes, and they are all read only things;


However, the host application needs to maintain and update some of these
values, such as "CurrentUsername"

Imagine at some early point i create a TMyJunk object, and save it:


var
junk: TMyJunk;
begin
junk := TMyJunk.Create;
FJunkList.Add(junk); //add to a TList
end;


and now later i create an object exposed from a dll:

var
c: ISomeOtherObject;
junk: TMyJunk;
begin
c := CreateComObject('BorlandVisualBlindBling.PeanutMandM') as
ISomeOtherObject;

//Give c some junk
junk := FJustList.Items[0] as TMyJunk;
c.AdviseJunk(junk as IMyJunk); //junk is freed out from beneath me due
to reference counting

//Store C around in my list of things
FSomeOtherObjects.Add(c); //add to a TInterfaceList
end;


Now, let's go back and change junk's currentUsename

var
junk: TMyJunk;
begin
junk := FJunkList.Items[0] as TMyJunk; //bad pointer
junk.CurrentUsername := 'Ian'; //junk is freed, access violation
end;


And finally, cleanup

var
c: ISomeOtherObject;
junk: TMyJunk;
begin
//remove some other object out of the list
c := FSomeOtherObjects.Items[0] as ISomeOtherObject;
FSomeOtherObjects.Delete(0);

//Make some other object let go of it's held reference to My Junk
c.LetGoOfYourReferenceToIMyJunk;

//Now free junk
junk := FJunkList.Items[0] as TMyJunk; //bad pointer
FJunkList.Delete(0);
junk.Free; //freeing already freed object, access violation
end;



Discuss.
Back to top
Patrick Kursawe
Guest





PostPosted: Mon Aug 28, 2006 6:04 pm    Post subject: Re: How to maintain interface based objects Reply with quote

Ian Boyd wrote:
Quote:
If you only need to set parameters on initialization and don't directly
create instances of this object from COM clients but have methods of other
objects which return IBottle there's a very easy way out:

Don't inherit from TComObject but from TAutoIntfObject.

What does TAutoIntfObject get me? It's still reference counted.

It gets you overridable constructors which all kinds of arguments you need.

Quote:
In other cases, just introduce an interface that inherits from IBottle but
which is not in the type library.

IBottleExt = interface(IBottle)
[...]

and put everything there you need from within your program.

Ugh. That's not the answer i wanted to hear. i wanted the easy way out.

Well, I think this _is_ an easy way out. Especially if you have something
like function GetImpl: TBottle; in your IBottleExt for the case you want to
mess around with your bare object and know what you're doing.

YMMV.

Patrick
Back to top
Ian Boyd
Guest





PostPosted: Mon Aug 28, 2006 6:24 pm    Post subject: Re: How to maintain interface based objects Reply with quote

Quote:
What does TAutoIntfObject get me? It's still reference counted.
It gets you overridable constructors which all kinds of arguments you
need.

But i can't talk to those member variables after construction.


i've created an unreference counted IUnknown implementing class.

type
TUncountedObject = class(TObject, IUnknown)
protected
{ IUnknown }
function QueryInterface(const IID: TGUID; out Obj): HResult;
stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;


{ TUncountedObject }

function TUncountedObject.QueryInterface(const IID: TGUID;
out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := S_OK
else
Result := E_NOINTERFACE;
end;

function TUncountedObject._AddRef: Integer;
begin
Result := 2;
end;

function TUncountedObject._Release: Integer;
begin
Result := 1;
end;


Now i can create

type
TBottle = class(TUncountedObject, IBottle)
protected
function Get_Capacity: Long; safecall;
function Get_CurrentVolume: Long; safecall;
public
property Capacity: Long read Get_Capacity;
property CurrentVolume: Long read Get_CurrentVolume;
end;


So now as long as IBottle is declared in a type library registered on the
system, COM will marshall it for me, and i don't have to worry about mixing
object references and interface references causeing premature
freeactulation.
Back to top
Patrick Kursawe
Guest





PostPosted: Tue Aug 29, 2006 8:12 am    Post subject: Re: How to maintain interface based objects Reply with quote

Ian Boyd wrote:
[...]
Quote:
function TUncountedObject._AddRef: Integer;
begin
Result := 2;
end;

function TUncountedObject._Release: Integer;
begin
Result := 1;
end;

Maybe it's just a cosmetic issue, but the usual thing here would be
returning -1.

[...]
Quote:
So now as long as IBottle is declared in a type library registered on the
system, COM will marshall it for me, and i don't have to worry about mixing
object references and interface references causeing premature
freeactulation.

Great. So you just have to worry about freeing your object while there's
still an interface reference to it somewhere else :-)

Bye, Patrick
Back to top
Ian Boyd
Guest





PostPosted: Thu Aug 31, 2006 12:46 am    Post subject: Re: How to maintain interface based objects Reply with quote

Quote:
function TUncountedObject._Release: Integer;
begin
Result := 1;
end;

Maybe it's just a cosmetic issue, but the usual thing here would be
returning -1.

No, release is supposed to return the current reference count. i want anyone
who calls Relese to think there are still outstanding references.


Quote:
Great. So you just have to worry about freeing your object while there's
still an interface reference to it somewhere else Smile

Probably will never happen Smile
Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OLE Automation All times are GMT
Page 1 of 1

 
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.