Reputation: 4768
I have a .NET Component that is being exposed to COM.
This component has a thread running internally that connects to a named pipe, and handles disconnects from the pipe and reconnects as needed.
The thread runs for the lifetime of the instance of the component that was instantiated inside the COM Program (vb6 in this instance).
If the COM application is shutting down, but not calling my "Close" method that gracefully shuts down the thread (and pipe), is there a way to detect that they are closing the application down so that I do not need to rely on them following the rules properly?
The thread has it's ISBackground = true set.
This is the basic parts of my Thread
private System.IO.Pipes.NamedPipeClientStream mPipe;
private System.Threading.AutoResetEvent mConnectedHold;
private void ConnectPipe()
{
while (mRunning)
{
try
{
mConnecting = true;
mConnected = false;
if (mPipe != null)
{
mPipe.Close();
mPipe.Dispose();
}
mPipe = new System.IO.Pipes.NamedPipeClientStream(@".", @"MyPipe", System.IO.Pipes.PipeDirection.InOut, System.IO.Pipes.PipeOptions.Asynchronous);
mPipe.Connect(1000);
mConnecting = false;
mConnected = true;
mPipe.BeginRead(mBuffer, 0, mBuffer.Length, new AsyncCallback(ReadPipe), null);
mConnectedHold.WaitOne();
}
catch (TimeoutException ex)
{
System.Diagnostics.Debug.Print("Not connected yet");
}
catch (Exception ex)
{
mRunning = false;
}
}
}
and the close is fairly basic.
public void Close()
{
mRunning = false;
if (mPipe != null )
{
mPipe.Close()
mPipe.Dispose()
}
mConnected = false;
}
Upvotes: 1
Views: 327
Reputation: 941465
The thread has it's ISBackground = true set.
COM doesn't know anything about your thread, VB6 especially since it doesn't support threading at all. Setting the Thread.IsBackground property to true tells the CLR that it is okay to abort the thread when the program's main thread exits. So seeing it terminated abruptly without a diagnostic is unsurprising.
There's a likely problem with your mRunning variable, it needs to be declared volatile. The way you use it in your thread makes it likely that it never sees a change to the variable in the Release build. The jitter optimizer is liable to optimize the code and load the variable value in a CPU register. The volatile keyword prevents this.
This is still a half-baked solution, you really ought to use a ManualResetEvent to signal the thread. Call its Set() method in your Close() method, WaitOne(0) in the thread to test it. You then also have to wait for the thread to exit so that disposing the pipe doesn't bomb the thread code. Watch out for deadlock.
Now you don't need Thread.IsBackground anymore and got a better shot at debugging this code. Next thing you need to do is implement IDisposable and write a finalizer for your class to ensure that your code shuts down properly even if the client code didn't call the Close() method nicely. Short from just forgetting to do so, this is also necessary if the client code bombed. Have your Close() method call Dispose(). You might want to do two different things with the pipe depending on whether the app shutdown in a normal way. Implement the Disposing pattern, you can tell the difference between a 'nice' shutdown (Close was called) versus a nasty one (the finalizer was called) through the disposing argument.
Now your code is resilient either way. Finding out why the client code didn't call Close() should be simple.
Upvotes: 1
Reputation: 14726
I investigated it some time ago and came to the conclusion that it is not possible to detect when .Net COM objects are released.
The closest thing I found was someone who tried to replace the IUnknown.Release method by changing the vtable of the CCW (there are some methods in the Marshal class that allows you to do such things). There were some serious issues that I don't remember.
You just have to build your class so that it can handle the situation using internal reference counts and finalizers.
To me it seems strange that the COM interop doesn't allow you to add some kind of OnFinalRelease event.
Upvotes: 0
Reputation: 30165
Perhaps use one of the AppDomain events. The "DomainUnload" and "ProcessExit" look like good candidates.
Actually, it sounds like you intend for you managed code to remain running, even if the COM application comes down. Perhaps then change your WaitOne to take a TimeSpan parameter to awake periodically, and test if the client COM application is still there.
Upvotes: 0