7

我有一个在后台运行的 C# winforms 应用程序,监听要按下的热键。当按下热键时,我的表单会短暂出现。表单始终在运行,但在我收到热键事件之前设置为隐藏,此时我将可见属性设置为 true。代码如下所示:

void hook_volumeDown(object sender, KeyPressedEventArgs e)
{
    this.Visible = true;
}

需要注意的是,这个表单的topmost属性设置为true。

真正奇怪的是,在我的 C# 应用程序从另一个应用程序中窃取焦点后,它再也不会这样做了。例如:我启动我的应用程序,然后启动一些全屏应用程序,例如 Team Fortress 2。然后我按下我的热键。军团要塞 2 最小化,我看到了我的表格。但是,然后,我可以恢复 TF2,并再次按我想要的所有热键(具有所需的效果),TF2 将保持专注。

无论如何,我正在寻找解决此问题的方法。我在这里发现了很多涉及类似问题的问题,但所有这些问题都与创建/启动新表单有关,而不是使现有表单可见(除非我错过了什么)。每次我需要一个新表单时,我都可以重新设计应用程序以创建一个新表单,但这需要创建另一个始终不可见的表单,只是为了等待热键事件,所以我宁愿保持原样。

有任何想法吗?

4

2 回答 2

7

我认为您的问题与 Visible = true 在第一次和后续调用之间的行为不同这一事实有关。第一次调用 visible 并且尚未创建窗口句柄时,通过调用 CreateWindowEx 创建窗口,它具有一些控制窗口应如何表现的样式参数。我认为您需要确保使用样式 WS_EX_NOACTIVATE 创建窗口,您可以通过覆盖 CreateParams 来做到这一点。

其他要尝试的事情:

1) ShowWindow 函数(由 Visible = true 使用)在第一次调用时忽略焦点参数(http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx)如果该程序提供了一个 STARTUPINFO 结构。深入研究反射器并找出 Form 类是否提供了 STARTUPINFO 结构,如果是,如何操作它。

2) Form 有一个 ShowWithoutActivation 属性,可以被覆盖并设置为 true,你覆盖了吗?

很抱歉“没有确切的答案”,但我希望这至少能为您提供一些进一步调查的起点。祝你好运。

于 2010-05-15T08:38:26.933 回答
1

看到在您的函数中使用 KeyPressedEventArgs 看起来真的很奇怪。热键可以通过 P/Invoking RegisterHotKey() API 函数来实现。当按下热键时,它会向您的窗口发送一条消息。这是一个在启动时不可见的表单示例,当您按下热键时会活跃起来。Ctrl+Alt+U 在这种情况下:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1 {
    public partial class Form1 : Form {
        private const int MYKEYID = 0;    // In case you want to register more than one...
        public Form1() {
            InitializeComponent();
            this.FormClosing += (s, args) => UnregisterHotKey(this.Handle, MYKEYID);
        }
        protected override void SetVisibleCore(bool value) {
            if (value && !this.IsHandleCreated) {
                this.CreateHandle();
                RegisterHotKey(this.Handle, MYKEYID, MOD_CONTROL + MOD_SHIFT, Keys.U);
                value = false;
            }
            base.SetVisibleCore(value);
        }
        protected override void WndProc(ref Message m) {
            if (m.Msg == WM_HOTKEY && m.WParam.ToInt32() == MYKEYID) {
                this.Visible = true;
                if (this.WindowState == FormWindowState.Minimized)
                    this.WindowState = FormWindowState.Normal;
                SetForegroundWindow(this.Handle);
            }
            base.WndProc(ref m);
        }
        // P/Invoke declarations
        private const int WM_HOTKEY = 0x312;
        private const int MOD_ALT = 1;
        private const int MOD_CONTROL = 2;
        private const int MOD_SHIFT = 4;
        [DllImport("user32.dll")]
        private static extern int RegisterHotKey(IntPtr hWnd, int id, int modifier, Keys vk);
        [DllImport("user32.dll")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
    }
}

请注意, SetForegroundWindow() 函数是问题,可能也是您在问题中描述的问题的根源。当用户正在使用另一个窗口时,Windows 不允许应用程序将窗口推到用户面前。在允许窗口窃取焦点之前,必须至少有几秒钟的不活动状态。使用给定的代码,很容易看到,表单的任务栏按钮将闪烁。避免将 ShowInTaskbar 属性设置为 false。使用此代码没有必要这样做,任务栏按钮在按下热键之前不会显示。

于 2010-05-15T14:25:52.987 回答