I did not evaluate the correctness of the code snippet itself; there is a
possibility that the call to DdeDisconnect will never occur, regardless of
other concerns.
The finalizer will never get called on the same thread as that used by the
code that created the object, so the alternative code path (posting a
message to the window/thread that created the object) will be used. The
concern would when the application was exiting and the runtime is shutting
down, or the appdomain the code is running in is getting destroyed. There
are no guarantees made about the order that finalizers will be called in, so
there is a possibility that the window you are posting the message to has
already been destroyed. In this case the call to DdeDisconnect may never
occur since the window or thread may already have been destroyed. It's even
possible that the runtime's infrastructure for delivering messages has
already been torn down.
The real question is what the side-effects are of not cleaning up the dde
connection. If there are none then your workaround may work as intended, but
if it means that the client or server on the other end never cleans up its
connection then you may have a resouce leak on the other end. How resilient
is the Dde client/server you are connected to? Many of them are poorly
written (the dde protocol itself is horrible). If the Ddeml layer cleans up
behind connections that disappear without following the defined shutdown
semantics then you may be ok, but in my experience dde apps are flaky.
If these are legitimate concerns then I would run the entire dde connection
on a separate thread created and managed by the class that encapsulates the
interface to the ddeml layer, and marshal all dde messages to and from this
thread from other threads. You can create a hidden window on this thread to
serve as the target of dde messages. When the object is finalized,
regardless of the thread calling Dispose or the finalizer, you can signal
the worker thread to shutdown and cleanup, and the other thread can block
until that has completed. That way the lifetime of the thread that the dde
connection is created on, and the window used to send and receive messages,
is entirely under your control.
Just some thoughts,
Dave
"Brian Gideon" <br*********@yahoo.com> wrote in message
news:11**********************@g47g2000cwa.googlegr oups.com...
Anyone have an opinion or alternative approach? If not, I'll stick
with what I have.
Brian Gideon wrote: How have you handled the finalization of thread-specific unmanaged
resources?
My question pertains specifically to using the DDEML which is a
thread-specific API. In other words, every call to the API using the
same handle must be made on the same thread as the one that obtained
the handle. Obviously, there is a conflict with the GC and the
finalizer thread in cases where the programmer forgets to call Dispose
on a wrapper class.
I have a solution that I have been using for 3 or 4 years now and it
seems to work, but to be honest I'm not positive that it is the right
one. I'm just now beginning to explore the pitfalls of finalization on
thread-specific resources in a careful manner.
Basically what I have is this:
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Cleanup managed resources.
}
if (Thread.CurrentThread.ManagedThreadId == knownThreadId)
{
DdeDisconnect(handle);
}
else
{
PostThreadMessage(knownThreadId, WM_MYUSERMSG, handle,
IntPtr.Zero);
}
}
}
Of course, I have to install an IMessageFilter on the target thread
first. The implementation would be:
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (m.Msg == WM_MYUSERMSG)
{
DdeDisconnect(m.wParam);
return true;
}
return false;
}
It seems clean, but there are situations where DdeDisconnect will never
be called and I realize that. In most circumstances this seems to work
well.