views:

530

answers:

1

If I show a MessageBox as modal of a window on another process, it works just fine as long as my program remains responding. If it is closed or terminated while the MessageBox is showing the windows that received the MessageBox will be locked (but still responding) and it will have to be finalized via Task Manager.

Here is a sample code to demonstrate that:

using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;

namespace TestMessageBox
{
    class Program
    {
        private WindowWrapper notepad;

        Program(IntPtr handle)
        { notepad = new WindowWrapper(handle); }

        static void Main(string[] args)
        {
            Process[] procs = Process.GetProcessesByName("notepad");
            if (procs.Length > 0)
            {
                Console.WriteLine("Notepad detected...");
                Program program = new Program(procs[0].MainWindowHandle);
                Thread thread = new Thread(new ThreadStart(program.ShowMessage));
                thread.IsBackground = true;
                thread.Start();
                Console.Write("Press any key to end the program and lock notepad...");
                Console.ReadKey();
            }
        }

        void ShowMessage()
        { MessageBox.Show(notepad, "If this is open when the program ends\nit will lock up notepad..."); }
    }

    /// <summary>
    /// Wrapper class so that we can return an IWin32Window given a hwnd
    /// </summary>
    public class WindowWrapper : System.Windows.Forms.IWin32Window
    {
        public WindowWrapper(IntPtr handle)
        { _hwnd = handle; }
        public IntPtr Handle
        { get { return _hwnd; } }
        private IntPtr _hwnd;
    }

}

How to avoid that?

Luiz Borges

A: 

The act of showing a modal dialog disables the parent window of the dialog (Notepad's window in your example). When the modal dialog is closed, the parent window gets re-enabled.

If your program dies before it re-enables the window, that window will never get re-enabled - it's up to the thread that's showing the dialog to re-enable the parent. (In your example, it happens within MessageBox.Show(), after the user clicks OK or whatever.)

The only way to make this work would be to have a second process whose responsibility it was to put things back as they should be if the process creating the modal dialog dies prematurely, but that's horrible. And it's still not bulletproof - what if the watcher process dies too?

RichieHindle
Wow, this is some bad design. How come an external program can lock every window of the system? :( My app will need to show a modal form, and I wanted to make it fail-safe, so if anything happens to my app the target application can go on with its business...
Luiz Borges
@Luiz: How come an external program can show a MessageBox as modal dialog of a window on another process? 8-) It cuts both ways - either you have a flexible system that lets you do what you're doing with Notepad, or you have a safer system that doesn't, in which case the question wouldn't arise.
RichieHindle
@Richie: What I meant, is that if a process is no longer running the OS should be smart enough to know that any lock caused by its handles should be released. It's diferent from a lock-up caused by something that stoped responding, the process and its handle no longer exists. It has been a long time since my last OS class, so I don't really know if it is feasible for a OS to work like that in the real world... :(
Luiz Borges
BTW: I don't think that having another program running would be a workaround, since it wouldn't be able to release the handles created by another process. Also, MessageBox.Show don't return any handles, so there is nothing to do except terminate (forcely) the target program.
Luiz Borges
@Luiz: The problem is not a lock, nor a handle, but the enabled/disabled state of the Notepad window. `MessageBox.Show()` looks like this under the hood: `EnableWindow(parent, FALSE); (Show the modal dialog); EnableWindow(parent, TRUE);` There's no lock or handle, just whether the Notepad window is enabled or not. Something needs to call `EnableWindow(Notepad, TRUE);`, and any process can do that. (By the way, I'm not saying that the second process idea is a good solution - far from it - just that it's the only solution I can think of.)
RichieHindle
@Ritchie: I didn't like the second program idea, but also I didn't know (or remember) of the EnableWindow function. As some sort of workaround, I might use some sort of mutex, semaphore, file flag, etc to indicate the lock and then test in my program start if it is ticked, is so, call EnableWindow. This way if my program locks up I just have to restart it and it will fix everything up.
Luiz Borges
@Luiz: Yes, that ought to work.
RichieHindle