I'm trying to open "mspaint" and find handle of it right after it has been initialized. But FindWindow
returns NULL
if I call WaitForInputIdle
. If I try to use the function Sleep(1000)
it works. But I don't think it's a right way to wait for the program to be ready. Is there a solution for this code?
CString strWindowDirectory;
GetSystemDirectory(strWindowDirectory.GetBuffer(MAX_PATH), MAX_PATH);
SHELLEXECUTEINFO sei = { 0 };
sei.cbSize = sizeof(SHELLEXECUTEINFO);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb = L"open";
sei.lpFile = L"mspaint";
sei.lpDirectory = strWindowDirectory;
sei.nShow = SW_SHOWNORMAL;
HWND hPaint = NULL;
if(ShellExecuteEx(&sei))
{
int r = ::WaitForInputIdle(sei.hProcess, INFINITE);
ATLTRACE(L"WaitForInputIdle %d\n", r);
if (sei.hProcess == NULL) return;
hPaint = ::FindWindow(L"MSPaintApp", NULL);
ATLTRACE(L"Handle %d\n", hPaint);
if (!hPaint) return;
}
else
{
MessageBox(L"Couldn't find mspaint program");
return;
}
WaitForInputIdle works, but not the way you assume it does. This is largely, because the documentation is misleading (or at least not as explicit as it should be):
This is almost criminally inaccurate. While the Remarks section notes, that
WaitForInputIdle
waits at most once per process, it never mentions significant details. Specifically:WaitForInputIdle
returns, as soon as the initial startup has come to a point, where any thread in the process is ready to process messages. Those messages need not be user input.WaitForInputIdle
was invented to allow a process to communicate with a child process using a message-based protocol. The specific scenario addressed was DDE, which no one1) uses anymore.WaitForInputIdle
cannot be used as a reliable solution to your problem: Waiting for a child process' UI to show up. You really need to wait for the UI show up.The system offers two solutions you can use:
HCBT_CREATEWND
callback. You can inspect the CREATESTRUCT's lpszClass and/or lpszName members to filter out the window you are interested in.EVENT_OBJECT_CREATE
event.The global CBT hook is called, whenever a window is about to be created. The kernel structures that the
HWND
references have been fully populated, but the client callingCreateWindow[Ex]
may still terminate window creation. In contrast, the WinEvent is issued, after the window has been fully constructed, and is ready for interaction.In general, a solution based on a CBT hook is used, when an application needs to update certain aspects of a window before the caller of
CreateWindowEx
gets to see theHWND
for the first time. WinEvents, instead, are usually the tool of choice when implementing accessibility or UI automation solutions.Additional resources:
1) Yes, I know, some applications might still use DDE. But if Raymond Chen suggested in 2007, that we should feel free to stop using DDE, I'll just pass that on as authoritative guidance.