it-swarm.asia

Mencegah aplikasi dari mencuri fokus

Apakah ada solusi untuk mencegah aplikasi mencuri fokus dari jendela aktif?

Ini sangat menjengkelkan ketika saya memulai aplikasi, beralih untuk melakukan sesuatu yang lain dan aplikasi baru mulai menerima setengah kalimat teks.

190
svandragt

Ini tidak dimungkinkan tanpa ekstensif manipulasi Windows internal dan Anda harus mengatasinya.

Ada saat-saat dalam penggunaan komputer harian ketika sangat penting bagi Anda untuk melakukan satu tindakan sebelum sistem operasi memungkinkan Anda melakukan hal lain. Untuk melakukan itu, Anda perlu mengunci fokus Anda pada jendela tertentu. Di Windows, kontrol atas perilaku ini sebagian besar diserahkan kepada pengembang program individual yang Anda gunakan.

Tidak semua pengembang membuat keputusan yang tepat dalam hal topik ini.

Saya tahu ini sangat menyebalkan dan menyebalkan, tetapi Anda tidak dapat memiliki kue dan memakannya juga. Mungkin ada banyak kasus sepanjang kehidupan sehari-hari Anda di mana Anda baik-baik saja dengan fokus dipindahkan ke elemen UI tertentu atau aplikasi yang meminta agar fokus tetap terkunci di sana. Tetapi sebagian besar aplikasi agak sama ketika memutuskan siapa yang memimpin saat ini dan sistem tidak akan pernah sempurna.

Beberapa waktu yang lalu saya melakukan penelitian yang luas untuk menyelesaikan masalah ini sekali dan untuk semua (dan gagal). Hasil penelitian saya dapat ditemukan di halaman proyek gangguan .

Proyek ini juga mencakup aplikasi yang berulang kali mencoba meraih fokus dengan menelepon:

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;

Seperti yang dapat kita lihat dari cuplikan ini, penelitian saya juga difokuskan pada aspek lain dari perilaku antarmuka pengguna yang tidak saya sukai.

Cara saya mencoba menyelesaikan ini adalah dengan memuat DLL ke setiap proses baru dan menghubungkan panggilan API yang menyebabkan windows lain diaktifkan.
Bagian terakhir adalah yang mudah, terima kasih kepada perpustakaan pengait API yang mengagumkan di luar sana. Saya menggunakan pustaka mhook yang sangat bagus :

#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;
}

Dari tes saya saat itu, ini bekerja dengan baik. Kecuali untuk bagian yang memuat DLL ke dalam setiap proses baru. Seperti yang bisa dibayangkan, itu bukan masalah. Saya menggunakan pendekatan AppInit_DLLs saat itu (yang sama sekali tidak cukup).

Pada dasarnya, ini bekerja dengan baik. Tapi saya tidak pernah menemukan waktu untuk menulis sesuatu yang dengan benar menyuntikkan DLL saya ke dalam proses baru. Dan waktu yang diinvestasikan dalam hal ini sebagian besar menutupi kekesalan yang mencuri perhatian saya.

Selain masalah injeksi DLL, ada juga metode mencuri fokus yang tidak saya bahas dalam implementasi pada Google Code. Seorang rekan kerja sebenarnya melakukan riset tambahan dan membahas metode itu. Masalahnya dibahas pada SO: https://stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus

51
Der Hochstapler

Di Windows 7, entri registri ForegroundLockTimeouttidak lagi dicentang, Anda dapat memverifikasi ini dengan Monitor Proses. Bahkan, di Windows 7 mereka melarang Anda mengubah jendela latar depan. Pergi dan baca tentang perinciannya , bahkan sudah ada sejak Windows 2000.

Namun, dokumentasinya payah dan mereka saling mengejar dan menemukan jalan keluarnya .

Jadi, ada sesuatu yang bermasalah dengan SetForegroundWindowNAME _ , atau fungsi API serupa ...

Satu-satunya cara untuk benar-benar melakukan ini dengan benar adalah membuat aplikasi kecil yang secara berkala memanggil LockSetForegroundWindowNAME _ , secara virtual menonaktifkan semua panggilan ke fungsi API kereta kami.

Jika itu tidak cukup (panggilan API kereta lain?) Anda dapat melangkah lebih jauh dan melakukan beberapa Pemantauan API untuk melihat apa yang terjadi, dan kemudian Anda cukup kaitkan panggilan API pada setiap proses setelah itu Anda dapat menyingkirkan panggilan apa saja yang mengacaukan latar depan . Namun, ironisnya, ini tidak disarankan oleh Microsoft ...

23
Tamara Wijsman

Ada opsi di TweakUI yang melakukan ini. Ini mencegah sebagian besar trik biasa yang digunakan pengembang perangkat lunak yang meragukan untuk memaksa fokus pada aplikasi mereka.

Ini adalah perang senjata yang sedang berlangsung, jadi saya tidak tahu apakah itu bekerja untuk semuanya.

Pembaruan : Menurut EndangeredMassa , TweakUI tidak berfungsi pada Windows 7.

18
Simon P Stevens

Saya percaya bahwa beberapa kebingungan mungkin ada, karena ada dua cara "mencuri fokus": (1) jendela datang ke latar depan, dan (2) jendela menerima penekanan tombol.

Masalah yang dimaksud di sini mungkin yang kedua, di mana jendela mengklaim fokus dengan membawa dirinya sendiri ke latar depan - tanpa permintaan atau izin pengguna.

Diskusi harus dibagi di sini antara XP dan 7.

Windows XP

Di XP ada peretasan registri yang membuat XP bekerja sama dengan Windows 7 dalam mencegah aplikasi mencuri fokus:

  1. Gunakan regedit untuk membuka: HKEY_CURRENT_USER\Control Panel\Desktop.
  2. Klik dua kali pada ForegroundLockTimeout dan tetapkan nilainya dalam heksadesimal menjadi 30d40.
  3. Tekan OK dan keluar dari regedit.
  4. Nyalakan ulang PC Anda agar perubahan diterapkan.

Windows 7

(Diskusi di bawah ini sebagian besar berlaku untuk XP juga.)

Harap mengerti bahwa tidak ada cara di mana Windows dapat benar-benar memblokir aplikasi dari mencuri fokus dan tetap berfungsi. Misalnya, jika selama file-copy anti-virus Anda mendeteksi kemungkinan ancaman dan ingin memunculkan jendela yang meminta Anda untuk mengambil tindakan, jika jendela ini diblokir maka Anda tidak akan pernah mengerti mengapa salinan tidak pernah berakhir.

Di Windows 7 hanya ada satu modifikasi yang mungkin untuk perilaku Windows itu sendiri, yaitu menggunakan MS-Windows focus-following-mouse hacks Registry , di mana fokus dan/atau aktivasi selalu menuju ke jendela di bawah kursor. Penundaan dapat ditambahkan untuk menghindari aplikasi yang muncul di seluruh desktop.
Lihat artikel ini: Windows 7 - Mouse Hover Membuat Jendela Aktif - Aktifkan .

Kalau tidak, kita harus mendeteksi dan menetralisir program yang bersalah: Jika ini selalu aplikasi yang sama yang mendapatkan fokus, maka aplikasi ini diprogram untuk mengambil fokus dan mencegah hal ini dapat dilakukan dengan menonaktifkannya dari memulai dengan komputer, atau gunakan beberapa pengaturan yang disediakan oleh aplikasi itu untuk menghindari perilaku ini.

Anda dapat menggunakan skrip VBS yang termasuk dalam Kode VB yang mengidentifikasi siapa yang mencuri fokus , yang digunakan penulis untuk mengidentifikasi pelakunya sebagai "pemanggil rumah" pembaru untuk sebuah perangkat lunak printer.

Langkah putus asa ketika semua yang lain gagal, dan jika Anda telah mengidentifikasi aplikasi yang tidak terprogram ini, adalah untuk meminimalkannya dan berharap itu tidak akan muncul. Bentuk minimalisasi yang lebih kuat adalah ke baki dengan menggunakan salah satu produk gratis yang tercantum dalam Pengecil Aplikasi Gratis Terbaik .

Gagasan terakhir dalam urutan keputus-asaan adalah mematahkan desktop Anda secara virtual dengan menggunakan produk seperti Desktops atau Dexpot , dan lakukan pekerjaan Anda di desktop lain selain default.

[EDIT]

Karena Microsoft telah pensiun dari Arsip Arsip, berikut adalah kode VB yang direproduksi:

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
harrymc

Terinspirasi oleh Jawaban Der Hochstapler , saya memutuskan untuk menulis injektor DLL, yang bekerja dengan proses 64 dan 32-bit dan mencegah mencuri fokus pada Windows 7 atau lebih baru: https://blade.sk/stay-focused/

Cara kerjanya adalah ia mengawasi windows yang baru dibuat (menggunakan SetWinEventHook) dan menyuntikkan DLL sangat mirip dengan Der Hochstapler ke dalam proses jendela jika belum ada. Ini membongkar DLL dan mengembalikan fungsionalitas asli saat keluar.

Dari pengujian saya, ini bekerja dengan sangat baik sejauh ini. Namun, masalah ini tampaknya lebih dalam dari sekadar aplikasi yang memanggil SetForegroundWindow. Misalnya ketika jendela baru dibuat, itu secara otomatis dibawa ke latar depan, yang juga mengganggu pengguna mengetik ke jendela lain.

Untuk menangani metode mencuri fokus lainnya, diperlukan lebih banyak pengujian dan saya menghargai umpan balik tentang skenario di mana itu terjadi.

2
blade

Ghack memiliki solusi yang memungkinkan:

Ini terjadi beberapa kali sehari bahwa beberapa aplikasi mencuri fokus jendela aktif dengan muncul. Ini bisa terjadi karena sejumlah alasan, ketika saya mengekstrak file atau transfer selesai misalnya. Tidak masalah sebagian besar waktu ketika ini terjadi tetapi kadang-kadang saya menulis artikel dan itu tidak hanya berarti bahwa saya harus mengetik beberapa kata lagi tetapi juga bahwa saya kehilangan konsentrasi dan harus mengklik untuk mendapatkan kembali fokus.

Situs web Peninjau Pro memiliki tip tentang cara mencegah hal ini terjadi. Cara termudah untuk mencegah mencuri fokus adalah dengan menggunakan Tweak UI yang memiliki pengaturan yang disebut "Mencegah aplikasi dari mencuri fokus". Memeriksa opsi ini mencegah aplikasi lain muncul tiba-tiba dan mencuri fokus jendela yang sedang Anda gunakan.

Ini hanya berfungsi ketika aplikasi telah diminimalkan sebelumnya. Alih-alih mencuri fokus itu akan berkedip beberapa kali yang dapat didefinisikan dalam menu yang sama di Tweak UI . Jika Anda tidak ingin menggunakan UI Tweak, Anda dapat mengubah pengaturan di Windows Registry.

Buka kunci Registry HKEY_CURRENT_USER> Control Panel> Desktop dan ubah nilai ForegroundLockTimeout menjadi 30d40 (Hexadecimal) atau 200000 (Decimal). Kunci ForeGroundFlashCount menentukan jumlah blitz jendela untuk mengingatkan pengguna di mana 0 berarti tidak terbatas.

2
Ivo Flipse

Saya menemukan cara untuk menghentikan TaskBar dari mem-flash jendela target yang baru diaktifkan setelah Anda secara terprogram mengaktifkan, memaksimalkan, dan memfokuskan jendela utama proses itu dari proses lain. Pertama-tama, ada banyak batasan apakah operasi ini akan diizinkan.

"Sistem membatasi proses mana yang dapat mengatur jendela latar depan. Suatu proses dapat mengatur jendela latar depan hanya jika salah satu dari kondisi berikut ini benar:

  • Prosesnya adalah proses foreground.
  • Proses dimulai oleh proses latar depan.
  • Proses menerima input acara terakhir.
  • Tidak ada proses foreground.
  • Proses latar depan sedang di-debug.
  • Foreground tidak dikunci (lihat LockSetForegroundWindow).
  • Batas waktu kunci latar depan telah kedaluwarsa (lihat SPI_GETFOREGROUNDLOCKTIMEOUT di SystemParametersInfo).
  • Tidak ada menu yang aktif.

https://docs.Microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-allowsetforegroundwindow

Jadi jika proses mengendalikan ada di latar depan, ia dapat sementara mengaktifkan proses lain untuk sepenuhnya mencuri latar depan dengan memanggil AllowSetForegroundWindow dengan id proses dari proses target. Kemudian setelah itu, proses target dapat memanggil SetForegroundWindow itu sendiri, menggunakan pegangan jendela sendiri, dan itu akan berfungsi.

Jelas, ini memerlukan beberapa koordinasi antara kedua proses, tetapi itu berhasil, dan jika Anda melakukan ini untuk menerapkan aplikasi instance tunggal yang mengarahkan ulang semua klik Explorer meluncurkan ke instance aplikasi yang ada, maka Anda sudah akan memiliki (misalnya) pipa bernama untuk mengoordinasikan hal-hal.

0
Glenn Slayden