Abort thread blocked by API call

163 views Asked by At

I have a C# WPF program which makes a blocking windows API call. When the soundcard/windows goes into an error state, the call blocks the thread permanently.

I wrote code so that the program execution 'timed out' and the program carried on. However, the Thread.Abort() call does not actually abort the thread successfully. The thread calling the API then lives on and prevents my program from shutting down later, once the user chooses to press close.

Is there a way to forcibly close the thread which hangs? If not, is there a better method to close the application than calling Environment.Exit(1) to force the application to close in this circumstance?

A short version of the code looks something like this:

//Program starts
try
{
   new OutputDevice();
}
catch
{ 
    //Tell the user that midi won't work, but otherwise carry on as usual
}

//Output device class
public class OutputDevice():Device
{
    //This method never returns a value when Windows or the hardware misbehave
    [DllImport("winmm.dll")]
    private static extern int midiOutOpen(ref int handle, int deviceID,MidiOutProc proc, int instance, int flags);

    public OutputDevice(int deviceID) : base(deviceID)
    {
        //Set up some variables
        var midiOutProc = HandleMessage;

        //This function calls the windows API when called, and hangs waiting for a response
        Func<int> openMidi = ()=>midiOutOpen(ref hndle, deviceID, midiOutProc, 0, CALLBACK_FUNCTION);

        //Try to call openMidi, and if that times out or returns an error, then throw an exception
        int result;
        if (!TryExecute<int>(openMidi, 20000, out result))
        {
            result = Constants.TimeoutCode;
        }

        if(result != Constants.MMSYSERR_NOERROR)
        {
            throw new Exception(result);
        }
    }

    ///<summary>
    ///Tries to execute a function on another thread. If the operation times out, aborts the thread and returns false.
    ///</summary>
    public static bool TryExecute<T>(Func<T> func, int timeout_milliseconds, out T result)
    {
        var t = default(T);
        var thread = new System.Threading.Thread(() => t = func());
        thread.Start();
        var completed = thread.Join(timeout_milliseconds);
        if (!completed)
        {
            //timeout
            thread.Abort();
        }
        result = t;
        return completed;
    }

    ///<summary>
    /// Handles Windows messages.
    /// </summary>
    protected virtual void HandleMessage(int handle, int msg, int instance, int param1, int param2)
    {
    //Handle the message
    }
}

Thanks!

0

There are 0 answers