我意识到在正常情况下这将是完全不好的做法,但这仅适用于需要从条形码扫描仪(模拟键盘)输入的测试应用程序.问题是我需要在扫描时启动一些脚本,所以我需要窗口在我单击脚本运行它之后直接重新获得焦点.我尝试过使用Activate(),BringToFront(),Focus()以及一些Win32调用,如SetForegroundWindow(),Setcapture()和SetActiveWindow()...但是我能得到的最好的就是让任务栏项目开始闪烁告诉我它想要有焦点,但有些东西正在阻止它.顺便说一下,我在XP SP2上运行它并使用.NET 2.0.
这可能吗?
编辑:为了澄清,我通过在资源管理器中双击它们来运行脚本.所以我需要它从探险家和测试应用程序窃取焦点.
我在相当长的一段时间内遇到了类似的问题.经过多次实验和猜测,这就是我解决它的方法:
// Get the window to the front. this.TopMost = true; this.TopMost = false; // 'Steal' the focus. this.Activate();
使窗口成为"最顶层"窗口.这是任务管理器可以保持在其他窗口之上的方式.这是a的属性,Form
您可以通过将值设置为最顶层(浮动在其他窗口上方)true
.
您不应该使用最顶层的设置覆盖任何"活动窗口"行为.
我之前在StackOverflow上问了一个类似的问题,答案可以解决你的问题.您可以使应用程序使用低级输入挂钩,并获取来自扫描仪的密钥代码的通知.这样,即使应用程序没有焦点,您的应用程序也始终会获取这些键.
您可能需要增强解决方案以压缩密钥代码,以便它们不会传输到"对焦"应用程序(例如记事本).
从Windows 2000开始,没有官方机制让应用程序在没有用户直接干预的情况下获取焦点.通过RawInputDevices钩子查看输入流是唯一明智的方法.
一些文章可能有所帮助(C#实现)
关于CodeProject的RawInput文章
RawInput的MSDN文档
我遇到了类似的问题,发现以下内容可以解决问题.从这里适应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(); ///The GetForegroundWindow function returns a handle to the foreground window. [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中添加Sleep,或者让其他事件触发它.
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(); } } }