11

我正在寻找一种方法来检查给定窗口是否有任务栏按钮。也就是说,给定一个窗口句柄,如果窗口在任务栏中,我需要一个 TRUE,否则为 FALSE。

相反,我想知道是否有办法获得属于给定任务栏按钮的窗口的句柄,我想这需要一种方法来枚举任务栏按钮。

(前一部分是我需要的部分,后一部分是可选的。)

非常感谢。

4

3 回答 3

11

Windows 使用启发式方法来决定是否将任务栏按钮提供给窗口,有时在它做出决定之前会有延迟,因此 100% 准确地做到这一点将是相当困难的。这是规则的粗略开始。有一些现代风格的标志可以很容易地知道,但是当这些风格缺失时,任务栏就只能猜测了。

首先,您将需要两个窗口样式标志。

LONG Style = GetWindowLong(hwnd, GWL_STYLE);
LONG ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);

现在规则,有三个规则是确定的。

  • 如果ExStyle & WS_EX_APPWINDOW, 那么 TASKBAR
  • 如果ExStyle & WS_EX_TOOLWINDOW, 那么 NOT_TASKBAR
  • 如果Style & WS_CHILD然后 NOT_TASKBAR

剩下的都是猜测:

  • Style & WS_OVERLAPPED建议 TASKBAR
  • Style & WS_POPUP建议 NOT_TASKBAR 特别是如果GetParent() != NULL
  • ExStyle & WS_EX_OVERLAPPEDWINDOW建议 TASKBAR
  • ExStyle & WS_EX_CLIENTEDGE建议 NOT_TASKBAR
  • ExStyle & WS_EX_DLGMODALFRAME建议 NOT_TASKBAR

我确信还有其他的猜测规则,事实上,猜测规则在不同版本的 Windows 中已经发生了变化。

于 2010-02-14T21:10:34.010 回答
7
  1. 顶层窗口

  2. WS_EX_APPWINDOW -> 任务栏,不管其他样式!

  3. OWNER 必须为 NULL (GetWindow(window, GW_OWNER))

  4. 否:WS_EX_NOACTIVATE 或 WS_EX_TOOLWINDOW:

顺序很重要。

第二个问题:在 windows xp/vista 中,可以进入任务栏的进程并获取所有窗口 ID:

void EnumTasklistWindows()
{
  int b2 = 0;
  TBBUTTON tbButton;
  DWORD dwProcessId = 0, dwThreadId = 0;

  HWND hDesktop =::GetDesktopWindow();
  HWND hTray =::FindWindowEx(hDesktop, 0, ("Shell_TrayWnd"), NULL);
  HWND hReBar =::FindWindowEx(hTray, 0, ("ReBarWindow32"), NULL);
  HWND hTask =::FindWindowEx(hReBar, 0, ("MSTaskSwWClass"), NULL);
  HWND hToolbar =::FindWindowEx(hTask, 0, ("ToolbarWindow32"), NULL);

  LRESULT count =::SendMessage(hToolbar, TB_BUTTONCOUNT, 0, 0);
  dwThreadId = GetWindowThreadProcessId(hToolbar, &dwProcessId);

  shared_ptr<void> hProcess (OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId), CloseHandle);
  if (NULL == hProcess.get())
  {
    return;
  }

  memset(&tbButton, 0, sizeof(TBBUTTON));

  for (int i = 0; i < count; i++)
  {
    memset(&tbButton, 0, sizeof(TBBUTTON));

    shared_ptr<void> lpRemoteBuffer (
      VirtualAllocEx(hProcess.get(), NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE), 
      bind<BOOL>(VirtualFreeEx, hProcess.get(), _1, 0, MEM_RELEASE));
    if (NULL == lpRemoteBuffer.get())
    {
      return;
    }

    SendMessage(hToolbar, TB_GETBUTTON, i, (LPARAM) lpRemoteBuffer.get());

    b2 = ReadProcessMemory(hProcess.get(), lpRemoteBuffer.get(),
      (LPVOID) & tbButton, sizeof(TBBUTTON), NULL);
    if (0 == b2)
    {
      continue;
    }

    BYTE localBuffer[0x1000];
    BYTE *pLocalBuffer = localBuffer;
    DWORD_PTR ipLocalBuffer = (DWORD_PTR) pLocalBuffer;
    pLocalBuffer = localBuffer;
    ipLocalBuffer = (DWORD_PTR) pLocalBuffer;
    DWORD_PTR lpRemoteData = (DWORD_PTR) tbButton.dwData;

    ReadProcessMemory(hProcess.get(), (LPVOID) lpRemoteData, (LPVOID) ipLocalBuffer,
      sizeof(DWORD_PTR), NULL);

    HWND windowHandle;
    memcpy(&windowHandle, (void *) ipLocalBuffer, 4);

    if (windowHandle != NULL)
    {
      trace ("adding button: %x\n", windowHandle);
    }
  }
}

这在 Windows 7 上是不可能的了。所以你需要遍历所有顶层窗口。

于 2010-02-14T21:25:27.773 回答
1

这篇 MSDN 文章提供了一些关于 Shell 何时以及为何决定为窗口创建任务栏按钮的有用信息:

每当应用程序创建一个不属于自己的窗口时,Shell 就会在任务栏上创建一个按钮。要确保窗口按钮位于任务栏上,请使用 WS_EX_APPWINDOW 扩展样式创建一个无主窗口。要防止窗口按钮被放置在任务栏上,请使用 WS_EX_TOOLWINDOW 扩展样式创建无主窗口。作为替代方案,您可以创建一个隐藏窗口,并使该隐藏窗口成为可见窗口的所有者。

于 2011-09-26T21:22:06.027 回答