保持窗口顶部,并在WinForms中偷取焦点

我意识到这在正常情况下是完全不好的做法,但这仅仅是一个testing应用程序,需要从条码扫描器(模拟键盘)input。 问题是我需要在扫描时启动一些脚本,所以我需要窗口来重新获得焦点后,我点击脚本来运行它。 我尝试过使用Activate(),BringToFront(),Focus()以及一些Win32调用,如SetForegroundWindow(),Setcapture()和SetActiveWindow()…但是我可以得到他们中的任何一个最好的做法是使任务栏项目开始闪烁告诉我,它想要有焦点,但有些东西在阻止它。 顺便说一句,我在XP SP2上运行这个和使用.NET 2.0。

这可能吗?

编辑:为了澄清,我正在运行脚本在浏览器中双击它们。 所以我需要它从浏览器和testing应用程序窃取焦点。

能见度

使窗口成为“最多”窗口。 这是任务pipe理器可以保持在其他窗口之上的方式。 这是Form一个属性,通过将该值设置为true可以将表单设置为最顶层(浮动在其他窗口之上)。

您不需要用最上面的设置覆盖任何“活动窗口”行为。

焦点

我在StackOverflow中问过类似的问题,答案可以解决你的问题。 您可以使应用程序使用低级别的input钩子并获取来自扫描器的密钥代码的通知。 这样,即使应用程序没有焦点,您的应用程序也始终获取这些键。

您可能需要增强解决scheme来压缩键码,使其不会传输到“对焦”应用程序(例如记事本)。

从Windows 2000开始,没有官方的机制让应用程序在没有用户直接干预的情况下抓住焦点。 通过RawInputDevices钩子在inputstream中窥视是唯一明智的方法。

一些文章可能有帮助(C#实现)

  • 在CodeProject上的RawInput文章
  • RawInput的MSDN文档

我在相当一段时间里也遇到过类似的问题。 经过多次实验和猜测,我就是这样解决的:

 // Get the window to the front. this.TopMost = true; this.TopMost = false; // 'Steal' the focus. this.Activate(); 

我有一个类似的问题,并发现以下伎俩。 从这里适应C#

  // force window to have focus uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero); uint appThread = GetCurrentThreadId(); const uint SW_SHOW = 5; if (foreThread != appThread) { AttachThreadInput(foreThread, appThread, true); BringWindowToTop(form.Handle); ShowWindow(form.Handle, SW_SHOW); AttachThreadInput(foreThread, appThread, false); } else { BringWindowToTop(form.Handle); ShowWindow(form.Handle, SW_SHOW); } form.Activate(); 

编辑:这是C#必要的PInvoke定义:

 [DllImport("user32.dll", SetLastError = true)] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); // When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); [DllImport("kernel32.dll")] static extern uint GetCurrentThreadId(); /// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary> [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach); [DllImport("user32.dll", SetLastError = true)] static extern bool BringWindowToTop(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] static extern bool BringWindowToTop(HandleRef hWnd); [DllImport("user32.dll")] static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow); 

我接触这个问题的方式是产生另一个线程,其唯一目的是确保Form是TopMost并始终关注。 这段代码将使所有其他应用程序在运行时不可用,这是我的特定应用程序所需的。 您可以添加一个睡眠在keepFocus或有其他事件触发它。

 using System.Threading; // be sure to include the System.Threading namespace //Delegates for safe multi-threading. delegate void DelegateGetFocus(); private DelegateGetFocus m_getFocus; //Constructor. myForm() { m_getFocus = new DelegateGetFocus(this.getFocus); // initialise getFocus InitializeComponent(); spawnThread(keepFocus); // call spawnThread method } //Spawns a new Thread. private void spawnThread(ThreadStart ts) { try { Thread newThread = new Thread(ts); newThread.IsBackground = true; newThread.Start(); } catch(Exception e) { MessageBox.Show(e.Message, "Exception!", MessageBoxButtons.OK, MessageBoxIcon.Error); } } //Continuously call getFocus. private void keepFocus() { while(true) { getFocus(); } } //Keeps Form on top and gives focus. private void getFocus() { //If we need to invoke this call from another thread. if (this.InvokeRequired) { try { this.Invoke(m_getFocus, new object[] { }); } catch (System.ObjectDisposedException e) { // Window was destroyed. No problem but terminate application. Application.Exit(); } } //Otherwise, we're safe. else { this.TopMost = true; this.Activate(); } } } 

您可以尝试专注于特定的input,或尝试设置.TopMost属性为true(然后再次取消设置)。

但我怀疑你的问题是,这些方法都只是把消息放在Windows事件队列中,并且你的程序必须等待所有现有事件完成处理,然后才能处理该事件并聚焦应用程序。