On the importance of making sure WaitForInputIdle doesn’t think you’re idle, episode 2


Continuing our DDE micro-series, we'll look at another customer who was having trouble getting the shell to recognize their DDE server.

We have a program that supports DDE for legacy reasons. More specifically, we have two versions of that program, and we support the user installing both of them side by side. To mediate this uneasy coexistence, we intend to have a "selector" program that registers for all the file extensions, and the user configures the selector so that the requests are directed at the specific version of the program the user chooses.

We threw together a quick prototype of the selector, which simply looks up the user's preference, and then forwards its command line to the appropriate version. For example, we register the open verb as ddeexec = selector.exe, and the selector program decides that (say) the user wants files to open in v1, so the selector runs "C:\Program Files\Contoso\v1\contoso.exe".

What we found is that we get There was a problem sending the command to the program. What are we doing wrong?

Recall that after the shell launches the registered command, the shell calls Wait­For­Input­Idle, and then when that call returns, the shell goes looking for the DDE server.

The first catch is that Wait­For­Input­Idle requires a GUI program. This makes sense because console programs don't pump messages, so any such wait would be infinite. What's happening is that the shell launches the selector, and then the shell calls Wait­For­Input­Idle, which returns (with an error), and then the shell goes looking for the DDE server. But the DDE server isn't ready yet.

The selector needs to be a GUI program, and it needs to perform a Wait­For­Input­Idle on the final program, so that it doesn't go input idle until the actual server goes input idle. (In a sense, the selector is proxying input idle-ness.)

The customer tried a quick prototype with a WPF program, but it still didn't work. I don't know for sure, but I suspect that something in the WPF framework is pumping messages (perhaps due to a cross-thread COM operation) or creating a background thread that goes input-idle, which causes the entire process to go input-idle before the business logic can launch the true DDE server.

The customer tried another prototype with a pure Win32 program that launched the true DDE server, and then used Wait­For­Input­Idle to wait for the true DDE server to go idle, and then exited.

And this worked, sort of.

What the customer found is that they needed to add a Sleep(1000) between launching the true DDE server and calling Wait­For­Input­Idle. If they called Wait­For­Input­Idle immediately after the Create­Process, then the shell error occurred.

This stumbles across another fine detail of Wait­For­Input­Idle: The process must be a GUI process. And even though the true DDE server is a GUI process, the selector is so fast that it calls Wait­For­Input­Idle before the true DDE server can call into the window manager and create its message queue, which is what causes the program to be marked as a GUI program. When this happens, the Wait­For­Input­Idle function returns WAIT_FAILED.

Therefore, the selector program should check whether Wait­For­Input­IdleWAIT_FAILED; if so, it should sleep a little bit and try again. (And eventually give up.)

Phew.

Please stop using DDE.

Comments (6)
  1. DWalker says:

    Using Sleep is always weird. If you have to wait “for a second or two” in order for a program to run correctly, then something is probably wrong with your program. And you can never get the timing “provably right”.

    However, sometimes you have to resort to Sleeps in order to just “get stuff done”. Unfortunately.

    1. Yuri Khan says:

      The alternative is to design a communication mechanism between the programs. For example, the selector could create an event object, let the child program inherit it, and wait for the child to signal it. Or it could pass a pipe as the standard output and wait for the child to say “hello world\n” into it. Or it could wait until the child goes input-idle… wait.

      1. DWalker says:

        Exactly right, Yuri!

    2. smf says:

      > However, sometimes you have to resort to Sleeps in order to just “get stuff done”. Unfortunately.

      I hit this problem when one of our hardware suppliers introduced Windows CE devices and their printer driver was just ridiculously unreliable. It could print a page ok, but not multiple pages. Unfortunately their main focus was on the parking ticket industry, while we were doing invoices etc. When I visited them so they could tell me what I was doing wrong I eventually persuaded them to show me their code and it was multithreaded and full of for() delay loops and sleep to keep the threads in some form of sync. After much effort I got them to try putting some variables in (still not using events because they are “too hard”) and they were surprised that it started working better. It was running on an SH2, so it wasn’t ever going to be the fastest device. But printing really did burn up the CPU.

  2. skSdnW says:

    Does this mean the shell uses Sleep as well? From what I remember, it takes a while for the shell error MessageBox to appear…

Comments are closed.

Skip to main content