【问题标题】:Win32: Bring a window to topWin32:将窗口置于顶部
【发布时间】:2010-10-29 07:28:49
【问题描述】:

我有一个 Windows 程序,其中有两个 2 个窗口:

hwnd (main interface)

hwnd2 (toplevel window, no parent, created by hwnd)

当我双击hwnd时,我需要hwnd2弹出并显示一些数据,所以我使用这个功能将hwnd2拉到顶部:

BringWindowToTop(hwnd2);

hwnd2 被带到顶部,但有一点很奇怪。当我再次点击 hwnd2 时,hwnd(主界面)会自动再次弹出。 我尝试使用以下函数来解决这个问题,但它们都不起作用。

SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
                                                                  //doesn't work

BringWindowToTop(hwnd2);    //This is the function brings hwnd2 to top

SetForegroundWindow(hwnd2); //doesn't work

SetWindowPos(hwnd2, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); 
                                                                  //doesn't work

SetWindowPos(hwnd2, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
                                       // hwnd2 "always" on top, not what I want

SetActiveWindow(hwnd2); // doesn't work too (for replying to Magnus Skog, thanks)

SwitchToThisWindow(hwnd2, TRUE);// got the same problem with BringWindowToTop function
SwitchToThisWindow(hwnd2, FALSE);

我该如何解决这个问题? 提前致谢。

(回复aJ,hwnd2没有父窗口,因为它需要是顶层窗口,所以它可以在其他窗口的前面/后面)

(hwnd2是一个媒体播放器,由几个窗口组成,其中一个窗口用于视频显示,另外两个用于进度条和音量条的轨迹栏控件,一个用于控制面板的工具栏控件。)

(有一个可能有帮助,无论我点击哪个窗口hwnd2,hwnd都会自动弹出,就像“鼠标在z-order在hwnd的顶部”一样,包括菜单栏和非客户区,等等)

(这个媒体播放器是用Direct Show写的。我用IVideoWindow::put_Owner把video window作为video owner,Direct Show内部创建一个sub-video window作为video window的child。除了这个sub-我看不到源代码的视频窗口,我在 hwnd2 中没有看到任何可疑的东西。)

我找到了原因,这是因为 Direct Show。我使用多线程执行它,然后问题就解决了。但是……为什么??

这个问题可以通过使用 PostMessage(而不是 SendMessage)来解决。

【问题讨论】:

  • hwnd2 没有父母的任何具体原因? hwnd 可以在创建时设置为父级。
  • 你在hwnd2的鼠标按键处理代码中做了什么?有什么可疑的地方吗?
  • 那么你是如何使用 PostMessage 解决的?

标签: c++ winapi visual-c++


【解决方案1】:

试试这个,据说来自M$

    HWND hCurWnd = ::GetForegroundWindow();
    DWORD dwMyID = ::GetCurrentThreadId();
    DWORD dwCurID = ::GetWindowThreadProcessId(hCurWnd, NULL);
    ::AttachThreadInput(dwCurID, dwMyID, TRUE);
    ::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    ::SetWindowPos(m_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
    ::SetForegroundWindow(m_hWnd);
    ::SetFocus(m_hWnd);
    ::SetActiveWindow(m_hWnd);
    ::AttachThreadInput(dwCurID, dwMyID, FALSE);

为了让一个窗口置顶,你应该得到你的窗口句柄,线程句柄,在前台的窗口线程句柄

然后我们将我们的线程附加到前台窗口线程并通过 AttachThreadInput 获取输入,然后我们设置我们的窗口 z 顺序 到最顶层,然后将其 z 顺序恢复正常,调用 SetForegroundWindow,SetFocus,SetActiveWindow 以确保我们的窗口被带到顶部并处于活动状态并具有焦点

然后从旧的前台窗口线程中分离输入队列,使我们的线程成为唯一捕获输入事件的线程

那么我们为什么要调用AttachThreadInput,因为

SetFocus 将键盘焦点设置到指定窗口。窗户必须是 附加到调用线程的消息队列。

AttachThreadInput 有什么作用?

AttachThreadInput 函数可用于允许一组线程 共享相同的输入状态。通过共享输入状态,线程 分享他们对活动窗口的概念。通过这样做,一个线程 总是可以激活另一个线程的窗口。这个功能也是 用于共享焦点状态、鼠标捕获状态、键盘状态、 和不同线程创建的窗口之间的窗口 Z 顺序状态 其输入状态是共享的。

我们使用 SetWindowPos 将窗口带到最顶层,如果窗口被隐藏,我们使用 SWP_HIDEWINDOW 显示窗口

SetWindowPos 函数改变一个窗口的大小、位置和 Z 顺序 子窗口、弹出窗口或顶级窗口。这些窗口是有序的 根据他们在屏幕上的样子。最上面的窗口 获得最高排名并且是 Z 顺序中的第一个窗口

如果你的问题是你的窗口也被最小化了,你应该在最后添加一行代码

ShowWindow(m_hWnd, SW_RESTORE);

【讨论】:

  • 我一直在寻找这种神奇的歌舞。它非常适合我。
  • 我仍然无法理解它的作用,但可以正常工作
  • 希望我也能这么说。没有为我工作。对话框获得焦点,但另一个对话框仍位于其前面。
  • 当我想在 win7 中将最小化窗口置于顶部时不适合我。它只是不将最小化窗口带到顶部。如果没有最小化窗口,它可以将窗口带到顶部。 SetFocus api返回错误码0x57“参数不正确。”以我的win7为例。我不知道为什么这段代码会忽略所有 windows api 错误。
  • 没有windows api源码,不好说
【解决方案2】:

两者都很好用:

::SetForegroundWindow(wnd)

::SetWindowPos(m_hWnd,       // handle to window
            HWND_TOPMOST,  // placement-order handle
            0,     // horizontal position
            0,      // vertical position
            0,  // width
            0, // height
            SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE// window-positioning options
            );

但请记住,最后一个设置窗口始终位于顶部。

【讨论】:

  • 我可能有点晚了,但您必须在显示窗口之前致电::SetForegroundWindow(确保同时更新窗口)。
【解决方案3】:

经过多次尝试和错误。我找到了解决此问题的以下方法:

SendMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); // restore the minimize window
SetForegroundWindow(hwnd); 
SetActiveWindow(hwnd); 
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE  | SWP_NOSIZE);
//redraw to prevent the window blank.
RedrawWindow(hwnd, NULL, 0, RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN );

hwnd 是您的窗口 HWND 。请不要只是复制和粘贴。您还需要在每次 api 调用后使用GetLastError 来检查 api 错误。

我在win7上确认了以下结果:

  • 可以恢复最小化窗口且不返回错误。
  • 如果窗口已经置顶,窗口标题会闪烁并且不返回错误。
  • 如果窗口已经关闭,会返回错误“0x578 Invalid window handle”。
  • 它可以将所有非最顶层窗口的窗口置顶,并且不返回错误。(例如它会在最顶层的任务管理器后面)
  • 它不会使窗口位于最顶部。用户可以在其上制作其他窗口。

【讨论】:

    【解决方案4】:

    SwitchToThisWindow 最适合我。

    【讨论】:

    • MSDN:此功能已弃用,不适合一般用途。建议您不要在新程序中使用它,因为它可能会在后续版本的 Windows 中被更改或不可用。
    【解决方案5】:

    你试过SetActiveWindow()吗?

    【讨论】:

      【解决方案6】:

      如果已最小化,这将恢复应用并将其置于最前面:

      ShowWindow(hWnd, SW_SHOW);
      SetForegroundWindow(hWnd);
      

      【讨论】:

        【解决方案7】:

        //工作得很好!

        Var
         WndHandle:HWND;
        
        begin
         WndHandle :=FindWindowEx(0,0,nil,'Calculator');
         PostMessage(WndHandle,WM_SHOWWINDOW,SW_RESTORE,0);
         SetForegroundWindow(WndHandle);
        end; 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-11
          • 2015-07-19
          • 1970-01-01
          相关资源
          最近更新 更多