【问题标题】:SetForgroundWindow() fails after program was Updated程序更新后 SetForgroundWindow() 失败
【发布时间】:2014-04-01 09:44:51
【问题描述】:

我编写了一个图像匹配 C++ 工具,它是一个通知区域工具(无可见窗口)。它获得了另一个窗口的句柄并将其聚焦,然后进行了保存的图像匹配。现在这个工具正在完善,但程序的制造者(不是我写的)我正在关注推送一个更新,这改变了他们的程序。

因此,我似乎无法再设置程序的焦点(将其置于所有窗口的顶部)。我尝试以管理员身份运行但没有运气。我可以看到任务栏中的图标闪烁橙色,表示它处于活动状态,但窗口不会出现在前台。

这是我的工具的隐形 hwnd:

hwnd =  CreateWindowEx (0, className,
TEXT( "" ),
WS_OVERLAPPEDWINDOW,
0, 0, 
0, 0, 
NULL, NULL, 
hInstance, NULL);

这是我在函数顶部的代码,用于匹配图像:

ShowWindow(handle, SW_SHOWDEFAULT); //maximize handle
SetForegroundWindow(handle); //bring to foreground

我使用 SW_SHOWDEFAULT 而不是 SW_SHOW 和 showWindow,因为这解决了我遇到的一个问题,如果该工具被最小化,它将无法匹配图像,因为窗口被隐藏了。

看起来 SetForegroundWindow() 在之前工作时现在返回 0(失败)。我未能找到有效的在线解决方案。

唯一有效的(有点)是这样的:

ShowWindow(handle, SW_MINIMIZE);
ShowWindow(handle, SW_SHOWDEFAULT);

这似乎是最小化窗口并将其重新打开,从而使其聚焦,但这不是解决方案,因为图像匹配过程应该持续用户想要的时间。

感谢任何帮助!

【问题讨论】:

  • SetForegroundWindow失败时GetLastError()返回的值是多少?你读过msdn.microsoft.com/en-us/library/windows/desktop/ms633539.aspx中的Remarks吗?
  • 我似乎无法从 GetLastError() 得到任何东西,但 SetForegroundWindow 的返回肯定是 0(失败)。我看过评论,不知道我是否失败了。 “Windows 闪烁任务栏”是我得到的暗示我不符合条件。我只是尝试双击通知区域中的图标,而该工具试图在循环中设置前景窗口并且它有效,它设法设置它(因为我发送了最后一个事件?)。有什么想法吗?
  • 此页面上的 Alt-Tab 方法似乎对我有用:codeproject.com/Tips/76427/… 使用此“hack”的含义是什么。如果找不到其他选择,我很想使用它。
  • Window 努力禁止“窃取焦点”,这是有充分理由的。
  • 其中一条规则是,如果您自己不是前台,则不能更改前台窗口。您从未设置窗口前景,这意味着您无权更改前景。解决方案:在隐藏窗口上调用 SetForegroundWindow(现在你有前景并有权更改前景),然后在另一个窗口上调用 SetForegroundWindow。

标签: c++ windows winapi windows-7


【解决方案1】:

让你的应用程序通过RegisterHotKey注册一个(精心挑选的)热键,然后通过SendInput模拟密钥

在处理WM_HOTKEY消息时,你应该能够“窃取焦点”。

// Register the Ctrl+5 (numpad Hot Key)
BOOL BWin32Success = RegisterHotKey( hWnd, 4242, MOD_CONTROL, VK_NUMPAD5 );

[...]

// Later, Emulate the Hot Key
std::vector<INPUT> vInputs;
INPUT OneInput;

OneInput.type = INPUT_KEYBOARD;
OneInput.ki.time = 0;
OneInput.ki.dwExtraInfo = 0;
OneInput.ki.wScan = 0;
OneInput.ki.dwFlags = 0;

OneInput.ki.wVk = VK_CONTROL;
vInputs.push_back( OneInput );
OneInput.ki.wVk = VK_NUMPAD5;
vInputs.push_back( OneInput );
OneInput.ki.dwFlags |= KEYEVENTF_KEYUP;
vInputs.push_back( OneInput );
OneInput.ki.wVk = VK_CONTROL;
vInputs.push_back( OneInput );

// Update a global HWND variable with the target hWnd 
UINT Sent = SendInput( static_cast<UINT>( vInputs.size() ), &vInputs[ 0 ] );

[...]

// WM_HOTKEY Handler (message is posted)
case WM_HOTKEY: {
   if ( wParam == 4242 ) {
      // HERE USE THE GLOBAL "TARGET" HWND WITH APIs
   }
   break;
}

【讨论】:

  • 所以这个解决方案是发送一个 control+7 消息(WM_HOTKEY),然后我的程序将被允许更改前台窗口?我会试试这个,看看它是否有效,与我发布的其他方法相比,它有什么好处。非常感谢:D
  • Control+7 (Num Pad) 确实是个糟糕的选择。我认为这可能会与标准的 Ctrl+Home 混淆。你必须选择别的东西,也许是 Ctrl+5(数字键盘)。好处是,也许:比假鼠标点击更可靠。一定要处理RegisterHotKey失败`(并用另一种组合重试)
【解决方案2】:

当 SetForeground 不起作用时,您需要“窃取焦点”。皱眉,因为其他程序可能依赖于焦点窗口。 基本上,只有当前的前台窗口才能将属于另一个程序的窗口设置为前台。我相信这是在 Vista 中引入的。

请小心使用。

bool ForceToForeground(HWND hWnd)
{
    HWND hForeground = GetForegroundWindow(); 

    int curThread    = GetCurrentThreadId();
    int remoteThread = GetWindowThreadProcessId(hForeground,0);

    AttachThreadInput( curThread, remoteThread, TRUE);
    SetForegroundWindow(hWnd);
    AttachThreadInput( curThread, remoteThread, FALSE);

    return GetForegroundWindow() == hWnd;
}

【讨论】:

    【解决方案3】:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-26
      • 2019-08-27
      • 1970-01-01
      • 2019-12-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多