当 WPF 应用程序的控件聚焦时捕获活动窗口标题失败



我正在尝试使用 SetWindowsHookEx 添加到我的时间跟踪器窗口标题跟踪中,但它只能部分工作。这是我用来为订阅者提供共同响应事件的代码:

public class Hooks
{
    #region DllImport
    delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
    [DllImport("user32.dll")]
    static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
    private const uint WINEVENT_SKIPOWNPROCESS = 2;
    private const uint WINEVENT_SKIPOWNTHREAD = 1;
    private const uint WINEVENT_OUTOFCONTEXT = 0;
    private const uint EVENT_SYSTEM_FOREGROUND = 3;
    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")]
    static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); 
    #endregion DllImport
    public static event EventHandler<EventArgs<string>> WindowActivated;
    public void Bind()
    {
        var listener = new WinEventDelegate(EventCallback);
        var result = SetWinEventHook(EVENT_SYSTEM_FOREGROUND,
                        EVENT_SYSTEM_FOREGROUND,
                        IntPtr.Zero,
                        listener,
                        0,
                        0,
                        (WINEVENT_OUTOFCONTEXT));
    }
    private static void EventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
                                      int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
    {
        System.Diagnostics.Debug.Write("EventCallback enter!");
        if (eventType == EVENT_SYSTEM_FOREGROUND)
        {
            var buffer = new StringBuilder(300);
            var handle = GetForegroundWindow();
            System.Diagnostics.Debug.Write(string.Format("EventCallback GetWindowText: {0}, '{1}'",
                GetWindowText(handle, buffer, 300),
                buffer));
            if (GetWindowText(handle, buffer, 300) > 0 && WindowActivated != null)
            {
                System.Diagnostics.Debug.Write(string.Format(
                    "Calling handlers for WindowActivated with buffer='{0}'",
                    buffer));
                WindowActivated(handle, new EventArgs<string>(buffer.ToString()));
            } 
        }
        System.Diagnostics.Debug.Write("EventCallback leave!");
    }
}

我有具有单个Textbox的主要 UI WPF 应用程序,我将 Hook 的事件绑定到文本框内容。当我运行它时,它看起来工作正常,直到它聚焦。即,如果我单击WPF窗口的texbox变为活动钩子,则会停止工作约30-40秒。之后,事件捕获成为工作,但再次 - 直到主 WPF 窗口变为活动状态。

任何想法是什么以及如何解决它?

我使用标准的Visual Studio模板(VS2012; .NET 4.5)创建了一个新的WPF应用程序。然后,我替换了 XAML 和代码隐藏,如下所示:

MainWindow.xaml

<Window x:Class="stackoverflowtest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow">
    <Grid>
        <StackPanel VerticalAlignment="Top" Margin="15">
            <TextBox x:Name="_tb" />
            <Button Content="Does Nothing" HorizontalAlignment="Left" Margin="0,15" />
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace stackoverflowtest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            _hooks = new Hooks(_tb);
            _hooks.Bind();
        }
        readonly Hooks _hooks;
    }
    public class Hooks
    {
        public Hooks(TextBox textbox)
        {
            _listener = EventCallback;
            _textbox = textbox;
        }
        readonly WinEventDelegate _listener;
        readonly TextBox _textbox;
        IntPtr _result;
        public void Bind()
        {
            _result = SetWinEventHook(
                EVENT_SYSTEM_FOREGROUND,
                EVENT_SYSTEM_FOREGROUND,
                IntPtr.Zero,
                _listener,
                0,
                0,
                WINEVENT_OUTOFCONTEXT
                );
        }
        void EventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild,
            uint dwEventThread, uint dwmsEventTime)
        {
            var windowTitle = new StringBuilder(300);
            GetWindowText(hwnd, windowTitle, 300);
            _textbox.Text = windowTitle.ToString();
        }
        #region P/Invoke
        const uint WINEVENT_OUTOFCONTEXT = 0;
        const uint EVENT_SYSTEM_FOREGROUND = 3;
        [DllImport("user32.dll")]
        static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc,
            WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
        [DllImport("user32.dll")]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
        delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject,
            int idChild, uint dwEventThread, uint dwmsEventTime);
        #endregion
    }
}

对我来说,这很好用。我可以在不同的应用程序之间切换,它们的窗口标题反映在 WPF 应用程序的文本框中。即使文本框聚焦,也不会有延迟。我添加了一个无所事事Button控件,只是为了证明无论 texbox 还是按钮聚焦,它都不会改变行为。

我只能假设您遇到的问题与您在事件处理程序中执行的操作有关,或者失败了某种线程问题。

原因可能是委托被垃圾回收,因为它被分配到局部变量中。这就是为什么它在 30-40 秒后停止工作的原因。

最新更新