Theos PowerBasic Museum 2017

Archive => Discussion - Legacy Software (PBWIN 9.0+/PBCC 5.0+) => Topic started by: Eriq VanBibber on April 07, 2009, 09:57:56 PM

Title: Object Lifetime and Scope
Post by: Eriq VanBibber on April 07, 2009, 09:57:56 PM
Does anyone know whether we need to call object.Release for a Direct COM object in PB9?

Documentation does not say either way only that setting an object to NOTHING releases memory and resources.

Title: Re: Object Lifetime and Scope
Post by: Edwin Knoppert on April 07, 2009, 10:11:28 PM
A variable holds the interface and will call .release() for you.
The variable will be set to empty or nothing (read: invalid pointer)
When all variables holding the same object instance are released, the data and internal allocation will be cleared.

When the variable goes out of scope or when set to nothing it will call release, never call it yourself unless you are 'misusing' the object's interface.
I do that in some cases, for example to hold the interface in a long integer.
When a long is cleared, the release() will not called of course and so i have to make sure it does.


Title: Re: Object Lifetime and Scope
Post by: Eriq VanBibber on April 07, 2009, 10:24:08 PM
Sweet!

I figured as much and have been programming it that way, but without knowing for sure.

Y, i have some cases where i have aggregated COM objects and have to manage the addref and release myself...

Thanks!
Title: Re: Object Lifetime and Scope
Post by: Edwin Knoppert on April 07, 2009, 10:53:10 PM
Oh btw, to be clear, i was talking about variant, dispatch and interface for variables but you seem to understand that :)
These 3 are supported by PB to be released (or manually) if they contain a com interface.
Title: Re: Object Lifetime and Scope
Post by: Eriq VanBibber on April 17, 2009, 07:31:34 PM
Ok, new question.  Does PB9 automatically call release on INSTANCE variables in a CLASS?

Considering the example class below...when an instance of the class "test" is destroyed or goes out of scope, what happens to the instance variable 'var1'?  Is PB calling relase on all internal object references before releasing the class itself? Or do we need to do release/NOTHING in a destructor?

CLASS test
    INSTANCE var1 AS ISomething

    INTERFACE ITest
        INHERIT IUNKNOWN

        METHOD t()
            var1 = CLASS "Something"
        END METHOD
    END INTERFACE
END CLASS
Title: Re: Object Lifetime and Scope
Post by: Edwin Knoppert on April 17, 2009, 08:09:45 PM
PB releases all stuff for you but this discussion has been on the pb forum as well.
A logical error is made really quick.
For example my main interface held a reference to a 'child' item interface and this child also to the main.
A circular reference that is and is not going to be released (chicken - egg).

Your question can be solved by yourself by using an OutputDebugString() in the release call.
This is how i develop my code to be sure it is released as anticipated.

Title: Re: Object Lifetime and Scope
Post by: José Roca on April 17, 2009, 08:41:49 PM
Quote
Considering the example class below...when an instance of the class "test" is destroyed or goes out of scope, what happens to the instance variable 'var1'?  Is PB calling relase on all internal object references before releasing the class itself?

Yes, it releases the instance variables.
Title: Re: Object Lifetime and Scope
Post by: Eriq VanBibber on April 17, 2009, 11:14:50 PM
Ok, another question then...

When working in loops, i have a variable that i instantiate inside the loop, and set it to nothing just before the next iteration.

I'm concerned that this may not be safe since when the function completes, PB may try to call release on an object that already has a ref count of zero.  Am i overly concerned?

Consider the following function:

FUNCTION myLoop(obj as ISomeObject)

  DIM x AS INTEGER

  FOR x = 0 TO obj.Count
    DIM subObj as ISubObject
    subObj = obj.item(x)
    ...
    {some work is done on subObj}
    ...
    subObj = NOTHING '// need to explicitly free the object to avoid a memory/handle leak
  NEXT

END FUNCTION

Will PB already know that subObj has a ref count of zero and not call release?
OR
Will PB call release anyway and swallow any error about the ref count being at or less than zero?

Regards,
Eriq
Title: Re: Object Lifetime and Scope
Post by: José Roca on April 17, 2009, 11:43:35 PM
Quote
Am i overly concerned?

Yes, you are :)

Quote
Will PB already know that subObj has a ref count of zero and not call release?
OR
Will PB call release anyway and swallow any error about the ref count being at or less than zero?

PB can't know if the reference count is zero, but it doesn't need it. After setting subObj = NOTHING, the pointer hold by that variable is 0, and PB is clever enough to know that calling Release with a NULL pointer will cause a GPF, therefore it does nothing.

BTW even subObj = NOTHING in the loop isn't needed, because PB will call Release before assigning a new value to it (unless the pointer is already 0). In fact, you don't ever need to set an object variable to Nothing unless you want release objects in an orderly manner.

PB's object variables are smart pointers. Only if you manipulate them in an unsupported way, like poking a value (something I do often, but I know what I'm doing), may you need to be concerned about AddRef and Release.
Title: Re: Object Lifetime and Scope
Post by: Eriq VanBibber on April 17, 2009, 11:48:53 PM
Very nice.  Thanks much.

I'm a MAPI programmer and always have to be concerned about such.

-Eriq
Title: Re: Object Lifetime and Scope
Post by: Eriq VanBibber on April 18, 2009, 01:38:27 AM
Ok, i thought i had this all figured out...

I'm getting a memory access violation when my object's lifetime expires.

I know this because the debugger runs fine until is step out of my function.
I placed brakepoints in DESTROY for my class, and it bombs after leaving the DESTROY function.

So, what do we need to know about passing objects in to other object's methods?
Does PB call addref every time?  Could there be times where passing an object reference doesn't trigger addref?

If i figure it out before you do, i'll update this thread.

-Eriq
Title: Re: Object Lifetime and Scope
Post by: José Roca on April 18, 2009, 02:17:31 AM
Quote
Does PB call addref every time?

Every time you pass it by value.

Quote
Could there be times where passing an object reference doesn't trigger addref?

If you pass it by reference.

Do not set an object variable passed by reference to Nothing unless you first have called AddRef.
Title: Re: Object Lifetime and Scope
Post by: Edwin Knoppert on April 18, 2009, 01:11:20 PM
>Do not set an object variable passed by reference to Nothing unless you first have called AddRef.
Huh?
I set this to nothing when passed byref, it releases the variable just fine.
I often do this to reuse an existing var and a previously set object must be released of course.
?
Title: Re: Object Lifetime and Scope
Post by: José Roca on April 18, 2009, 03:26:26 PM
 
If you pass an object variable by reference, the reference count isn't incremented, therefore you must not set it to NOTHING.


#COMPILE EXE
#DIM ALL
#INCLUDE "windows.inc"
#INCLUDE "REGEXP.INC"

SUB Foo (BYREF pRegExp AS IRegExp)
   pRegExp = NOTHING   ' <-- don't do this
END SUB

FUNCTION PBMAIN () AS LONG

   LOCAL pRegExp AS IRegExp
   pRegExp = NEWCOM "VBScript.RegExp"
   Foo pRegExp
   ' Now pRegExp is a null reference
   ' If you try to use it, it will GPF

END FUNCTION

Title: Re: Object Lifetime and Scope
Post by: Edwin Knoppert on April 18, 2009, 05:19:28 PM
I think we have a miscommunication, i do this all the time like:


Class Class1

    Class Method Destroy
        MsgBox "D"
    End Method

    Interface Interface1: Inherit IUnknown

        Property Get DateValue() As String
            Property = "11"
        End Property

    End Interface

End Class

Function CreateTheObject( ByRef o As Interface1 ) As Long
    o = Class "Class1"
End Function


Called like:

    Local c As Interface1   
    c = Class "Class1"   
    CreateTheObject( c )


Passing c means the address of c and therefore the same principles apply as using code in the original procedure.
The msgbox is executed twice.. as should.

Your example isn't wrong just that you say don't do this.. i guess you mean don't do this if you suspect the var passed should still have an object.
Of course..
Title: Re: Object Lifetime and Scope
Post by: José Roca on April 18, 2009, 06:00:29 PM
Quote
The msgbox is executed twice. as should.

Of course. When you do o = Class "Class1", PB calls c.Release and assigns to it the pointer of the new create class. But the question asked by Eriq was "Could there be times where passing an object reference doesn't trigger addref?", and the answer is when you pass it by reference.
Title: Re: Object Lifetime and Scope
Post by: Eriq VanBibber on April 19, 2009, 03:53:42 AM
Ah fun with COM.  It never dies.

I figured it out.
I did in deed have a situation, buried deep, that was a weak reference in which i was forced to call addref on my own.

The problem was (mainly because of my lack of experience with PB directly) that i was also asuming that i needed to call release since i called addref.

When i commented out the release code, all was working fine.

Seems PB is smarter than we thought.  If an object var has a non-null object handle (as in OBJPTR(var) <> 0) then PB will call release when it goes out of scope, regardless of how the object was instantiated.

I suppose i could have also fixed it by calling release AND setting to object to nothing, or even just setting it to nothing, since var=NOTHING calls release (if necessary) and also sets the object pointer to zero.

i ended up just commenting the code to state that although addref was called, PB will call release.

For any that care about the specifics of what i'm doing...i'm working with MAPI profiles and there's a HACK that has to be used with some profiles where you pickup an object that exists only at an offset of a current one.  That's why i have a coded addref.
here's a link to some explanation of the HACK. http://mapispy.com/articles/prprovider/MAPIProfileProvider.htm
Look for the text 'HACK CENTRAL' in the page and you see the C++ version of the hack.

Thanks guys.