0

After calling Application.AddMessageFilter, the PreFilterMessage method is never called. It's just in a simple WPF application (below). Is there something I've missed here?

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var _filter = new PreMessageFilter();
        Application.AddMessageFilter(_filter);
    }
}

[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public class PreMessageFilter : IMessageFilter
{
    public bool PreFilterMessage(ref Message m)
    {
        Console.WriteLine(m.ToString());
        return true;
    }
}
Paul
  • 9,409
  • 13
  • 64
  • 113

1 Answers1

1

I successfully used IMessageFilter under WinForms but I'm not sure if it can work properly under WPF.

If you need some messages to be intercepted in WPF, there's the other way of doing it.

This way is different from MessageFilter because it can't filter the messages but just listening for Message Loop.

Let's listen WM_KEYDOWN message.

Showing the full App code to make it easy to reproduce

MyMessageHook.cs

using System;
using System.Windows.Input;
using System.Windows.Interop;

namespace WPFMessageHookExample
{
    public class MyKeyEventArgs : EventArgs
    {
        public Key Key { get; private set; }
        public MyKeyEventArgs(Key key) { Key = key; }
    }

    public class MyMessageHook : IDisposable
    {
        private const int WM_KEYDOWN = 0x0100;

        private readonly HwndSourceHook _hook;
        private static HwndSource _hwndSource;

        public event EventHandler<MyKeyEventArgs> KeyDown;
        public MyMessageHook(HwndSource hwndSource)
        {
            _hook = new HwndSourceHook(WndProc);
            _hwndSource = hwndSource ?? throw new ArgumentNullException(nameof(hwndSource));
            _hwndSource.AddHook(_hook);
        }

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch (msg)
            {
                case WM_KEYDOWN:
                    KeyDown?.Invoke(this, new MyKeyEventArgs(KeyInterop.KeyFromVirtualKey((int)wParam)));
                    break;
            }
            return IntPtr.Zero;
        }

        #region IDisposable
        private bool disposed;
        protected virtual void Dispose(bool disposing)
        {
            if (disposed) return;
            if (disposing)
            {
                _hwndSource.RemoveHook(_hook);
            }
            disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        ~MyMessageHook()
        {
            Dispose(false);
        }
        #endregion
    }
}

MainWindow.xaml.cs

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Interop;

namespace WPFMessageHookExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private MyMessageHook messageHook;
        private string _myText;

        public string MyText
        {
            get => _myText;
            set
            {
                _myText = value;
                OnPropertyChanged();
            }
        }
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        private void Window_SourceInitialized(object sender, EventArgs e)
        {
            HwndSource hwnd = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
            messageHook = new MyMessageHook(hwnd);
            messageHook.KeyDown += MessageHook_KeyDown;
        }

        private void MessageHook_KeyDown(object sender, MyKeyEventArgs e)
        {
            MyText += e.Key + ", ";
        }

        private void Window_Closing(object sender, CancelEventArgs e)
        {
            messageHook.Dispose();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

MainWindow.xaml

<Window x:Class="WPFMessageHookExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFMessageHookExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" SourceInitialized="Window_SourceInitialized" Closing="Window_Closing">
    <Grid>
        <TextBox Margin="5" VerticalScrollBarVisibility="Auto" Text="{Binding MyText}" IsReadOnly="True" TextWrapping="Wrap"/>
    </Grid>
</Window>

Note that TextBox is Read-Only.

enter image description here

aepot
  • 4,558
  • 2
  • 12
  • 24
  • This is useful thank you - but I was using the Messagefilter because it also gave me access to know which device it was that the keydown came from. Is that possible with this approach? – Paul Jul 31 '20 at 09:35
  • @Paul how did you do that? Can you show the line of code? Windows Message here has the same field set as in `IMessageFilter`. – aepot Jul 31 '20 at 09:37
  • I might have lied...it may never have worked, this line was just supposed to be suppressing the keydown from elsewhere on the system (it's a barcode scanner). I'm using a class library called RawInput to detect keys from a specific device. – Paul Jul 31 '20 at 09:49
  • @Paul `IMessageFilter` can't supress input to the whole system but only for current app. Btw, in WPF `KeyDown` is `RoutedEvent`. If you handle it in `PreviewKeyDown` Event handler of the `Control` e.g. `TextBox`, it will not go to the standard input handler. Set `e.Handled = true` inside. – aepot Jul 31 '20 at 09:55
  • @Paul here's [some example](https://stackoverflow.com/a/38193799/12888024). – aepot Jul 31 '20 at 09:58
  • Ah alas it needs to be able to pick it up when the app is out of focus. The RawInput does (https://www.codeproject.com/Articles/17123/Using-Raw-Input-from-C-to-handle-multiple-keyboard) but I can't get it to suppress the input as well. Which I thought the messagefilter does, but not in WPF. – Paul Jul 31 '20 at 10:04
  • @Paul `RawInput` will work in WPF because Win32 API is the same here. But yes, it will not supress the input at all. Here I've found [something interesting](https://stackoverflow.com/a/9330103/12888024) but I didn't try it. – aepot Jul 31 '20 at 10:13
  • Thanks - I'll give that a go this afternoon. – Paul Jul 31 '20 at 10:22
  • 1
    @Paul if the answer was helpful, you may mark it accepted. – aepot May 24 '21 at 12:28