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

时间:2015-01-14 09:04:19

标签: c# wpf setwindowshookex

我正在尝试使用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窗口变为活动状态。

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

2 个答案:

答案 0 :(得分:0)

我使用标准的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或按钮是否聚焦,它不会改变行为。

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

答案 1 :(得分:0)

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