I have the following scenario:
Class Managed (C++/CLI) holds a pointer to class Unmanaged (native C++).
Class Unmanaged in turn holds a gcroot< > pointer (to a TcpClient).
On process exit, the finalizer of "Managed" deletes the Unmanaged object.
The destructor of Unmanaged tries to use the gcroot<> object (to perform a
disconnect) and I seem to get an Exception that the gcroot object has
already been disposed (ObjectDisposedException).
So the question really comes down to what assurances exist at process exit
in terms of managed objects held by the gcroot<> template, and whether they
can be assumed to still exist. Or, does the fact that I have a finalizer
indirectly referencing another managed object (ie. via the native object)
mean I am violating the rule that a finalizer should not refer to other
managed objects (which is a definite no-no in a fully managed environment)
Thanks
Kevin
> Class Managed (C++/CLI) holds a pointer to class Unmanaged (native C++).
> Class Unmanaged in turn holds a gcroot< > pointer (to a TcpClient).
>
> On process exit, the finalizer of "Managed" deletes the Unmanaged object.
This is ok.
> The destructor of Unmanaged tries to use the gcroot<> object (to perform a
> disconnect) and I seem to get an Exception that the gcroot object has
> already been disposed (ObjectDisposedException).
If you are called from the finalizer, you must never use any .NET
objects! This is not allowed!
So I suggest you implement a "Dispose" Methode in your unmanaged class.
And I also suggest, you should implement the IDisposable-Pattern in your
managed class.
If the managed class is called from Dispose, then you should also
Dispose other managed classes and delete the unmanaged class.
If you are called from the finalizer, you should *only* delete the
unmanaged class and never ever use or reference managed classes!
> So the question really comes down to what assurances exist at process exit
> in terms of managed objects held by the gcroot<> template, and whether they
> can be assumed to still exist.
Inside a finilizer, you must assume, that any other managed objects does
not exist anymore!
> Or, does the fact that I have a finalizer
> indirectly referencing another managed object (ie. via the native object)
> mean I am violating the rule that a finalizer should not refer to other
> managed objects (which is a definite no-no in a fully managed environment)
Exactly.
--
Greetings
Jochen
My blog about Win32 and .NET
http://blog.kalmbachnet.de/
"Jochen Kalmbach [MVP]" <nospam-Joch...@holzma.de> wrote in message
news:#NljU2vS...@TK2MSFTNGP02.phx.gbl...
> Hi Kevin!
>
>> Class Managed (C++/CLI) holds a pointer to class Unmanaged (native C++).
>> Class Unmanaged in turn holds a gcroot< > pointer (to a TcpClient).
>>
>> On process exit, the finalizer of "Managed" deletes the Unmanaged object.
>
> This is ok.
>
>> The destructor of Unmanaged tries to use the gcroot<> object (to perform
>> a disconnect) and I seem to get an Exception that the gcroot object has
>> already been disposed (ObjectDisposedException).
>
> If you are called from the finalizer, you must never use any .NET objects!
> This is not allowed!
>
> So I suggest you implement a "Dispose" Methode in your unmanaged class.
> And I also suggest, you should implement the IDisposable-Pattern in your
> managed class.
> If the managed class is called from Dispose, then you should also Dispose
> other managed classes and delete the unmanaged class.
>
> If you are called from the finalizer, you should *only* delete the
> unmanaged class and never ever use or reference managed classes!
>
>> So the question really comes down to what assurances exist at process
>> exit in terms of managed objects held by the gcroot<> template, and
>> whether they can be assumed to still exist.
>
> Inside a finilizer, you must assume, that any other managed objects does
> not exist anymore!
This is because when multiple objects become unreachable during a single GC
cycle, there's no guarantee on which order they will be finalized.
The object in the gcroot is reachable though. Reachability analysis can't
look into the managed class.
The only time reachable objects can be finalized is during appdomain
shutdown, but I think finalization is actually just skipped in that case.
I suspect the TcpClient isn't being finalized at all, but explicitly
disposed in two different places.
>
>> Or, does the fact that I have a finalizer indirectly referencing another
>> managed object (ie. via the native object) mean I am violating the rule
>> that a finalizer should not refer to other managed objects (which is a
>> definite no-no in a fully managed environment)
That's a misstatement of the rule. A finalizer should not refer to any
managed objects which aren't independently reachable from a root, because
they could already have been collected. Objects protected against
collection using a static field or C++ gcroot template can be used during
finalization.
__________ Information from ESET NOD32 Antivirus, version of virus signature database 4498 (20091011) __________
The message was checked by ESET NOD32 Antivirus.
> That's a misstatement of the rule. A finalizer should not refer to any
> managed objects which aren't independently reachable from a root,
> because they could already have been collected. Objects protected
> against collection using a static field or C++ gcroot template can be
> used during finalization.
Yes, it is possible to use "reachable" managed objects... but this is
bad app-design and could lead to object-resurrection...
"Jochen Kalmbach [MVP]" <nospam-Joch...@holzma.de> wrote in message
news:#JXmSPwS...@TK2MSFTNGP02.phx.gbl...
> Hi Ben!
>
>> That's a misstatement of the rule. A finalizer should not refer to any
>> managed objects which aren't independently reachable from a root, because
>> they could already have been collected. Objects protected against
>> collection using a static field or C++ gcroot template can be used during
>> finalization.
>
> Yes, it is possible to use "reachable" managed objects... but this is bad
> app-design and could lead to object-resurrection...
There's a distinction between root-reachable vs addressable from the
finalizer. Root-reachable objects cannot have been finalized, so no
problems with resurrection. But a finalizer also has access to the fields
of the object being finalized, some of these may be the sole surviving
reference to an object that is not root-reachable, and might already be
gone.
Anyway, for the OP's problem, the gcroot keeps the TcpClient alive, so the
ObjectDisposedException was not caused by the GC running the TcpClient's
finalizer. Something else is disposing the TcpClient.
"Jochen Kalmbach [MVP]" <nospam-Joch...@holzma.de> wrote in message
news:#JXmSPwS...@TK2MSFTNGP02.phx.gbl...
> Hi Ben!
>
>> That's a misstatement of the rule. A finalizer should not refer to any
>> managed objects which aren't independently reachable from a root, because
>> they could already have been collected. Objects protected against
>> collection using a static field or C++ gcroot template can be used during
>> finalization.
>
> Yes, it is possible to use "reachable" managed objects... but this is bad
> app-design and could lead to object-resurrection...
oops, I see the OP is specifically talking about appdomain shutdown, when
the GC starts killing off root-reachable objects.
There's a mechanism specifically for dealing with this:
http://msdn.microsoft.com/en-us/library/system.environment.hasshutdownstarted.aspx
> The object in the gcroot is reachable though. Reachability analysis can't
> look into the managed class.
>
> The only time reachable objects can be finalized is during appdomain
> shutdown, but I think finalization is actually just skipped in that case.
>
> I suspect the TcpClient isn't being finalized at all, but explicitly
> disposed in two different places.
I have since found various articles on the web that actually describes the
finalization process, which in particular comments that during AppDomain
shutdown all reachable finalizable objects are also finalized. This would
seem to explain my situation.
The TcpClient being disposed in two places shouldn't actually be a problem,
since Dispose( ) is canonically intended to handle that situation - but I'm
pretty sure that does not happen in my case anyway.
Fortunately there are a couple of means to test if you are in an AppDomain
shutdown and therefore I can code against it.
Kevin