WaitForInputIdle不能以编程方式启动mspaint

时间:2015-10-29 02:55:43

标签: c++ winapi mfc atl

我试图打开" mspaint"并在初始化后立即找到它的句柄。但如果我致电FindWindowNULL会返回WaitForInputIdle。如果我尝试使用函数Sleep(1000)它可以工作。但我不认为这是等待程序准备就绪的正确方法。这个代码有解决方案吗?

    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;
    }

1 个答案:

答案 0 :(得分:7)

WaitForInputIdle有效,但不是你认为的方式。这在很大程度上是因为文档具有误导性(或者至少不是应该如此明确):

  

等待指定的进程完成处理其初始输入并等待没有输入待处理的用户输入,或等待超时间隔结束。

这几乎是犯罪上不准确的。虽然备注部分指出,WaitForInputIdle每个进程最多等待一次,但它从未提及重要细节。具体做法是:

    一旦初始启动到达某个点,
  • WaitForInputIdle就会返回,此过程中的任何线程都准备好处理消息。这些消息不需要是用户输入。
  • 发明了
  • WaitForInputIdle以允许进程使用基于消息的协议与子进程通信。解决的具体方案是DDE,没有人 1)再使用。

WaitForInputIdle不能用作问题的可靠解决方案:等待子进程'UI显示。你真的需要等待UI显示。

系统提供两种可供您使用的解决方案:

  1. 全局CBT hook,并等待HCBT_CREATEWND回调。您可以检查CREATESTRUCT lpszClass 和/或 lpszName 成员,以过滤掉您感兴趣的窗口。
  2. 使用WinEvents并回复EVENT_OBJECT_CREATE event
  3. 每当要创建一个窗口时,都会调用全局CBT挂钩。 HWND引用已完全填充的内核结构,但调用CreateWindow[Ex]的客户端仍可终止窗口创建。相反,WinEvent是在完全构建窗口之后发布的,并且可以进行交互。

    通常,当应用程序需要在CreateWindowEx的调用者第一次看到HWND之前更新窗口的某些方面时,使用基于CBT挂钩的解决方案。相反,WinEvents通常是实现可访问性或UI自动化解决方案时的首选工具。

    <小时/> 其他资源:

    <小时/> 1) 是的,我知道,某些应用程序可能仍然使用DDE。但是,如果雷蒙德·陈在2007年建议,我们应该feel free to stop using DDE,我将把它作为权威指导。