WPFでWndProcメッセージを処理する方法


112

Windowsフォームでは、をオーバーライドしWndProc、受信したメッセージの処理を開始します。

誰かがWPFで同じことを達成する方法の例を教えてもらえますか?

回答:


62

実際、私が理解している限り、WPFではHwndSourceand を使用することで、そのようなことは確かに可能HwndSourceHookです。例として、MSDNのこのスレッドを参照してください。(以下に含まれる関連コード)

// 'this' is a Window
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));

private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    //  do stuff

    return IntPtr.Zero;
}

現在、WPFアプリケーションでWindowsメッセージングメッセージを処理する理由がよくわかりません(別のWinFormsアプリを操作するための相互運用の最も明白な形式でない限り)。WPFではWinFormsとはデザインイデオロギーとAPIの性質が大きく異なるため、WndProcに相当するものがない理由を正確に理解するには、WPFに慣れることをお勧めします。


48
さて、USBデバイスの(切断)接続イベントはこのメッセージループを介して発生しているようです。そのため、WPFから接続する方法を知ることは悪いことではありません
flq

7
@Noldorin:「デザインイデオロギーとAPIの性質はWPFとWinFormsで大きく異なります...なぜWndProcに相当するものがないのか」という部分を理解するのに役立つ参照(記事/本)を提供していただけますか?
atiyar 2013年

2
WM_MOUSEWHEELたとえば、これらのメッセージを確実にトラップする唯一の方法はWndProc、WPFウィンドウにを追加することでした。これは私にとってMouseWheelEventHandlerはうまくいきましたが、役人は単に期待通りに動かなかったのです。で信頼できる動作を得るには、正しいWPFタキオンを正しく配置できなかったため、MouseWheelEventHandlerに直接アクセスする必要がありましたWndProc
Chris O

4
実際、多くの(ほとんどの)WPFアプリケーションは標準のデスクトップWindowsで実行されます。WPFアーキテクチャがWin32のすべての基本機能を公開しないことを選択することは、Microsoft側の意図的なものですが、それでも対処するのは面倒です。デスクトップWindowsのみを対象とするWPFアプリケーションを構築していますが、@ flqが言及しているようにUSBデバイスと統合しており、デバイス通知を受信する唯一の方法はメッセージループにアクセスすることです。時々抽象化を破ることは避けられない。
NathanAldenSr

1
クリップボードの監視は、WndProcが必要になる可能性がある1つの理由です。もう1つは、メッセージを処理してアプリケーションがアイドル状態でないことを検出することです。
user34660 2018

135

というSystem.Windows.Interop名前のクラスを含む名前空間を介してこれを行うことができますHwndSource

これの使用例

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

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
            source.AddHook(WndProc);
        }

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // Handle messages...

            return IntPtr.Zero;
        }
    }
}

優れたブログ投稿から完全に引用Steve RandsによるWPFアプリでのカスタムWndProcの使用


1
リンクが壊れています。直してもらえますか?
Martin Hennings、2011年

1
@Martin、これはSteve RandのWebサイトが存在しないためです。私が考えることができる唯一の修正はそれを削除することです。サイトが将来戻ってきたとしても付加価値はあると思うので削除しませんが、同意しない場合は自由に編集してください。
Robert MacLean、2011年

ウィンドウなしでWndProcメッセージを受信することは可能ですか?
Mo0gles 2013

8
@ Mo0gles-あなたが尋ねたことについて慎重に考えてください、そうすればあなたはあなたの答えを得るでしょう。
Ian Kemp 14

1
@ Mo0gles画面に描画され、ユーザーに表示されるウィンドウがない場合は?はい。そのため、一部のプログラムには奇妙な空のウィンドウがあり、プログラムの状態が破損した場合に表示されることがあります。
Peter

15
HwndSource src = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
src.AddHook(new HwndSourceHook(WndProc));


.......


public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{

  if(msg == THEMESSAGEIMLOOKINGFOR)
    {
      //Do something here
    }

  return IntPtr.Zero;
}

3

WinFormsを参照してもかまわない場合は、サービスとビューを連動させない、よりMVVM指向のソリューションを使用できます。メッセージを受信できる軽量ウィンドウであるSystem.Windows.Forms.NativeWindowを作成して初期化する必要があります。

public abstract class WinApiServiceBase : IDisposable
{
    /// <summary>
    /// Sponge window absorbs messages and lets other services use them
    /// </summary>
    private sealed class SpongeWindow : NativeWindow
    {
        public event EventHandler<Message> WndProced;

        public SpongeWindow()
        {
            CreateHandle(new CreateParams());
        }

        protected override void WndProc(ref Message m)
        {
            WndProced?.Invoke(this, m);
            base.WndProc(ref m);
        }
    }

    private static readonly SpongeWindow Sponge;
    protected static readonly IntPtr SpongeHandle;

    static WinApiServiceBase()
    {
        Sponge = new SpongeWindow();
        SpongeHandle = Sponge.Handle;
    }

    protected WinApiServiceBase()
    {
        Sponge.WndProced += LocalWndProced;
    }

    private void LocalWndProced(object sender, Message message)
    {
        WndProc(message);
    }

    /// <summary>
    /// Override to process windows messages
    /// </summary>
    protected virtual void WndProc(Message message)
    { }

    public virtual void Dispose()
    {
        Sponge.WndProced -= LocalWndProced;
    }
}

SpongeHandleを使用して関心のあるメッセージを登録し、WndProcをオーバーライドしてそれらを処理します。

public class WindowsMessageListenerService : WinApiServiceBase
{
    protected override void WndProc(Message message)
    {
        Debug.WriteLine(message.msg);
    }
}

唯一の欠点は、System.Windows.Forms参照を含める必要があることですが、それ以外の場合、これは非常にカプセル化されたソリューションです。

これについての詳細はここで読むことができます


1

Behaviorsを使用したWindProcのオーバーライドに関するリンクは次のとおりです:http ://10rem.net/blog/2010/01/09/a-wpf-behavior-for-window-resize-events-in-net-35

[編集:遅くならない方が良い]以下は、上記のリンクに基づく私の実装です。これを再検討しますが、私はAddHook実装の方が好きです。私はそれに切り替えるかもしれません。

私の場合、ウィンドウのサイズがいつ変更されているのかを知りたいと思いました。この実装は、ウィンドウxamlにフックしてイベントを送信します。

using System;
using System.Windows.Interactivity;
using System.Windows; // For Window in behavior
using System.Windows.Interop; // For Hwnd

public class WindowResizeEvents : Behavior<Window>
    {
        public event EventHandler Resized;
        public event EventHandler Resizing;
        public event EventHandler Maximized;
        public event EventHandler Minimized;
        public event EventHandler Restored;

        public static DependencyProperty IsAppAskCloseProperty =  DependencyProperty.RegisterAttached("IsAppAskClose", typeof(bool), typeof(WindowResizeEvents));
        public Boolean IsAppAskClose
        {
            get { return (Boolean)this.GetValue(IsAppAskCloseProperty); }
            set { this.SetValue(IsAppAskCloseProperty, value); }
        }

        // called when the behavior is attached
        // hook the wndproc
        protected override void OnAttached()
        {
            base.OnAttached();

            AssociatedObject.Loaded += (s, e) =>
            {
                WireUpWndProc();
            };
        }

        // call when the behavior is detached
        // clean up our winproc hook
        protected override void OnDetaching()
        {
            RemoveWndProc();

            base.OnDetaching();
        }

        private HwndSourceHook _hook;

        private void WireUpWndProc()
        {
            HwndSource source = HwndSource.FromVisual(AssociatedObject) as HwndSource;

            if (source != null)
            {
                _hook = new HwndSourceHook(WndProc);
                source.AddHook(_hook);
            }
        }

        private void RemoveWndProc()
        {
            HwndSource source = HwndSource.FromVisual(AssociatedObject) as HwndSource;

            if (source != null)
            {
                source.RemoveHook(_hook);
            }
        }

        private const Int32 WM_EXITSIZEMOVE = 0x0232;
        private const Int32 WM_SIZING = 0x0214;
        private const Int32 WM_SIZE = 0x0005;

        private const Int32 SIZE_RESTORED = 0x0000;
        private const Int32 SIZE_MINIMIZED = 0x0001;
        private const Int32 SIZE_MAXIMIZED = 0x0002;
        private const Int32 SIZE_MAXSHOW = 0x0003;
        private const Int32 SIZE_MAXHIDE = 0x0004;

        private const Int32 WM_QUERYENDSESSION = 0x0011;
        private const Int32 ENDSESSION_CLOSEAPP = 0x1;
        private const Int32 WM_ENDSESSION = 0x0016;

        private IntPtr WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, ref Boolean handled)
        {
            IntPtr result = IntPtr.Zero;

            switch (msg)
            {
                case WM_SIZING:             // sizing gets interactive resize
                    OnResizing();
                    break;

                case WM_SIZE:               // size gets minimize/maximize as well as final size
                    {
                        int param = wParam.ToInt32();

                        switch (param)
                        {
                            case SIZE_RESTORED:
                                OnRestored();
                                break;
                            case SIZE_MINIMIZED:
                                OnMinimized();
                                break;
                            case SIZE_MAXIMIZED:
                                OnMaximized();
                                break;
                            case SIZE_MAXSHOW:
                                break;
                            case SIZE_MAXHIDE:
                                break;
                        }
                    }
                    break;

                case WM_EXITSIZEMOVE:
                    OnResized();
                    break;

                // Windows is requesting app to close.    
                // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa376890%28v=vs.85%29.aspx.
                // Use the default response (yes).
                case WM_QUERYENDSESSION:
                    IsAppAskClose = true; 
                    break;
            }

            return result;
        }

        private void OnResizing()
        {
            if (Resizing != null)
                Resizing(AssociatedObject, EventArgs.Empty);
        }

        private void OnResized()
        {
            if (Resized != null)
                Resized(AssociatedObject, EventArgs.Empty);
        }

        private void OnRestored()
        {
            if (Restored != null)
                Restored(AssociatedObject, EventArgs.Empty);
        }

        private void OnMinimized()
        {
            if (Minimized != null)
                Minimized(AssociatedObject, EventArgs.Empty);
        }

        private void OnMaximized()
        {
            if (Maximized != null)
                Maximized(AssociatedObject, EventArgs.Empty);
        }
    }

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:behaviors="clr-namespace:RapidCoreConfigurator._Behaviors"
        Title="name" Height="500" Width="750" BorderBrush="Transparent">

    <i:Interaction.Behaviors>
        <behaviors:WindowResizeEvents IsAppAskClose="{Binding IsRequestClose, Mode=OneWayToSource}"
                                      Resized="Window_Resized"
                                      Resizing="Window_Resizing" />
    </i:Interaction.Behaviors>

    ... 

</Window>

このリンクで質問に答えることができますが、回答の重要な部分をここに含め、参照用のリンクを提供することをお勧めします。リンクされたページが変更されると、リンクのみの回答が無効になる可能性があります。
最大

@max>おそらく今は少し遅れます。
2014年

1
@Rook StackOverflowのレビューサービスが奇妙に動作していると思います。ちょうど20のように正確でした:Here is a link...上記のような答え。
最大

1
@Max少し遅れましたが、関連するコードを含めるように回答を更新しました。
ウェス・

0

組み込みのWin32クラスの「SystemEvents」クラスにアタッチできます。

using Microsoft.Win32;

WPFウィンドウクラス内:

SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
SystemEvents.SessionEnded += SystemEvents_SessionEnded;

private async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
    await vm.PowerModeChanged(e.Mode);
}

private async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
    await vm.PowerModeChanged(e.Mode);
}

private async void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
    await vm.SessionSwitch(e.Reason);
}

private async void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
    if (e.Reason == SessionEndReasons.Logoff)
    {
        await vm.UserLogoff();
    }
}

private async void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e)
{
    if (e.Reason == SessionEndReasons.Logoff)
    {
        await vm.UserLogoff();
    }
}

-1

WPFでWndProcを使用してメッセージを処理する方法(HwndSourceを使用するなど)はありますが、通常、これらの手法は、WPFを介して直接処理できないメッセージとの相互運用のために予約されています。ほとんどのWPFコントロールは、Win32(およびWindows.Formsの拡張)の意味でのウィンドウではないため、WndProcsはありません。


-1 /不正確。WPFフォームがWinFormsではないためWndProc、オーバーライドに公開されていないことは事実ですが、System.Windows.InteropではHwndSourceHwndSource.FromHwndまたはを使用してオブジェクトを取得PresentationSource.FromVisual(someForm) as HwndSourceでき、特別にパターン化されたデリゲートをバインドできます。このデリゲートには、WndProcMessageオブジェクトと同じ引数が多数あります。
Andrew Grey

私は答えでHwndSourceについて言及しますか?確かにトップレベルウィンドウにはHWNDがありますが、ほとんどのコントロールにはないというのは正確です。
Logan Capaldo


-13

簡単に言えば、それはできません。WndProcは、Win32レベルのHWNDにメッセージを渡すことで機能します。WPFウィンドウにはHWNDがないため、WndProcメッセージに参加できません。基本WPFメッセージループはWndProcの上にありますが、コアWPFロジックからそれらを抽象化します。

HWndHostを使用して、WndProcで取得できます。しかし、これはほぼ間違いなくあなたがやりたいことではありません。ほとんどの場合、WPFはHWNDおよびWndProcで動作しません。ほとんどの場合、ソリューションはWndProcではなくWPFを変更することに依存しています。


13
「WPFウィンドウにはHWNDがありません」-これは単に正しくありません。
スコットソルマー2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.