I have the code following below that is for problem illustration only. Please ignore the part where CodeDom is used to compile the sources. Also in case you try it it leaves the "other process" executable running and consuming CPU time for no good.
The real problem is I start the thread, then abort the thread and see "Thread abort initiated" message and there's
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
message in the debugger output but neither catch
nor finally
blocks are invoked and there's no output from those blocks.
Of course because the extra threads created to handle the redirected output are not aborted the program hangs forever, but...
Why are catch
and finally
not invoked?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
new Program().payload();
}
private void payload()
{
var otherExecutableName = "OtherProcess.exe";
compileOtherProcessExecutable(otherExecutableName);
var thread = new System.Threading.Thread(() => threadFunc(otherExecutableName));
thread.Start();
Thread.Sleep( 1000 );
thread.Abort();
Debug.WriteLine("Thread abort initiated");
}
private void threadFunc( String secondExecutableName )
{
Process process = null;
try {
process = new Process();
{
process.StartInfo.FileName = secondExecutableName;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += new DataReceivedEventHandler(outputHandler);
process.ErrorDataReceived += new DataReceivedEventHandler(outputHandler);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
}
} catch( Exception e ) {
Debug.WriteLine( "Catch block: " + e.Message );
} finally {
Debug.WriteLine( "Finally block" );
if( process != null ) {
process.Kill();
Debug.WriteLine( "Process killed" );
}
}
}
static void compileOtherProcessExecutable(string filePath)
{
using (var compiler = new CSharpCodeProvider())
{
var parameters = new CompilerParameters(null, filePath, true);
parameters.GenerateExecutable = true;
var compResult = compiler.CompileAssemblyFromSource( parameters, otherProcessCode);
var errs = compResult.Errors;
if (errs.HasErrors)
{
var err = errs.Cast<CompilerError>().First();
throw new InvalidOperationException(String.Format(
"Compilation failed. Line {0}: {1}", err.Line, err.ErrorText));
}
}
}
private void outputHandler(object process, DataReceivedEventArgs output)
{
}
static readonly String otherProcessCode =
@"class Program {
public static void Main(string[] args)
{
while( true ) {
}
}
}";
}
}
The problem is the WaitForExit
call. This managed call will eventually bottom out in a native frame. When an Abort
call occurs while the thread is in native code nothing will actually happen until the thread returns to managed code. In this case the thread is waiting for the process to finish and hence won't actually return until you physically kill the process
Note: Even if the exception was thrown the catch
block won't actually catch it. The exception will be rethrown at the end of the block. You need to catch it and call the ResetAbort
method