【问题标题】:CloseWindow doesn't minimize window from the process I just launchedCloseWindow 不会从我刚刚启动的进程中最小化窗口
【发布时间】:2016-05-11 10:02:31
【问题描述】:

好吧,我想在启动窗口后最小化窗口,但它没有生效,我不知道我做错了什么。能否请您指出下面代码中的错误?

除了打开一个窗口之外什么都没有发生。

HWND  g_hwnd;
int  g_nFound;

BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam);

HWND GetHwndFromPID(DWORD dwProcessId)
{
g_hwnd = NULL;
g_nFound = 0;

EnumWindows(FindHwndFromPID, (LPARAM)dwProcessId);

if (g_hwnd)  // we found one...
    return (g_hwnd);

// nothing found :-(

return (NULL);
}

BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam)
{
DWORD   dwPID2Find = (DWORD)lParam;
DWORD   dwPID = 0;

if (GetWindowThreadProcessId(hwnd, &dwPID))
{
    if (dwPID == dwPID2Find)
    {
        g_hwnd = hwnd;


        return  (FALSE);
    }
}

return  (TRUE);
}


 int main
 {
     ..../
 if (!CreateProcessA(NULL,     // No module name (use command line)
     command_line.GetBuffer(),
     NULL,           // Process handle not inheritable
     NULL,           // Thread handle not inhberitable
     FALSE,          // Set handle inheritance to FALSE
     0,              // No creation flags
     NULL,           // Use parent's environment block
     NULL,           // Use parent's starting directory 
     &si,            // Pointer to STARTUPINFO structure
     &pi)           // Pointer to PROCESS_INFORMATION structure
     )
 {

     //... error handling
     return 0;
 }

 WaitForInputIdle(pi.hProcess, 1000);
 HWND hwnds = GetHwndFromPID(pi.dwProcessId);

 printf("Process Handle %d, hwnd id: %p ",pi.dwProcessId, hwnds);

 CloseWindow(hwnds);

该代码应该最小化窗口,但我不知道为什么没有。

【问题讨论】:

  • 我没有看到任何对 MinimizeWindow 的调用。 (我看到了对 CloseWindow 的调用 - 但这应该只是关闭它,最小化)。您必须调试您的 GetHwndFromPID。
  • @MegRay 你确定你检索到了正确的窗口句柄吗?尝试使用“Spy++”工具检查句柄值。另外,您没有检查来自WaitForInputIdle 的输出,可能发生超时...
  • @MartinBonner 没有MinimizeWindow()CloseWindow() minimizes。 (它变得更奇怪了,而不是UnminimizeWindow()RestoreWindow() 它是OpenIcon()。)我猜两者的现代方法是使用ShowWindow(),但我不确定。 (这可能不会解决 OP 的问题;我不知道解决方案是什么,而且 IInspectable 可能还是有它。)
  • @MegRay:您是否有理由尝试使用CloseWindow(),而不是使用官方方法来请求生成的应用程序最小化运行?将STARTUPINFO 结构传递给CreateProcess(),其中dwFlags 设置为STARTF_USESHOWWINDOWwShowWindow 根据需要设置为SW_MINIMIZESW_SHOWMINIMIZEDSW_SHOWMINNOACTIVEwShowWindow 值将在生成的进程第一次为自己调用 ShowWindow() 时生效。这是记录在案的行为。
  • 如果程序创建了多个窗口怎么办?除了主窗口之外,大多数程序还会创建一堆帮助窗口。你的程序只是抓取第一个窗口,它可能是一个工具提示或其他一些帮助窗口。

标签: c++ winapi


【解决方案1】:

您以错误的方式处理此问题。在调用CreateProcess() 时,要求生成的进程运行最初最小化的官方和记录方法是使用STARTUPINFO 结构,例如:

int main()
{
    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.cbSize = sizeof(si);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_MINIMIZE;

    //...

    if (!CreateProcessA(..., &si, ...))
    {
        //... error handling
        return 0;
    }

    //...

    return 0;
}

STARTUPINFO structure

wShowWindow
如果 dwFlags 指定 STARTF_USESHOWWINDOW,则此成员可以是可以在 ShowWindow 函数的 nCmdShow 参数中指定的任何值,SW_SHOWDEFAULT 除外。否则,此成员将被忽略。

对于 GUI 进程,第一次调用 ShowWindow 时,忽略其 nCmdShow 参数 wShowWindow 指定默认值。在随后对 ShowWindow 的调用中,如果 ShowWindow 的 nCmdShow 参数设置为 SW_SHOWDEFAULT,则使用 wShowWindow 成员。

ShowWindow function

nCmdShow [输入]
类型:整数

控制窗口的显示方式。 如果启动应用程序的程序提供了 STARTUPINFO 结构,则该参数在应用程序第一次调用 ShowWindow 时将被忽略。否则,第一次调用 ShowWindow 时,该值应该是 WinMain 函数在其 nCmdShow 参数中获取的值。

...

应用程序第一次调用 ShowWindow 时,它应该使用 WinMain 函数的 nCmdShow 参数作为它的 nCmdShow 参数。对 ShowWindow 的后续调用必须使用给定列表中的值之一,而不是 WinMain 函数的 nCmdShow 参数指定的值。

正如在讨论 nCmdShow 参数时所指出的,如果启动应用程序的程序在结构中指定了启动信息,则在第一次调用 ShowWindow 时会忽略 nCmdShow 值。在这种情况下,ShowWindow 使用 STARTUPINFO 结构中指定的信息来显示窗口。在随后的调用中,应用程序必须调用 ShowWindow 并将 nCmdShow 设置为 SW_SHOWDEFAULT 以使用启动应用程序的程序提供的启动信息。此行为适用于以下情况:

• 应用程序通过调用设置了 WS_VISIBLE 标志的 CreateWindow 来创建它们的主窗口。

• 应用程序通过调用 CreateWindow 并清除 WS_VISIBLE 标志来创建它们的主窗口,然后调用 ShowWindow 并设置 SW_SHOW 标志以使其可见。

【讨论】:

  • 嗯,HWND 是我做任何活动所必需的,比如在使用后销毁窗口,这正是我想要的。我认为这个问题有点误导。抱歉,请告诉我如何计算 HWND ID。
  • @MegRay:或者,您可以将WM_QUIT 消息发布到衍生进程的主线程ID,而不是任何特定的HWND。或者,如果您决定以编程方式杀死它而不是让它优雅地退出,您可以只 TerminateProcess() 生成的进程。无论如何,您已经知道如何找到HWND,您显示的代码没有任何问题。因此,要么您在创建 HWND 之前搜索它,要么您找到的 HWND 与应用程序向用户显示的 HWND 不同。就像 Marco 说的,使用 Spy++ 来验证。
  • 嗯,我已经使用了 spy++,我看到它显示了完全不同的进程 ID 和 HWND ID,我为调试目的打印了。
  • 我没有看到您的HWND 枚举有任何问题(尽管您没有对EnumWindows() 进行错误处理,并且没有使用g_nFound)。除非在WaitForInputIdle() 退出后创建HWND,否则您只需要调试代码并查看实际发生的情况。鉴于您显示的代码,枚举不可能返回不属于CreateProcess() 报告的进程ID 的非零HWND。所以要么你打印错了,要么你误解了 Spy++ 向你展示的内容。
  • 注意对于 Windows 8 及更高版本,EnumWindows 仅枚举桌面应用程序的顶级窗口。我在 EnumWindows 函数上看到了这个注释。这有什么影响吗?
【解决方案2】:

由于应用程序窗口可能不属于启动的进程,而是属于其子进程,因此您必须通过在比较中包含父进程来更深入地了解FindHwndFromPID

我们也不能指望WaitForInputIdle()自己,你必须给创建的进程足够的时间来完全初始化。

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tlhelp32.h>

HWND  g_hwnd;
int  g_nFound;

BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam);
/*___________________________________________________________________________________________________
*/
HWND GetHwndFromPID(DWORD dwProcessId)
{
    g_hwnd = NULL;
    g_nFound = 0;

    EnumWindows(FindHwndFromPID, (LPARAM)dwProcessId);

    if (g_hwnd)  // we found one...
        return (g_hwnd);

    // nothing found :-(

    return (NULL);
}
/*___________________________________________________________________________________________________
*/
DWORD GetParentProcess(DWORD pid){
    PROCESSENTRY32 p32={sizeof(PROCESSENTRY32)};
    DWORD ParentPID=0;
    HANDLE hSnapShot =  CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 );
    if(Process32First(hSnapShot,&p32)){
        do{
            if(p32.th32ProcessID==pid){
                ParentPID = p32.th32ParentProcessID;
                break;
            }
        }while(Process32Next(hSnapShot,&p32));
    }
    CloseHandle(hSnapShot);

    return ParentPID;

}
/*___________________________________________________________________________________________________
*/
int __stdcall WindowText(HWND hWnd,LPSTR lpString,int nMaxCount){
    int ret;
    ret=SendMessage(hWnd,WM_GETTEXTLENGTH,0,0);
    if(ret){
        ret=SendMessage(hWnd,WM_GETTEXT,nMaxCount,(LPARAM)lpString);
    }
    return ret;
}
/*___________________________________________________________________________________________________
*/
BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam){
    DWORD   dwPID2Find = (DWORD)lParam;
    DWORD   dwPID = 0;

    if(GetWindowLong(hwnd,GWLP_HWNDPARENT) || !IsWindowVisible(hwnd))
        return 1;

    if (GetWindowThreadProcessId(hwnd, &dwPID)){
        if ((dwPID == dwPID2Find) || ( GetParentProcess(dwPID) == dwPID2Find)){
            g_hwnd = hwnd;
            return  (FALSE);
        }
    }

    return  (TRUE);
}
/*___________________________________________________________________________________________________
*/

int main(){
    PROCESS_INFORMATION pi;
    STARTUPINFO si={sizeof(si)};
    TCHAR exename[]=TEXT("write.exe"); // writable buffer (for Unicode bug.)
    if (!CreateProcess(NULL,exename,NULL,    NULL,FALSE, 0,     NULL,NULL,&si,     &pi)){
        return 0;
    }


   //WaitForInputIdle(pi.hProcess, 1000); // this alown will not always work (process may have children)

   // give enough time to process to fully initialize
   // put all in a loop until you
   // get the window handle or timeout.

HWND hwnds = GetHwndFromPID(pi.dwProcessId);
for( int i=0 ;i<1000;i++ ){
    if(hwnds)
        break;
    Sleep(10); 
    hwnds = GetHwndFromPID(pi.dwProcessId);
}

    printf("Process Handle %d, hwnd id: %p ",pi.dwProcessId, hwnds);

    CloseWindow(hwnds);


    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-12
    • 2014-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多