1

I have a bit of an issue with an interaction between two applications.

You can replicate the issue with code like this:

using (Form1 frm = new Form1())
{
    frm.ShowDialog();
}

Thread.Sleep(120000);

Using this code, a separate application, which we do not control, freezes when our application is running the Thread.Sleep portion of the code. We actually have a good idea of what's happening, having been on the receiving end of this bug before: the other app has a callback from the native EnumWindows method which doesn't check to see if windows are visible using the IsWindowVisible native method. Then when it calls GetWindowText or possible some other function, it hangs.

The reason for this hang is that it is trying to talk to our (now closed) window, which still has a handle registered, but doesn't respond to messages sent to its queue, and so the caller just sits there indefinitely (until the window fully dies after our Thread.Sleep).

Here is some sample code for the "untouchable" application that we have to deal with:

public bool RunTest()
{
    return NativeMethods.EnumWindows(new NativeMethods.EnumWindowsProc(EnumWindowsCallback), IntPtr.Zero);
}

private bool EnumWindowsCallback(IntPtr windowHandle, IntPtr parameter)
{
    NativeMethods.SendMessage(windowHandle, 0x000E, IntPtr.Zero, IntPtr.Zero);
    return true;
}

If you run the first code, close the window that it opens, then run the second code block in a separate process, the second (EnumWindows) process will hang.

My problem is that I have to deal with this, and can't just fix the code in the hanging application. I can kick the window off into a separate thread, but that seems clumsy and somewhat fragile. Does anyone know of a way to clean this problem up using native calls, or any other more direct method?

Steve Westbrook
  • 1,698
  • 2
  • 20
  • 21
  • Your real code, which is executed instead of Thread.Sleep, possibly has some loop. Try to add Application.DoEvents to this loop. – Alex F Mar 13 '13 at 18:50
  • This code doesn't contain any loops - the code posted triggers the bug, and Form1 is the default form created in an empty Windows Forms project in Visual Studio. – Steve Westbrook Mar 13 '13 at 19:06
  • The form *shouldn't* still have a handle registered, since the `using` should have called `Dispose`. Is there some other code executed after that sleep? Or does the program exit once the sleep is finished? – Jim Mischel Mar 13 '13 at 19:51
  • 1
    This is all perfectly normal and the expected outcome. There's only *one* good workaround for poorly written programs like this: uninstall them. – Hans Passant Mar 13 '13 at 20:29
  • @Jim Mischel - Shoudn't is right, but the operating system has held onto the form's handle past the form's destruction, hence the problem. The OS only cleans up the handle when the form's owning thread terminates. – Steve Westbrook Mar 13 '13 at 21:22
  • @Hans Passant - There's a big world full of bad code out there. If I don't deal with it, when the user installs my application and something else breaks, I get blamed whether I deserve it or not. In other words, my code is the bad code if I don't account for other people's bugs (to a reasonable degree). There's at least one solution - since the handle seems to be maintained in thread-scope, narrow that scope - but I was hoping for something a little more graceful. – Steve Westbrook Mar 13 '13 at 21:24
  • Maybe I'm not understanding, but if the goal is to make your application never block this rogue application, then you need to make your application's UI thread so that it *never blocks*. Which is good advice for any Windows application. – Tergiver Mar 14 '13 at 19:01

1 Answers1

1

Instead of calling Sleep you can use a waitable timer (created with the CreateWaitableTimer function) and continue to run a message loop (using the MsgWaitForMultipleObjects function) until the timer expires.

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • I've updated my post a little: the `Sleep` call is just an example which quickly reproduces the issue. In my production code, various forms are shown at arbitrary times as part of a long-running application thread. The message queues are associated with these windows. But the scope of the window re: EnumWindows is different than its scope re:SendMessage! That is what I'm trying to deal with. – Steve Westbrook Mar 13 '13 at 21:21