C ++使用ShowWindow恢复窗口会禁用其最小化功能

时间:2014-09-12 11:49:47

标签: c++ winapi

我在保持一个窗口运行实例的同时遇到了问题。

实施细节:我有一个C ++应用程序,它在任务栏中显示为一个图标。在图标的双重clik上,我使用ShellExecuteW函数打开一个新的delphi窗口。现在我已经实现了一个逻辑,每当用户双击图标时,它只打开一个窗口实例,避免启动多个窗口。如果用户双击该图标,并且已经打开了一个窗口,它只会将窗口置于前面,或者如果它被最小化,它将恢复窗口。

下面的代码显示了我是如何实现上述逻辑的,它是在双击图标时触发的:

///////////////////////Double Click Code starts/////////////////////////
HWND hWnd = NULL;
HWND hWndFirst = NULL;
DWORD dw = FindProcessId("abc.exe");
if(dw == 0)
{
  //Open new window
  ShellExecuteW(0, L"open", acExePath, acParams, acFullPath, SW_SHOW);
  hWndFirst = NULL;
}
else
{
  //Open existing window
  hWnd = hGetWindowHandleOfProcess(dw);
  if(hWndFirst == NULL) hWndFirst = hWnd;

  if(hWndFirst != hWnd)
  {
    //This is just a small work-around as minimizing the window was changing
    //it's window handle. So I preserve the window handle the first time and 
    //use it whenever the window is minimized (i.e. the handle is changed)
    SetForegroundWindow(hWndFirst);
    ShowWindow(hWndFirst, SW_RESTORE);
  }
  else
  {
    SetForegroundWindow(hWnd);
    ShowWindow(hWnd, SW_RESTORE);
  }
}
///////////////////////Double Click Code ends///////////////////////////

///////////////////////Supporting functions/////////////////////////
struct ProcessHandleData
{
    unsigned long lProcessId;
    HWND hProcessWindowHandle;
};
//Finds the process id when given the process name
DWORD FindProcessId(char* pcProcessName)
{
    char* pcBegin = strrchr(pcProcessName, '\\');
    if(pcBegin)
        pcProcessName = pcBegin+1;

    PROCESSENTRY32 sProcessInfo;
    sProcessInfo.dwSize = sizeof(sProcessInfo);

    HANDLE sProcessesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if ( sProcessesSnapshot == INVALID_HANDLE_VALUE )
        return 0;

    Process32First(sProcessesSnapshot, &sProcessInfo);
    if ( !strcmp(pcProcessName, sProcessInfo.szExeFile) )
    {
        CloseHandle(sProcessesSnapshot);
        return sProcessInfo.th32ProcessID;
    }

    while ( Process32Next(sProcessesSnapshot, &sProcessInfo) )
    {
        if ( !strcmp(pcProcessName, sProcessInfo.szExeFile) )
        {
          CloseHandle(sProcessesSnapshot);
          return sProcessInfo.th32ProcessID;
        }
    }
    CloseHandle(sProcessesSnapshot);
    return 0;
}

//Search predicate for EnumWindows function    
BOOL CALLBACK bEnumWindowsSearcher(HWND handle, LPARAM lParam)
{
    ProcessHandleData& sProcesshandleData = *(ProcessHandleData*)lParam;
    unsigned long lProcessId = 0;
    GetWindowThreadProcessId(handle, &lProcessId);
    if (sProcesshandleData.lProcessId != lProcessId)
      return true;
    sProcesshandleData.hProcessWindowHandle = handle;
    return false;
}

//Gets the window handle of the process (input process id)
HWND hGetWindowHandleOfProcess(unsigned long lProcessId)
{
    ProcessHandleData sProcessHandleData;
    sProcessHandleData.lProcessId = lProcessId;
    sProcessHandleData.hProcessWindowHandle = 0;
    EnumWindows(bEnumWindowsSearcher, (LPARAM)&sProcessHandleData);  //enumerate all windows
    return sProcessHandleData.hProcessWindowHandle;
}

问题: 上面的代码工作正常。但是我面临一个问题。如果打开的窗口未激活或在任何窗口后面,则上面的代码将它带到前面(如预期的那样)。但是如果我最小化窗口并双击图标,它会恢复窗口并将其带到前面(再次按预期方式)。但是在这个动作之后,我无法使用窗口右上角的最小化条来最小化窗口。有些东西会禁用(非物理上)窗口的最小化条。

在这方面,我们将非常感谢Ay的帮助。如果您需要有关此问题的更多信息,请与我们联系。

非常感谢。

PS:上面的代码并不完全是我的,我已经研究了各种网站来获取运行问题的各个部分的代码。

1 个答案:

答案 0 :(得分:2)

问题的根源在于您是以错误的方式从错误的进程中恢复上一个窗口。通过这样做,前一个窗口的状态不同步,这就是最小化停止工作的原因。

相反,设计您的应用程序,以便新实例可以向前一个实例发送自定义窗口消息(或您选择的任何其他形式的IPC),并且响应该消息,前一个实例可以使用{{1}和Application->Restore()正确地恢复自己。

此外,您的整个进程枚举逻辑只是简单的过度杀伤。

尝试更像这样的东西:

SetForegroundWindow()

然后在const UINT uiMyMsg = RegisterWindowMessage(TEXT("MY_RESTORE_MSG")); ... if (!FindProcessId("abc.exe")) { //Open new window ShellExecuteW(0, L"open", acExePath, acParams, acFullPath, SW_SHOW); } else { if (uiMyMsg != 0) PostMessage(HWND_BROADCAST, uiMyMsg, 0, 0); } 内,做这样的事情:

abc.exe