Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

COM and VO crashes on run-time-class information

29 views
Skip to first unread message

FDW

unread,
May 25, 2002, 4:21:32 AM5/25/02
to
All,

At the moment I am writing lots of COM-stuff, most interfaces that I use are
casted to VO object from a COM interface pointer. E.g.

LOCAL oI AS AbstractIUnknown
...
...:QueryInterface(...,@oI)
// Use oI
oI:Release()

When using the run-time-class-information function on a object that is
created from a cast like that will ( mostly after some time ) crash VO. To
prevent these crashes I have written the next routine,

FUNCTION _TS_IsInstanceOfUsual(uItem,symClass)
LOCAL lIsInstanceOf AS LOGIC
LOCAL oItem AS OBJECT
IF IsObject(uItem)
oItem:=uItem
DO CASE
CASE oItem==NULL_OBJECT
lIsInstanceOf:=FALSE
CASE IsDynPtr(PTR(_CAST,oItem))
lIsInstanceOf:=IsInstanceOf(oItem,symClass)
CASE IsOldSpaceObject(oItem)
lIsInstanceOf:=IsInstanceOf(oItem,symClass)
OTHERWISE
// This can be wrong, If the object is 'DynSpace' and 'IsAxiting()' then
'AIUnknown' is reported.
lIsInstanceOf:=symClass==#AIUNKNOWN
END
oItem:=NULL_OBJECT
ELSE
lIsInstanceOf:=FALSE
END
RETURN lIsInstanceOf

The problem with the routine is, When the GC is actively calling Axit()
methods, then the object is moved to other memory then where it ( the
object ) normaly resides. On normaly allocated objects the function
IsDynPtr(PTR(_CAST,oItem)) returns TRUE exept when the GC is executing
Axit()'s then it returns FALSE.

The result is that when the GC is running Axit()'s the routine will never
call IsInstanceOf(oItem,symClass) and return wrong answers for objects that
could be queried for run-time-class-information.

The questions are.

1: Can I test if axit is being executed for a givven object, E.g. is the
object moved to the axit-memory-location ( by the GC ).

2: Is there a method to test if the pI interface is a COM interface
pointer.

Frans de Wit
www.tsobjects.nl

Rod da Silva

unread,
May 25, 2002, 2:59:46 PM5/25/02
to
Frans

> The questions are.
>
> 1: Can I test if axit is being executed for a givven object, E.g. is
the
> object moved to the axit-memory-location ( by the GC ).
>
> 2: Is there a method to test if the pI interface is a COM interface
> pointer.
>

1) Try the InCollect() function.

2) Not that I am aware. In C++ you could attempt to call QueryInterface()
on it for IUnknown which would of course return an HRESULT of 0 for all COM
interfaces, however, you would have to wrap such a call in a try...catch
construct. However, this won't work in VO since BEGIN
SEQUENCE...RECOVER...END blocks don't handle GPFs which is what would occur
if you attempted to call QueryInterface() on a pointer that wasn't a legal
COM interface.

What exactly are you trying to do (its not clear to me from your sample
code).

Rod


FDW

unread,
May 25, 2002, 3:32:07 PM5/25/02
to
Rod,

When the GC is executing the Axit() methods, InCollect() will return true,
the thing is I am running a class library that needs then to destroy/release
object. To be able to do this ( and for debugging purposes ) I need to be
able to tell the difference between a VO-object and a Interface-object.

While the app is not InCollect() I can do this by examining OldSpaceObject()
and/or IsDynPointer(), when the app is in collect that will not work.
IsOldSpaceObject() and IsDynPtr() will both return FALSE on a VO-allocated
object. Hence I can not tell the difference between the two types of
objects.

What I would need is a function IsObjectInAxit(oObject) that would tell me
if the object is moved to axit-memory by the GC, it it is then it will be a
VO-allocated object, otherwise I could make the assumption that it is an COM
interface casted to an object.

The other way around I would need some API function that can tell me if the
pointer is a COM interface and thus I could make the assumption ( if the
result from this function is FALSE ) that it is a VO-allocate object.

The information is needed to be able to determine if I can ask the object
for run-time-object information e.g. if I can call IsInstanceOf() or
IsMethod() etc. If I do this on a COM interface ( that has been casted to a
object ) then VO will ultimately crash. To prevent this type of crashes
information on the origin ( COM or VO allocated ) is needed.

Now I use
IF IsDynPtr(PTR(_CAST,oObject))
then it must be VO allocated
IF IsOldSpaceObject(oObject)
then it must be VO allocated
OTHERWISE
assume it is a COM inteface

The last assumption is wrong if VO is InCollect(). As the InCollect() flag
is global, then I can not see the difference between VO and COM objects
while VO is running InCollect()

Frans


"Rod da Silva" <RodDa...@SoftwarePerspectives.com> wrote in message
news:aconaf$l2d$1...@nntp-m01.news.aol.com...

Rod da Silva

unread,
May 26, 2002, 10:08:05 AM5/26/02
to
Frans

If you want to find out if a PTR is a VO object (whether in DynSpace or not)
you can use the information I have been giving in the last several SDT
Undocumented VO articles to get a pointer to the class structure (_VOCLASS)
and pull out the class symbol name...it will either be legitamite or
garbage. You can then call IsClass() using that symbol to determine whether
it is in fact an object of a VO Class. IOWs, if you do in fact have a PTR
that is a VO object, the object points to its class structure definition
where the name of the class is stored. If it is not a VO object it will
point off to garbage... Hope that's clear.

As I said I know of no way to ask a void PTR if it is a COM interface other
then trying to QueryInterface() it and trapping for possible errors as I
explained in my last message.

FWIW, all this sounds a bit unnecessary to me. You don't have to do any of
this if you use VOCOM. IOWs VOCOM interface objects are compatible with the
GC so that objects that represent your COM interfaces can move about - they
don't have to be locked down the way they do using native VO objects - and
there is no loss in speed. It is one of the major advantages of VOCOM over
the native VO approaches that have been tried. It also helps explain why
VOCOM is so much more stable then VO's native OLE implementation.

HTHs,

Rod


FDW

unread,
May 26, 2002, 11:31:48 AM5/26/02
to
Rod,

> If you want to find out if a PTR is a VO object (whether in DynSpace or
not)
> you can use the information I have been giving in the last several SDT
> Undocumented VO articles to get a pointer to the class structure
(_VOCLASS)
> and pull out the class symbol name...it will either be legitamite or
> garbage. You can then call IsClass() using that symbol to determine
whether
> it is in fact an object of a VO Class. IOWs, if you do in fact have a PTR
> that is a VO object, the object points to its class structure definition
> where the name of the class is stored. If it is not a VO object it will
> point off to garbage... Hope that's clear.

Yes I can do that, and it will solve my problem(s), Still it is kind of
silly that VO has no function for checking this ( it has
IsOldSpaceObject() ) one should expect something like IsAxitSpaceObject().

> As I said I know of no way to ask a void PTR if it is a COM interface
other
> then trying to QueryInterface() it and trapping for possible errors as I
> explained in my last message.

There was a COM API function called 'IsValidInterface()' but it only works
for the 16bit API. I think one could go for the 'Proxy' and/or 'Marshall'
interfaces and query those for the information wanted. I do not have enough
knowledge about those to know how to do this.

> FWIW, all this sounds a bit unnecessary to me. You don't have to do any
of
> this if you use VOCOM. IOWs VOCOM interface objects are compatible with
the
> GC so that objects that represent your COM interfaces can move about -
they
> don't have to be locked down the way they do using native VO objects - and
> there is no loss in speed. It is one of the major advantages of VOCOM
over
> the native VO approaches that have been tried. It also helps explain why
> VOCOM is so much more stable then VO's native OLE implementation.

It is necessary due to the fact that I am not using your library ;-). As an
exercise in using COM en building COM applications I ( about one year ago )
read 'Inside COM' and started to implement al that is in the book in a small
VO class library. That library is now the basis of several product that we
have created. The product is really stable and gives a lot of 'extra' debug
insights, the library will go public in a few months and I am trying to plug
all the possible ( and some of the impossible holes ). The library is
especially capable of handling events ( of all [ COM ] sorts ).

Frans

FDW

unread,
May 26, 2002, 5:22:15 PM5/26/02
to
Rod,

I figure this is the minimum ;-) amount of code to examine if a object is a
VO object.

FUNCTION IsVOObject(O AS OBJECT) AS LOGIC PASCAL
// O -> VTable -> F1
// -> ...
// -> FN
// -> OInfo ...
// NameSym
// AllocSizeForO
// ...
LOCAL P AS PTR PTR
P:=PTR(_CAST,O)
P+=1
P:=PTR(P) // Not safe, Could generate a bounds error
P+=1
P:=PTR(P) // Not safe, Could generate a bounds error
RETURN IsClass(SYMBOL(_CAST,P))

Can you confirm this ?

Frans


"Rod da Silva" <RodDa...@SoftwarePerspectives.com> wrote in message

news:acqqjg$4dk$1...@nntp-m01.news.aol.com...

malcolm.gray

unread,
May 26, 2002, 5:15:02 PM5/26/02
to

"FDW" <fdewit_...@planet.nl> wrote in message
news:acqv51$cio$1...@reader05.wxs.nl...

> Yes I can do that, and it will solve my problem(s), Still it is kind of
> silly that VO has no function for checking this ( it has
> IsOldSpaceObject() ) one should expect something like IsAxitSpaceObject().

Have we any evidence of what IsOldSpaceObject actually checks?
is it not possible that it just checks the pointer is in the
range of the old space heap, as opposed to the dynamic heap
or other.

Rod da Silva

unread,
May 26, 2002, 11:30:47 PM5/26/02
to
Frans

This is my version .

Rod

STRUCTURE _VOOBJECT ALIGN 1
MEMBER pVTable AS PTR
MEMBER pClassInfo AS _VOCLASS
MEMBER b1stByteOfObjectState AS BYTE


FUNCTION IsAVOObject( oObj AS OBJECT ) AS LOGIC PASCAL
LOCAL pVOObject AS _VOOBJECT
LOCAL symClassName AS SYMBOL
LOCAL lResult := FALSE AS LOGIC

RegisterKID( @pVOObject, 1, FALSE )

// Cast object to a _VOOBJECT pointer
pVOObject := PTR(_CAST, oObj )

IF pVOObject != NULL_PTR .AND. pVOObject.pClassInfo != NULL_PTR
// Retrieve the class name
symClassName := pVOObject.pClassInfo.symClassName
IF symClassName != NULL_SYMBOL
lResult := IsClass( symClassName )
ENDIF
ENDIF

UnRegisterKID( @pVOObject )

RETURN lResult

Rod da Silva

unread,
May 26, 2002, 11:43:10 PM5/26/02
to
Frans

<<There was a COM API function called 'IsValidInterface()' but it only works
for the 16bit API. I think one could go for the 'Proxy' and/or 'Marshall'
interfaces and query those for the information wanted. I do not have enough
knowledge about those to know how to do this.
>>

Wouldn't work for in-proc (DLL) COM components which have no proxy. I
don't believe in-proc servers existing in 16-bit Windows which explains why
the API disappeared in Win32.

<<> It is necessary due to the fact that I am not using your library ;-). As
an
> exercise in using COM en building COM applications I ( about one year
ago )
> read 'Inside COM' and started to implement al that is in the book in a
small
> VO class library. That library is now the basis of several product that we
> have created. The product is really stable and gives a lot of 'extra'
debug
> insights, the library will go public in a few months and I am trying to
plug
> all the possible ( and some of the impossible holes ). The library is
> especially capable of handling events ( of all [ COM ] sorts ).
>>

That sounds great. I have really seen an increased interest in COM
programming from the VO world in the last year or so. General event support
has been of particular interest to a number of people who have contacted me
for some help. If you have a general purpose library for supporting events
I think it would be of real value to the VO community. Supporting the
"sink" and "source" COM interfaces for events isn't all that tough but it
does take some skill with COM. If you have it wrapped up nicely so that its
painless to others then I think a lot of people will be greatful.

As I am sure you have found out, COM programming isn't all that tough when
you understand the few simple rules that govern it. Certainly calling
_existing_ implementations of COM interfaces is almost straight forward and
you can do this using nataive VO objects as many have already seen.
Creating servers is a little more complicated since you have to properly
manage interface (and therefore object) lifetimes. This gets really tricky
when you have call backs across process boundaries to deal with. But once
you get it all figured out its pretty neat.

Good luck with your project.

Rod

Rod da Silva

unread,
May 26, 2002, 11:50:40 PM5/26/02
to
Frans

Two questions:

> What I would need is a function IsObjectInAxit(oObject) that would tell me
> if the object is moved to axit-memory by the GC, it it is then it will be
a
> VO-allocated object, otherwise I could make the assumption that it is an
COM
> interface casted to an object.

1) What do you mean by "moved to axit-memory by the GC" ? The GC doesn't
move objects to a special place before collecting them.

> The information is needed to be able to determine if I can ask the object
> for run-time-object information e.g. if I can call IsInstanceOf() or
> IsMethod() etc. If I do this on a COM interface ( that has been casted to
a
> object ) then VO will ultimately crash. To prevent this type of crashes
> information on the origin ( COM or VO allocated ) is needed.
>
> Now I use
> IF IsDynPtr(PTR(_CAST,oObject))
> then it must be VO allocated
> IF IsOldSpaceObject(oObject)
> then it must be VO allocated
> OTHERWISE
> assume it is a COM inteface
>
> The last assumption is wrong if VO is InCollect(). As the InCollect() flag
> is global, then I can not see the difference between VO and COM objects
> while VO is running InCollect()

2) What happens if the VO object implements a COM interface? For example I
do this all the time within my VOCOM library. IUnknown_ is both a VO object
and a COM interface. Won't this screw up your test above? I would hate to
think my stuff wasn't going to be comatible with yours...

Rod

FDW

unread,
May 27, 2002, 3:31:34 AM5/27/02
to
Rod,

> Creating servers is a little more complicated since you have to properly
> manage interface (and therefore object) lifetimes. This gets really
tricky
> when you have call backs across process boundaries to deal with. But once
> you get it all figured out its pretty neat.

The library also support creating COM servers, also with multiple interfaces
contained in one class, the next code demonstrates the very little amount of
work that has to be done to create a interface with 2 interfaces IX and IY.

Code as simple as in the example can be used for creating 'SmartTags' and
other COM classes that must support multiple interfaces.

Frans

////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////

TEXTBLOCK (c) T&S ObjectTS, By:Frans de Wit, D.d:27-Feb-2002

STATIC DEFINE TS_TRACEDEBUG :=TS_TRACEDEBUG_TRUE
STATIC DEFINE TS_TRACELOGWARNINGS :=TS_TRACELOGWARNINGS_TRUE

GLOBAL goServerInfo AS TS_ServerInfo

FUNCTION DllCanUnloadNow() AS LONG PASCAL
LOCAL loRetVal AS LONG
IF goServerInfo<>NULL_OBJECT
loRetVal:=goServerInfo:CanUnloadNow()
IF loRetVal==S_OK
goServerInfo:Destroy()
goServerInfo:=NULL_OBJECT
END
ELSE
TSTrace Warning NOSELF "goServerInfo==NULL_OBJECT"
loRetVal:=S_OK
END
RETURN loRetVal

FUNCTION DllRegisterServer() AS LONG PASCAL
TSTrace ToDo NOSELF "DllRegisterServer(), FUNCTION not implemented"
RETURN S_OK

FUNCTION DllUnregisterServer() AS LONG PASCAL
TSTrace ToDo NOSELF "DllUnregisterServer(), FUNCTION not implemented"
RETURN S_OK

FUNCTION DllGetClassObject(pStruCLSID AS _WinGUID ;
,pStruIID AS _WinGUID ;
,poAIUnknown AS TS_AbstractIUnknown PTR) AS LONG PASCAL
LOCAL hResult AS LONG
hResult:=S_OK
IF Empty(goServerInfo)
goServerInfo:=TS_ServerInfo{#TestApp,COINIT_DEFAULT}
IF goServerInfo:IsValidObject
TS_LoadTypeLib(FALSE ;
,My_GUID_TypeLib_DLL() ;
,"C:\Work\TS_NoOLECOMComponent\7 My COM Client DLL.tlb";
,@hResult )
ELSE
hResult:=E_FAIL
END
END
DO CASE
CASE TS_HFailed(hResult,TRUE)
hResult:=CLASS_E_CLASSNOTAVAILABLE
CASE MemComp(pStruCLSID,My_GUID_CLSID_DLL(),_SizeOf(_WinGUID))==0
hResult:=TS_GetClassObject(pStruCLSID,pStruIID,poAIUnknown,#MyIComponent)
CASE MemComp(pStruCLSID,My_GUID_IX(),_SizeOf(_WinGUID))==0
hResult:=TS_GetClassObject(pStruCLSID,pStruIID,poAIUnknown,#MyIX)
CASE MemComp(pStruCLSID,My_GUID_IY(),_SizeOf(_WinGUID))==0
hResult:=TS_GetClassObject(pStruCLSID,pStruIID,poAIUnknown,#MyIY)
OTHERWISE
hResult:=CLASS_E_CLASSNOTAVAILABLE
END
RETURN hResult

//====
//
// Next is the COM CLS object
//
//====

CLASS MyIComponent INHERIT TS_IDispatch
HIDDEN _MyIX AS MyIX
HIDDEN _MyIY AS MyIY
//
DECLARE METHOD Hello

METHOD Destroy() CLASS MyIComponent
IF SELF:_MyIX<>NULL_OBJECT
SELF:_MyIX:Release()
SELF:_MyIX:=NULL_OBJECT
END
IF SELF:_MyIY<>NULL_OBJECT
SELF:_MyIY:Release()
SELF:_MyIY:=NULL_OBJECT
END
SUPER:Destroy()
RETURN NIL

METHOD QueryInterface(pstruIID AS _WinGUID,poAIUnknown AS
TS_AbstractIUnknown PTR) AS LONG PASCAL CLASS MyIComponent
LOCAL hResult AS LONG
LOCAL oMyIX AS MyIX
LOCAL oMyIY AS MyIY
IF TS_ValidQueryInterfaceRequest(SELF,pStruIID,poAIUnknown,@hResult)
DO CASE
CASE MemComp(pStruIID,My_GUID_CLSID_DLL(),_SizeOf(_WinGUID))==0
SELF:AddRef()
OBJECT(PTR(_CAST,poAIUnknown)):=SELF
CASE MemComp(pStruIID,My_GUID_IX(),_SizeOf(_WinGUID))==0
IF SELF:_MyIX==NULL_OBJECT

hResult:=TS_CreateInstanceIUnknown(#MyIX,SELF,NULL_SYMBOL,NULL_PTR,@oMyIX)
IF TS_HSucceeded(hResult)
SELF:_MyIX:=oMyIX
SELF:_MyIX:AddRef()
OBJECT(PTR(_CAST,poAIUnknown)):=SELF:_MyIX
ELSE
TSTrace Error "oMyIX==NULL_OBJECT"
OBJECT(PTR(_CAST,poAIUnknown)):=NULL_OBJECT
END
ELSE
SELF:_MyIX:AddRef()
OBJECT(PTR(_CAST,poAIUnknown)):=SELF:_MyIX
END
CASE MemComp(pStruIID,My_GUID_IY(),_SizeOf(_WinGUID))==0
IF SELF:_MyIY==NULL_OBJECT

hResult:=TS_CreateInstanceIUnknown(#MyIY,SELF,NULL_SYMBOL,NULL_PTR,@oMyIY)
IF TS_HSucceeded(hResult)
SELF:_MyIY:=oMyIY
SELF:_MyIY:AddRef()
OBJECT(PTR(_CAST,poAIUnknown)):=SELF:_MyIY
ELSE
TSTrace Error "oMyIY==NULL_OBJECT"
OBJECT(PTR(_CAST,poAIUnknown)):=NULL_OBJECT
END
ELSE
SELF:_MyIY:AddRef()
OBJECT(PTR(_CAST,poAIUnknown)):=SELF:_MyIY
END
OTHERWISE
hResult:=SUPER:QueryInterface(pStruIID,poAIUnknown)
DO CASE
CASE hResult==E_NOINTERFACE
IF TS_ShowNotSupportedInterfaces()
TSTrace Warning "Interface '"+TS_GUID2String(pStruIID)+"' not
supported"
END
OBJECT(PTR(_CAST,poAIUnknown)):=NULL_OBJECT
CASE TS_HFailed(hResult,TRUE)
TSTrace Warning "SUPER:QueryInterface(pStruIID,poAIUnknown)"
OBJECT(PTR(_CAST,poAIUnknown)):=NULL_OBJECT
END
END
END
RETURN hResult

METHOD Hello(pIn AS PTR,ppcOut AS PTR PTR /* BSTR */) AS LONG PASCAL CLASS
MyIComponent
LOCAL cString AS STRING
TSTrace Enter
IF SELF:IsValidObject
IF pIn<>NULL_PTR
cString:=TS_WordString2ByteString(pIn,TRUE)
ELSE
cString:="<<<ERROR>>>"
END
TSTrace ShwEx ">>>>>> MyIComponent:Hello("+AsString(SELF:NameSym)+")
"+cString+"("+AsHexString(pIn)+","+AsHexString(ppcOut)+") <<<<<<"
IF ppcOut<>NULL_PTR
PTR(ppcOut):=TS_ByteString2WordString("And HELLO to you to")
END
ELSE
TSTrace Warning "!SELF:IsValidObject"
END
TSTrace Leave
RETURN S_OK

METHOD UnRegisterIOwned(uoIOwned,ulOwned) CLASS MyIComponent
LOCAL oIOwned AS TS_IUnknown
LOCAL uRetVal AS USUAL
TSTrace Enter
oIOwned:=uoIOwned
DO CASE
CASE SELF:_MyIX==oIOwned
SELF:_MyIX:=NULL_OBJECT
CASE SELF:_MyIY==oIOwned
SELF:_MyIY:=NULL_OBJECT
END
uRetVal:=SUPER:UnRegisterIOwned(oIOwned,ulOwned)
TSTrace Leave
RETURN uRetVal

//====
//
// Next the two COM interfaces that are exposed by the CLS
//
//====

CLASS MyIX INHERIT TS_IDispatch
DECLARE METHOD MX

METHOD MX() AS LONG PASCAL CLASS MyIX
IF SELF:IsValidObject
TSTrace ShwEx ">>>>>>>>>>>>>>>> MX:MyIX() <<<<<<<<<<<<<<<<"
ELSE
TSTrace Warning "!SELF:IsValidObject"
END
RETURN S_OK

CLASS MyIY INHERIT TS_IDispatch
DECLARE METHOD MY

METHOD MY() AS LONG PASCAL CLASS MyIY
IF SELF:IsValidObject
TSTrace ShwEx ">>>>>>>>>>>>>>>> MY:MyIY() <<<<<<<<<<<<<<<<"
ELSE
TSTrace Warning "!SELF:IsValidObject"
END
RETURN S_OK


////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////

"Rod da Silva" <RodDa...@SoftwarePerspectives.com> wrote in message

news:acsabn$n87$1...@nntp-m01.news.aol.com...

FDW

unread,
May 27, 2002, 3:33:26 AM5/27/02
to
Malcolm,

You could be right, but this is of no concern, you have to pass a object as
the function parameter and if that object is in oldspace than surely it is a
VO object in oldspace.

Frans

"malcolm.gray" <malcol...@jobstream.co.uk> wrote in message
news:0gcI8.1593$0x3.1...@newsfep1-win.server.ntli.net...

FDW

unread,
May 27, 2002, 3:41:32 AM5/27/02
to
Rod,

It seems that your code is exactly the same as my code. It even has the two
'unsafe' pointers. The pointers are safe when you use the routine on a VO
object, but when used on a COM interface then the pointers are not safe.

pVOObject.pClassInfo // Not safe on a COM interface
pVOObject.pClassInfo.symClassName // Not safe on a COM interface

Any idea how to check for these un-safe pointers ;-) Ironically this is the
same question as the one that started the thread.

Frans

"Rod da Silva" <RodDa...@SoftwarePerspectives.com> wrote in message

news:acs9kg$m1r$1...@nntp-m01.news.aol.com...

FDW

unread,
May 27, 2002, 3:53:13 AM5/27/02
to
Rod,

> 1) What do you mean by "moved to axit-memory by the GC" ? The GC doesn't
> move objects to a special place before collecting them.

I do think so, Using IsDynPtr(PTR(_CAST,oObject)) while in collect yields
FALSE, and the function IsOldSpaceObject(oObject) will at that moment also
yield FALSE. Also I seem to remember that 'Thru' did say something about the
object being moved while collected ( Nurnberg 2 years ago [ You was
there ] ). Any way even if it was not moved then from the VO programmers
view it has moved ( IsDynPtr(PTR(_CAST,oObject)) reports FALSE ).

> 2) What happens if the VO object implements a COM interface? For example
I
> do this all the time within my VOCOM library. IUnknown_ is both a VO
object
> and a COM interface. Won't this screw up your test above? I would hate
to

> think my stuff wasn't going to be compatible with yours...

I do not think so, the main purpose of the test is to validate pointers and
there usage. In the library there are multiple classes that implement COM
interfaces ( ISink, IClassFactory etc ).

Frans

"Rod da Silva" <RodDa...@SoftwarePerspectives.com> wrote in message

news:acsapp$nue$1...@nntp-m01.news.aol.com...

FDW

unread,
May 27, 2002, 3:56:21 AM5/27/02
to
Rod,

> That sounds great. I have really seen an increased interest in COM
> programming from the VO world in the last year or so. General event
support
> has been of particular interest to a number of people who have contacted
me
> for some help. If you have a general purpose library for supporting
events
> I think it would be of real value to the VO community. Supporting the
> "sink" and "source" COM interfaces for events isn't all that tough but it
> does take some skill with COM. If you have it wrapped up nicely so that
its
> painless to others then I think a lot of people will be greatful.

I am not sure what I can say about this, but maybe 'Ed Richard' will jump in
on this point.

Frans

"Rod da Silva" <RodDa...@SoftwarePerspectives.com> wrote in message

news:acsabn$n87$1...@nntp-m01.news.aol.com...

Ed Richard

unread,
May 27, 2002, 4:11:20 AM5/27/02
to
Rod,

> That sounds great. I have really seen an increased interest in COM
> programming from the VO world in the last year or so. General event
support
> has been of particular interest to a number of people who have contacted
me
> for some help. If you have a general purpose library for supporting
events
> I think it would be of real value to the VO community. Supporting the
> "sink" and "source" COM interfaces for events isn't all that tough but it
> does take some skill with COM. If you have it wrapped up nicely so that
its
> painless to others then I think a lot of people will be greatful.

It certainly is our intention to include all of this in 2.6. The idea is to
include a COM-SDK that's basically the code Frans is talking about _and_ a
library demonstrating the capabilities. This would be VO2Outlook and/or
VO-code for creating Smarttag's. But it all depends on how stable we will be
able to get it. It looks very promising now!
Thanks for your help sofar!

Ed Richard


Malcolm Gray

unread,
May 27, 2002, 5:12:01 AM5/27/02
to
FDW <fdewit_...@planet.nl> wrote:
> Rod,
>
> It seems that your code is exactly the same as my code. It even has
> the two 'unsafe' pointers. The pointers are safe when you use the
> routine on a VO object, but when used on a COM interface then the
> pointers are not safe.
>
> pVOObject.pClassInfo // Not safe on a COM interface
> pVOObject.pClassInfo.symClassName // Not safe on a COM interface
>
> Any idea how to check for these un-safe pointers ;-) Ironically this
> is the same question as the one that started the thread.

Might MemCheckPtr help - not used it much myself.


Malcolm Gray

unread,
May 27, 2002, 5:14:58 AM5/27/02
to
FDW <fdewit_...@planet.nl> wrote:
> Rod,
>
>> 1) What do you mean by "moved to axit-memory by the GC" ? The GC
>> doesn't move objects to a special place before collecting them.
>
> I do think so, Using IsDynPtr(PTR(_CAST,oObject)) while in collect
> yields FALSE, and the function IsOldSpaceObject(oObject) will at that
> moment also yield FALSE. Also I seem to remember that 'Thru' did say
> something about the object being moved while collected ( Nurnberg 2
> years ago [ You was there ] ). Any way even if it was not moved then
> from the VO programmers view it has moved (
> IsDynPtr(PTR(_CAST,oObject)) reports FALSE ).

Whilst in collect various parts of memory are being maniulated in
strange
ways so various functions do not return the values you would extec
outside
of a GC run.

The VO GC is I think reffered to as a stop and copy. Every time it
decides
to run every object in the active GC heap that is in use is copied to
the other
GC heap (there are 2). Axits are then fired on any objects that have
them
registered that were not moved over.

Malcolm


Malcolm Gray

unread,
May 27, 2002, 6:14:28 AM5/27/02
to
FDW <fdewit_...@planet.nl> wrote:
> 1: Can I test if axit is being executed for a givven object, E.g.
> is the object moved to the axit-memory-location ( by the GC ).
The normal way of testing in an axit if the GC is active is Incollect,
this is used to check if the user is destroying the object or the GC.

Note that many objects in the GC do not have axits but are still
moved around the dynamic memory by the GC.

Malcolm


FDW

unread,
May 27, 2002, 7:09:32 AM5/27/02
to
Malcolm

Thanks, I will test that.

Frans

"Malcolm Gray" <malcol...@jobstream.co.uk> wrote in message
news:BLmI8.4$DC4.525@psinet-eu-nl...

FDW

unread,
May 27, 2002, 7:12:03 AM5/27/02
to
Malcolm

> Whilst in collect various parts of memory are being manipulated in
> strange
> ways so various functions do not return the values you would expect


> outside
> of a GC run.

This is exactly what I referred to saying 'moved to axit-memory by the GC'

Frans

"Malcolm Gray" <malcol...@jobstream.co.uk> wrote in message

news:mOmI8.5$DC4.438@psinet-eu-nl...

Malcolm Gray

unread,
May 27, 2002, 7:16:02 AM5/27/02
to
FDW <fdewit_...@planet.nl> wrote:
> Malcolm
>
>> Whilst in collect various parts of memory are being manipulated in
>> strange
>> ways so various functions do not return the values you would expect
>> outside
>> of a GC run.
>
> This is exactly what I referred to saying 'moved to axit-memory by
> the GC'
OK. axit-memory is not a phrase that means anything to me.
I suspect that objects whose axit are about to be
called are simply left alone and not moved by the last GC run
before the axit is executed.


FDW

unread,
May 27, 2002, 7:16:30 AM5/27/02
to
Malcolm

> The normal way of testing in an axit if the GC is active is Incollect,
> this is used to check if the user is destroying the object or the GC.

But this is flawed if you are trying to test if the current object is in
Axit(), the flag 'InCollect()' is global and if ( while in axit ) you start
to do your own destroy's these object will think that they are in axit.

Any way the problem(s) that this created is solved by using the
'IsVOObject()' function as described earlier in this thread.

> Note that many objects in the GC do not have axits but are still
> moved around the dynamic memory by the GC.

Yes i know ;-)

Frans

"Malcolm Gray" <malcol...@jobstream.co.uk> wrote in message

news:8GnI8.8$DC4.527@psinet-eu-nl...

FDW

unread,
May 27, 2002, 7:19:09 AM5/27/02
to
Malcolm

> OK. axit-memory is not a phrase that means anything to me.
> I suspect that objects whose axit are about to be
> called are simply left alone and not moved by the last GC run
> before the axit is executed.

That's fast.

I am aware that the phrase 'Axit memory' is invented by me, but I had to
give it some name. The object is no longer in dyn-space or old-space, thus I
invented axit-space.

Frans

"Malcolm Gray" <malcol...@jobstream.co.uk> wrote in message

news:TzoI8.10$DC4.888@psinet-eu-nl...

Malcolm Gray

unread,
May 27, 2002, 7:20:06 AM5/27/02
to
FDW <fdewit_...@planet.nl> wrote:
> Malcolm
>
>> The normal way of testing in an axit if the GC is active is
>> Incollect, this is used to check if the user is destroying the
>> object or the GC.
>
> But this is flawed if you are trying to test if the current object is
> in Axit(), the flag 'InCollect()' is global and if ( while in axit )
> you start to do your own destroy's these object will think that they
> are in axit.

Why are you directly destroying objects whose Axit the GC will call
anyway?

Malcolm


FDW

unread,
May 27, 2002, 7:25:55 AM5/27/02
to
Malcolm

> Why are you directly destroying objects whose Axit the GC will call
> anyway?

This is a COM library, the interface have dependencies that make it
necessary to destroy some object, some of these object are registered by me
in a array so they will never be destructed by VO. As you know the COM
interface objects use 'AddRef()' and 'Release()' and proper ref-counting
must be maintained.

My method of hiding the objects from the GC ( so to say ) guaranties that I
am able to do the correct refcounting.

Frans.

"Malcolm Gray" <malcol...@jobstream.co.uk> wrote in message

news:GDoI8.11$DC4.879@psinet-eu-nl...

FDW

unread,
May 27, 2002, 7:28:59 AM5/27/02
to
Malcolm

As Rod said earlier in the thread

> Creating servers is a little more complicated since you have to properly
> manage interface (and therefore object) lifetimes. This gets really
tricky
> when you have call backs across process boundaries to deal with. But once
> you get it all figured out its pretty neat.

This will force upon you that you have to do your own lifetime management.

Frans


"Malcolm Gray" <malcol...@jobstream.co.uk> wrote in message

news:GDoI8.11$DC4.879@psinet-eu-nl...

Malcolm Gray

unread,
May 27, 2002, 7:39:15 AM5/27/02
to
FDW <fdewit_...@planet.nl> wrote:
> Malcolm
>
>> Why are you directly destroying objects whose Axit the GC will call
>> anyway?
>
> This is a COM library, the interface have dependencies that make it
> necessary to destroy some object, some of these object are registered
> by me in a array so they will never be destructed by VO. As you know
> the COM interface objects use 'AddRef()' and 'Release()' and proper
> ref-counting must be maintained.
>
> My method of hiding the objects from the GC ( so to say ) guaranties
> that I am able to do the correct refcounting.
OK - what I think is confusing me is - are you saying that the objects
might sometimes be register axited and sometimes manual lifetime?

My usual preference is to have a destroy method and in the destroy
method
call unregister axit, possibly with a destroyed flag to stop me doing it
twice.

method axit....
self:Destroy()
method destroy
if !InCollect()
unregisterAxit(self)
endif
lDestroyed:=true

You may also want to explore the memory before the object in the
heap - I have seen it take interesting values during a GC run but
never actually worked out what they are...

Malcolm


FDW

unread,
May 27, 2002, 7:56:38 AM5/27/02
to
Malcolm

Yes that is what I do.

Frans

"Malcolm Gray" <malcol...@jobstream.co.uk> wrote in message

news:MVoI8.12$DC4.831@psinet-eu-nl...

Rod da Silva

unread,
May 27, 2002, 8:17:26 AM5/27/02
to
Frans

Like I said in my original post...you could do this in C++ by trapping
exception handlers but not in VO...this is why I didn't go this way with
VOCOM.

Sorry I can't help more..

Rod


Rod da Silva

unread,
May 27, 2002, 8:15:33 AM5/27/02
to
Frans

Brings back memories <g>...

Rod

Geoff Schaller

unread,
May 27, 2002, 9:19:46 AM5/27/02
to
I'm eavesdropping - the subject is very interesting.

I think your "new" phrase is quite relevant. Malcolm seems to be confirming
the behaviour and if this is correct, it would explain other behaviour we
see with objects around collection time and the importance of waiting for
the collection cycle to complete.

Geoff


"FDW" <fdewit_...@planet.nl> wrote in message

news:act4na$cts$1...@reader07.wxs.nl...

FDW

unread,
May 27, 2002, 11:16:15 AM5/27/02
to
Rod ( and Malcolm )

The suggestions from Malcolm pointed me in the correct direction, testing
validly of the pointer solved the problem. You ( Rod ) could even add this
to your method of testing. The magic was in using the 'PtrLenWrite()'
function.

Thanks for all the support given ;-)

Frans

//==== CodeStart

FUNCTION TS_IsVOObject(oObjectToTest AS OBJECT) AS LOGIC PASCAL
LOCAL dwMemoryBlockSizeSize AS DWORD
LOCAL symClassName AS SYMBOL
LOCAL lIsVOObject AS LOGIC
LOCAL ptrP AS PTR PTR
IF oObjectToTest==NULL_OBJECT
lIsVOObject:=TRUE
ELSE
// Dump the object memory, mainly to validate the testing method
// if this is not failing ( e.g. 5333's ) then testing is valid
__TS_ObjectDumper(oObjectToTest)
symClassName:=NULL_SYMBOL
DynLock()
// Get a pointer to the VO object
// If not tested the crashes on some interface pointers
ptrP:=PTR(_CAST,oObjectToTest)
IF PtrLenWrite(ptrP)>7
// Backup to the memory structure
ptrP-=2
// Get the allocated size of the memory block
dwMemoryBlockSizeSize:=DWORD(_CAST,PTR(ptrP))
// VO objects contain 8 or more bytes
IF dwMemoryBlockSizeSize>=8
// Skip to the pointer that points to the object runtime info
ptrP+=3
// Get a pointer to the runtime info
ptrP:=PTR(ptrP) // P->ObjectInfo
IF LOGIC(_CAST,ptrP)
// If not tested the crashes on some interface pointers
IF PtrLenWrite(ptrP)>11
// This test will not work !!!
// IF PtrLen(ptrP)>11
// This test will not work !!!
// IF MemCheckPtr(ptrP,12)
// Skip to the DWORD that contains the number of bytes needed
ptrP+=2
// If the bytes allocated matches the bytes needed
IF dwMemoryBlockSizeSize==DWORD(_CAST,PTR(ptrP))
// Backup to the DWORD that contains the classname symbol
ptrP-=1
// Get the classname
symClassName:=SYMBOL(_CAST,PTR(ptrP))
END
END
END
END
END
DynUnLock()
// If a classname was collected
IF symClassName<>NULL_SYMBOL
// And the class exists, then this must be a VO object
lIsVOObject:=IsClass(symClassName)
ELSE
lIsVOObject:=FALSE
END
END
RETURN lIsVOObject

FUNCTION __TS_ObjectDumper(oO AS OBJECT) AS VOID PASCAL
LOCAL pV,pC,pI AS PTR PTR
LOCAL pO AS PTR PTR
LOCAL X AS DWORD
?
DynLock()
pO:=PTR(_CAST,oO) // Object pointer
IF PtrLenWrite(pO)>7
pO-=2
? "Before[-2] 0x"+AsHexString(PTR(pO))+" MemoryBlockSize"
pO+=2
pV:=PTR(pO) // VTable
pO+=1
pC:=PTR(pO) // ClassInfo
? "Object 0x"+AsHexString(pO) // +" "+AsString(ClassName(oO))
? "VTable 0x"+AsHexString(pV)
? "ClassInfo 0x"+AsHexString(pC)
FOR X:=1 TO 3
IF PtrLenWrite(pC)>3
pI:=PTR(pC)
? "ClassInfo["+NTrim(X)+"] 0x" ;
+AsHexString(pI)+" " ;
+Iif(X==2 ;
,AsString(SYMBOL(_CAST,pI)) ;
,"" );
+Iif(X==3 ;
,"ObjectSize" ;
,"" )
ELSE
? "!(PtrLenWrite(pO)>7)"
END
pC+=1
NEXT
ELSE
? "!(PtrLenWrite(pC)>3)"
END
DynUnLock()
RETURN

//==== CodeEnd


"Rod da Silva" <RodDa...@SoftwarePerspectives.com> wrote in message

news:act8fv$iao$1...@nntp-m01.news.aol.com...

FDW

unread,
May 27, 2002, 11:18:18 AM5/27/02
to
Geof,

Just doing my 2cents ( that is Euro cents )

Frans

"Geoff Schaller" <ge...@softwareobjectives.com.au> wrote in message
news:SnqI8.184627$o66.5...@news-server.bigpond.net.au...

FDW

unread,
May 27, 2002, 11:25:58 AM5/27/02
to
Rod

BTW I am using DynLock()/DynUnLock().

Keeping the 'IsClass()' function outside the lock, I think that inside the
lock nothing can/will happen with dyn-space ?

The function is tested on a selection of outlook interfaces and eventsinks
and runs without any problem <g>

Frans

"FDW" <fdewit_...@planet.nl> wrote in message

news:actijs$l57$1...@reader07.wxs.nl...

FDW

unread,
May 27, 2002, 12:06:24 PM5/27/02
to
All,

I forgot to mention, this function can also be used to test if a given
object is being collected. Write something like this and know if a
particular object is being collected.

FUNCTION TS_IsInCollectObject(oObjectToTest AS OBJECT) AS LOGIC PASCAL
LOCAL lIsInCollectObject AS LOGIC
TSTrace Enter NOSELF
IF InCollect()
DO CASE
CASE oObjectToTest==NULL_OBJECT
lIsInCollectObject:=FALSE
CASE TS_IsVOObject(oObjectToTest)
DO CASE
CASE IsDynPtr(PTR(_CAST,oObjectToTest))
lIsInCollectObject:=FALSE
CASE IsOldSpaceObject(oObjectToTest)
lIsInCollectObject:=FALSE
OTHERWISE
lIsInCollectObject:=TRUE
END
OTHERWISE
lIsInCollectObject:=FALSE
END
ELSE
lIsInCollectObject:=FALSE
END
TSTrace Leave NOSELF
RETURN lIsInCollectObject

Frans

"FDW" <fdewit_...@planet.nl> wrote in message
news:actijs$l57$1...@reader07.wxs.nl...

Rod da Silva

unread,
May 27, 2002, 9:04:10 PM5/27/02
to
Frans

<<The suggestions from Malcolm pointed me in the correct direction, testing
validly of the pointer solved the problem. You ( Rod ) could even add this
to your method of testing. The magic was in using the 'PtrLenWrite()'
function.>>

Your function seems rather fragile to me. You are assuming that if the
allocated memory for the object matches whatever is in the "classsize"
member of the VO class structure then you are good to go. This will
work....most of the time.

I can't help but feel the entire thing is a bit of kludge.

If you go back and look at why you are having to do this, it is because you
want to use VO objects as native COM interfaces. This works great (as you
know) as long as the object doesn't move on you. IOWs as long as you keep
it locked down, or the objects are used by VO-only COM clients. Of course
this isn't a problem if you are using C++ COM objects, since once you get a
C++ implemented interface pointer it doesn't move from under you. However,
the moment you pass a VO based COM interface object implemented in the
fashion you describe to a non-VO program (i.e.; a C++ COM client) you have a
scenario where the VO component can have GC kick in and the object moves,
and the C++ client's interface pointer is never updated. This will cause a
GPF.

This is why VOCOM never uses VO objects' native VTable as the COM VTable.
VOCOM builds ands manages its own COM compliant VTables. I really believe
this is an infinitely better solution since you no longer have to worry
about when and where your VO objects move in memory. You can hand out
interface pointers to any VOCOM interface to any COM client and the
underlying VO objects are free to move around as normal.

You might want to consider this approach.

Having said all of this, if your soltuion works for your needs I suppose
that is all that matters!

Rod


FDW

unread,
May 28, 2002, 2:37:50 AM5/28/02
to
Rod,

> Your function seems rather fragile to me. You are assuming that if the
> allocated memory for the object matches whatever is in the "classsize"
> member of the VO class structure then you are good to go. This will
> work....most of the time.

This may be true, but then yours is also, my routine is an exact copy of the
one you wrote. I do not assume any thing in the routine, as you can see is
the deciding test a IsClass() function call to determine if the classname
found in the object's runtime information is existing ( as you yourself
advised to do ).

When using the function as was proposed by you I witnessed several crashes,
these could be resolved by checking the pointer. I do know that all VO's
class information is writable and thus this method works.

> I can't help but feel the entire thing is a bit of kludge.

As I said before the routine that I have written is a exact copy of your
routine with only a few lines added to prevent crashes ( and it works ).

> If you go back and look at why you are having to do this, it is because
you
> want to use VO objects as native COM interfaces. This works great (as you
> know) as long as the object doesn't move on you. IOWs as long as you keep
> it locked down, or the objects are used by VO-only COM clients. Of course
> this isn't a problem if you are using C++ COM objects, since once you get
a
> C++ implemented interface pointer it doesn't move from under you.
However,
> the moment you pass a VO based COM interface object implemented in the
> fashion you describe to a non-VO program (i.e.; a C++ COM client) you have
a
> scenario where the VO component can have GC kick in and the object moves,
> and the C++ client's interface pointer is never updated. This will cause
a
> GPF.

When allocating the objects statically non of these is a problem. The GC can
do whatever it wants to do no persistent data is ever transmitted between
COM servers and COM clients.

> This is why VOCOM never uses VO objects' native VTable as the COM VTable.
> VOCOM builds ands manages its own COM compliant VTables. I really believe
> this is an infinitely better solution since you no longer have to worry
> about when and where your VO objects move in memory. You can hand out
> interface pointers to any VOCOM interface to any COM client and the
> underlying VO objects are free to move around as normal.

I ( and the library ) have no worry about where the objects are ( they are
allocated statically ) and I can handout as many pointers as I want ( and it
works ).

Frans

"Rod da Silva" <RodDa...@SoftwarePerspectives.com> wrote in message

news:aculdi$db3$1...@nntp-m01.news.aol.com...

Rod da Silva

unread,
May 28, 2002, 7:47:02 AM5/28/02
to
Frans

<<This may be true, but then yours is also, my routine is an exact copy of
the
> one you wrote. I do not assume any thing in the routine, as you can see is
> the deciding test a IsClass() function call to determine if the classname
> found in the object's runtime information is existing ( as you yourself
> advised to do ).

Yes you have improved on the routine I sent you for sure. But VOCOM doesn't
use that routine...I just threw it together off the top of my head to try
and help you.

<<> I ( and the library ) have no worry about where the objects are ( they
are
> allocated statically ) and I can handout as many pointers as I want ( and
it
> works ).
>

Yes, this is the necessary requirement for it to work - lock down all
objects. The down side is that you have to manage memory yourself now
(something I have gotten use to VO handling for me).

However, as I said, if it works for you that's all that matters.

Rod


David Earl

unread,
Jun 3, 2002, 3:14:40 AM6/3/02
to
Fritz,

I think I would have used something more like "DynMem re-hash
buffer"...but that's comes from working with a different OS, which
worked with fixed length files. When a file had to be resized, we
"re-hashed" it.

<shrug>, my 0,02 Euro,
David.

On Mon, 27 May 2002 13:19:09 +0200, "FDW" <fdewit_...@planet.nl>
wrote:

FDW

unread,
Jun 3, 2002, 3:51:57 AM6/3/02
to
David,

Nice to see someone reading this ( old ) thread.

The name 'Axit-space' was selected ( in a way ) by me because VO already
uses names like this, there is 'Old-Space' ( Static memory, I would think
'Old' as from before there was 'Dyn-space' ). 'Dyn-space' for dynamic memory
and that is why I invented 'Axit-space'.

The term "DynMem re-hash buffer" contains 're-hash' and this implies that
something is changing ( in the object ), that is not what is happening to
the 'Collected' ( by the GC ) object. Any way maybe someone else will jump
in and have some idea's ;-)

Frans

"David Earl" <Alia...@Yahoo.Com> wrote in message
news:3cfb1669...@news.nyer.de...

David Earl

unread,
Jun 4, 2002, 3:07:04 AM6/4/02
to
>The term "DynMem re-hash buffer" contains 're-hash' and this implies that
>something is changing ( in the object )

That could be. In the example that I provided, the contents of the
files were not being changed, but the records were begin reorganized
to suit the change in their environment (a larger/smaller file space).
That is pretty much what is happening (to my mind) when the GC is
active...it is throwing out dead records, which increases the buffer
space, and re-arranging the remaining memory structures in the buffer
(and ?de-fragging?...where an object had to be crammed into
non-contigous space?)

Old thread, eh? The last message on it was from a couple of days
before my response...to me that is still active thread. I start
counting them as old when there's no activity for ~1 week.

And, why not read it. Always nice to learn more about VO's internals.
Thanks,
David.

On Mon, 3 Jun 2002 09:51:57 +0200, "FDW" <fdewit_...@planet.nl>
wrote:

0 new messages