題 防止應用程序竊取焦點


是否有任何解決方案可以防止應用程序從活動窗口中竊取焦點?

當我啟動應用程序,切換到執行其他操作並且新應用程序開始接收半個文本句子時,這尤其令人討厭。


178
2017-08-05 08:48


起源


@Ivo Windows 7在我的情況下,但我認為對於SuperUser所有Windows版本都是相關的 - svandragt
主持人合併了這個問題: superuser.com/questions/199821/... 與現在的一個。這是錯誤的,當前問題的答案不適用於Windows 7,所以它不應該合併。到目前為止,我無法在Windows 7中找到解決此問題的方法 - Alex Angelico
這是我用過的每一個GUI的頭號寵物之一。你正在打字和責備,一些混亂的對話框竊取焦點,一半你的擊鍵去其他地方。您認為窗口系統的實現者幾十年前就會想到這一點。如果窗口中有活動,請延遲新窗口的曝光。例如。在最後一次按鈕單擊或當前聚焦窗口中的擊鍵後三到四秒內,不要在GUI上彈出任何內容。衛生署! - Kaz
This is especially annoying when I'm starting an application, switch to do something else and the new application starts receiving half a sentence of text.當一個對話框彈出而你甚至在沒有看到該消息的情況下無意中忽略它時會更加煩人,因為你碰巧按下了 Space 要么 Enter 在打字的時候。 - Synetech
這實際上不僅僅是煩人的,我會說這是一個安全隱患。當您正在輸入密碼並抓住輸入時,沒有什麼可以阻止應用程序彈出。 - Chris Peacock


答案:


沒有這個是不可能的 廣泛 操縱Windows內部,你需要克服它。

在操作系統允許您執行另一個操作之前進行一個操作非常重要,在日常計算機使用中有一些時刻。要做到這一點,它需要將焦點鎖定在某些窗口上。在Windows中,對此行為的控制主要由您使用的各個程序的開發人員決定。

並非每個開發人員在涉及此主題時都做出正確的決定。

我知道這非常令人沮喪和討厭,但是你不能吃蛋糕也不能吃。在您的日常生活中可能存在許多情況,您可以完全將焦點移動到某個UI元素或請求焦點保持鎖定的應用程序。但是,大多數應用程序在確定誰是現在的領導者並且系統永遠不會是完美的時候會有些相同。

不久前,我做了大量研究,一勞永逸地解決了這個問題(並且失敗了)。我的研究結果可以在上面找到 煩惱項目頁面

該項目還包括一個應用程序,通過調用重複嘗試獲取焦點:

switch( message ) {
  case WM_TIMER:
    if( hWnd != NULL ) {
      // Start off easy
      // SetForegroundWindow will not move the window to the foreground,
      // but it will invoke FlashWindow internally and, thus, show the
      // taskbar.
      SetForegroundWindow( hWnd );

      // Our application is awesome! It must have your focus!
      SetActiveWindow( hWnd );

      // Flash that button!
      FlashWindow( hWnd, TRUE );
    }
    break;

正如我們從這個片段中看到的,我的研究也集中在我不喜歡的用戶界面行為的其他方面。

我試圖解決這個問題的方法是將DLL加載到每個新進程中並掛鉤API調用,這會導致另一個窗口被激活。
最後一部分是簡單的部分,這要歸功於令人敬畏的API掛鉤庫。我用的很棒 mhook圖書館

#include "stdafx.h"
#include "mhook-2.2/mhook-lib/mhook.h"

typedef NTSTATUS( WINAPI* PNT_QUERY_SYSTEM_INFORMATION ) ( 
  __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,     
  __inout    PVOID SystemInformation, 
  __in       ULONG SystemInformationLength, 
  __out_opt  PULONG ReturnLength    
);

// Originals
PNT_QUERY_SYSTEM_INFORMATION OriginalFlashWindow   = 
  (PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress( 
  ::GetModuleHandle( L"user32" ), "FlashWindow" );

PNT_QUERY_SYSTEM_INFORMATION OriginalFlashWindowEx = 
  (PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress( 
  ::GetModuleHandle( L"user32" ), "FlashWindowEx" );

PNT_QUERY_SYSTEM_INFORMATION OriginalSetForegroundWindow = 
  (PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress( 
  ::GetModuleHandle( L"user32" ), "SetForegroundWindow" );

// Hooks
BOOL WINAPI
HookedFlashWindow(
  __in  HWND hWnd,
  __in  BOOL bInvert
  ) {
  return 0;
}

BOOL WINAPI 
HookedFlashWindowEx(
  __in  PFLASHWINFO pfwi
  ) {
  return 0;
}

BOOL WINAPI 
HookedSetForegroundWindow(
  __in  HWND hWnd
  ) {
  // Pretend window was brought to foreground
  return 1;
}


BOOL APIENTRY 
DllMain( 
  HMODULE hModule,
  DWORD   ul_reason_for_call,
  LPVOID  lpReserved
  ) {
  switch( ul_reason_for_call ) {
    case DLL_PROCESS_ATTACH:
      Mhook_SetHook( (PVOID*)&OriginalFlashWindow,         HookedFlashWindow );
      Mhook_SetHook( (PVOID*)&OriginalFlashWindowEx,       HookedFlashWindowEx );
      Mhook_SetHook( (PVOID*)&OriginalSetForegroundWindow, HookedSetForegroundWindow );
      break;

    case DLL_PROCESS_DETACH:
      Mhook_Unhook( (PVOID*)&OriginalFlashWindow );
      Mhook_Unhook( (PVOID*)&OriginalFlashWindowEx );
      Mhook_Unhook( (PVOID*)&OriginalSetForegroundWindow );
      break;
  }
  return TRUE;
}

從我當時的測試來看,這很有效。除了將DLL加載到每個新進程的部分。可以想像,沒有什麼可以過於輕率的。我用了 AppInit_DLLs 當時接近(這根本不夠)。

基本上,這很有效。但我從來沒有找到時間寫一些東西 正確 將我的DLL注入新進程。投入的時間在很大程度上掩蓋了焦點竊取給我帶來的煩惱。

除了DLL注入問題之外,還有一種焦點竊取方法,我沒有在Google Code的實現中介紹。一位同事實際上做了一些額外的研究,並涵蓋了這種方法。在SO上討論了這個問題: https://stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus


45
2018-03-24 09:56



你認為你的這個解決方案可以移植到Java嗎?我一直在尋找和提問,但一無所獲。也許我可以在java中導入鉤子庫本身 jne? - Tomáš Zato
@TomášZato:不知道。我自己並沒有積極地使用這個代碼。 - Der Hochstapler
我試圖至少將它編譯為C ++(然後從Java注入/刪除已編譯的DLL)。但這也不太順利。我不想在評論中討論這個問題,但是如果你真的可以幫助我開展工作,我會非常優雅!我創建了一個聊天室,如果我開始工作,我會發布評論如何在這裡這樣做: chat.stackexchange.com/rooms/21637/... - Tomáš Zato


在Windows 7中 ForegroundLockTimeout 不再檢查註冊表項,您可以使用Process Monitor進行驗證。事實上,在Windows 7中,它們禁止您更改前景窗口。去看看 關於它的細節自從Windows 2000以來它甚至出現過。

然而, 文檔很糟糕,他們互相追逐,並找到解決方法

所以,有一些問題正在發生 SetForegroundWindow或類似的API函數......

真正做到這一點的唯一方法是製作一個定期調用的小應用程序 LockSetForegroundWindow,幾乎禁用對我們的錯誤API函數的任何調用。

如果這還不夠(另一個錯誤的API調用?)你可以更進一步做一些 API監控 看看發生了什麼,然後就是你 在每個進程上掛鉤API調用 之後你可以擺脫 任何 打擾前景的電話。然而,具有諷刺意味的是,微軟不鼓勵這樣做......


23
2018-03-22 09:52



有沒有人在Windows 7中具有可重現的用例? 鑑於人們相反經歷相反的情況(例如,我經常發現要求將Windows隱藏在我當前的窗口後面)並且我還沒有看到在Windows 7中發生這種情況,編寫應用程序但是無法進行操作會非常煩人測試一下。此外,正如微軟所說,Windows 7不再發生這種情況。最好的人發現它只能意外切換鍵盤的焦點,這個API調用會解決這個問題,但我不知道如何測試它是否真的有效。 。 - Tom Wijsman
安裝程序(基於InnoSetup)啟動其他進程和可能的其他(隱藏)設置,但我不知道他們所基於的設置創建者。 - Daniel Beck♦
@TomWijsman:打開註冊表,搜索一些無法找到的隨機文本。進入另一個應用程序並開始輸入。搜索完成後,regedit將竊取焦點。 - endolith
@endolith:不可重現,但在這裡使用Windows 8 Replase Preview。你用的是什麼操作系統?在我的情況下,它只是突出顯示底部的應用程序,但不會中斷我的瀏覽... - Tom Wijsman
是的,Win7 Pro 64位。對於提升的流程,焦點竊取甚至更糟,因為它們在不應該的情況下捕獲您按下的<Enter>,並且您告訴它意外地阻塞您的系統。什麼都不應該 曾經 能夠偷走焦點。 - endolith


有一個選項 的TweakUI 這樣做。它可以阻止可疑軟件開發人員使用的大多數常用技巧來強制關注他們的應用程序。

這是一場持續的軍備戰,所以我不知道它是否適用於所有事情。

更新: 根據 EndangeredMassa,TweakUI不適用於Windows 7。


18
2017-08-05 09:12



與Windows 7兼容的tweakui? - frankster
@frankster。不知道,對不起,我懷疑它可能不是。下載並試用。如果您這樣做,請回報每個人都知道。 - Simon P Stevens
即使使用TweakUI設置的註冊表設置也無法在Win7上運行。 - EndangeredMassa
@EndangeredMassa是哪個註冊表項? - n611x007
註冊表項是HKEY_CURRENT_USER \ Control Panel \ Desktop \ ForegroundLockTimeout(以毫秒為單位)。是的,它不再適用於Windows 7。 - foo


我認為可能存在一些混淆,因為有兩種“竊取焦點”的方式:(1)窗口到達前景,(2)窗口接收擊鍵。

這裡提到的問題可能是第二個問題,其中a windows通過將自己置於前台而無需用戶的請求或許可來聲明焦點。

討論必須在XP和7之間分開。

Windows XP

在XP中有 一個註冊表黑客,使XP在防止應用程序竊取焦點方面與Windows 7相同:

  1. 使用regedit轉到: HKEY_CURRENT_USER\Control Panel\Desktop
  2. 雙擊 ForegroundLockTimeout 並將其值設置為十六進制 30d40
  3. 按確定並退出regedit。
  4. 重新啟動PC以使更改生效。

Windows 7的

(以下討論主要適用於XP。)

請理解,Windows無法完全阻止應用程序竊取焦點並保持功能。 例如,如果在文件複製期間,您的防病毒軟件會檢測到可能的威脅 並且如果此窗口被阻止,則想要彈出一個窗口,詢問您要採取的操作 那麼你永遠不會理解為什麼副本永遠不會終止。

在Windows 7中,對Windows本身的行為只有一種可能的修改,即 使用 MS-Windows焦點跟隨鼠標註冊表黑客攻擊,焦點和/或激活始終到光標下的窗口。可以添加延遲以避免應用程序在整個桌面上彈出。
看到這篇文章: Windows 7 - 鼠標懸停使窗口處於活動狀態 - 啟用

否則,必須檢測併中和有罪程序: 如果這始終是獲得焦點的同一應用程序,則此應用程序 被編程為關注並防止這種情況可以通過禁止它從計算機啟動,或使用該應用程序提供的某些設置來避免這種行為。

您可以使用VBS腳本 包括在 VB代碼,用於識別誰在竊取焦點,作者用來識別 罪魁禍首是打印機軟件的“回撥”更新程序。

當一切都失敗時,絕望的措施,如果你已經確定了這個程序錯誤的應用程序, 是為了盡量減少它,並希望不會將自己帶到前線。 通過使用其中一種免費產品,托盤可以採用更強的最小化形式 最佳免費應用程序最小化

絕望的最後一個想法是使用產品虛擬破壞桌面 如 台式機 要么 Dexpot, 並在默認情況下在另一個桌面上工作。

[編輯]

由於微軟退役了Archive Gallery,下面是上面的VB代碼轉載:

Declare Auto Function GetForegroundWindow Lib "user32.dll" () As Integer
Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As Integer, ByRef procid As Integer) As UInteger

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.RichTextBox1.AppendText("Starting up at " & Now & vbCrLf)
    End Sub

    Private Sub GoingAway(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Deactivate, Me.LostFocus

        Dim hwnd As Integer = GetForegroundWindow()
        ' Note that process_id will be used as a ByRef argument
        ' and will be changed by GetWindowThreadProcessId
        Dim process_id As Integer = 1
        GetWindowThreadProcessId(hwnd, process_id)

        If (process_id <> 1) Then
            Dim appExePath As String = Process.GetProcessById(process_id).MainModule.FileName() 
            Me.RichTextBox1.AppendText("Lost focus at " & Now & " due to " & appExePath & vbCrLf)
        Else
            Me.RichTextBox1.AppendText("Lost focus due to unknown cause.")
        End If

    End Sub

14
2018-06-09 09:22



“如果這個窗口被阻止,那麼你永遠不會理解為什麼副本永遠不會終止”這不是真的。正確的行為是通過閃爍的任務欄圖標(或者可能是氣球彈出窗口或烤麵包機通知等)通知用戶。使用截取按鍵的窗口中斷用戶意味著他們告訴防病毒軟件隨機採取一個或另一個動作。絕對不是一個好辦法。 - endolith
“如果這個窗口被阻止,那麼你永遠不會理解為什麼副本永遠不會終止”這不是真的。正確的行為是通過閃爍的任務欄圖標通知用戶......   曾經有一段時間我點擊正在運行的程序中的按鈕或其他東西,導致創建一個新的模態對話框(例如, 打開文件),但在創建對話框之前我切換到另一個程序。結果,對話框被隱藏,另一個程序無法切換到,並且對話框無法被解除。它的任務欄按鈕也沒有 Alt-Tab 作品;只強制對話框到前面。 - Synetech
@Synetech:有時非前置對話框的唯一解決方案就是殺死任務。 Windows中的焦點算法真的很糟糕。 - harrymc
@harrymc,我永遠不必訴諸於殺死其中一個應用程序。我只是運行我的窗口操作程序(WinSpy ++ 訣竅很棒)並將窗口隱藏在前面,然後我可以解除卡住的對話框,然後重新顯示隱藏的窗口。這不方便,但它比殺死任何一個進程更好。 - Synetech
@harrymc,不是真的;殺死一個應用程序和丟失的東西只是讓更多的蒸汽,如果它是一個模態對話框(鎖定父窗口,沒有任務欄按鈕),那麼它將不會出現在 Alt+Tab 列表,並且,根據我的經驗,打開模態對話框的窗口並不總是(從不?)顯示模式對話框 Alt+Tab,特別是如果對話框從未進行過更改以獲得焦點。 :-| - Synetech


Ghacks有一個 解決方案:

它每天發生幾次   一些應用程序竊取了焦點   彈出活動窗口。這個   可能由於多種原因而發生,   當我提取文件或轉移   例如完成。它不是   大部分時間都是這個問題   發生但有時我正在寫一個   文章,它不僅僅意味著   我必須再次輸入一些單詞,但是   我也失去了注意力   必須點擊才能重新獲得焦點。

專業評論員 網站有一個提示   如何防止這種情況發生。   防止焦點的最簡單方法   竊取是使用具有的Tweak UI   一個名為“預防   竊取焦點的應用“。   選中此選項可以防止這種情   其他應用程序突然彈出   竊取你所在窗口的焦點   目前在工作。

這僅適用於應用程序   之前已經被最小化了。 代替   竊取焦點會閃現一個   可以定義的次數   在同一菜單中 調整UI。如果你   不想使用Tweak UI   更改Windows中的設置   註冊。

導航到註冊表項   HKEY_CURRENT_USER>控制面板>   桌面和更改   ForegroundLockTimeout值為30d40   (十六進制)或200000(十進制)。該   關鍵的ForeGroundFlashCount定義了   提醒窗口閃爍的數量   用戶0表示無限制。


2
2017-08-05 09:13



這在XP之後的任何操作系統上都不起作用。該註冊表值已經設置為(默認情況下,我相信)並且無論如何都不起作用。 - EndangeredMassa
僅僅是我在Windows 7(64位)上經歷了焦點竊取(VS 2012最終活躍時,例如),並且上述註冊表建議已經就位。這個答案中的技術確認: superuser.com/a/403554/972 - Michael Paulukonis