WPF'de WndProc mesajları nasıl işlenir?


112

Windows Forms'da, yalnızca geçersiz kılar WndProcve gelen iletileri işlemeye başlardım .

Birisi bana aynı şeyi WPF'de nasıl başaracağıma dair bir örnek gösterebilir mi?

Yanıtlar:


62

Aslında, anladığım kadarıyla böyle bir şey WPF'de HwndSourceve kullanarak gerçekten mümkün HwndSourceHook. Örnek olarak MSDN'deki bu ileti dizisine bakın . (İlgili kod aşağıdadır)

// '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;
}

Şimdi, neden bir WPF uygulamasında Windows Mesajlaşma mesajlarını işlemek isteyeceğinizden tam olarak emin değilim (başka bir WinForms uygulamasıyla çalışmak için en belirgin birlikte çalışma biçimi değilse). API'nin tasarım ideolojisi ve doğası, WPF'de WinForms'tan çok farklıdır, bu nedenle tam olarak neden WndProc'un eşdeğeri olmadığını görmek için WPF'ye daha fazla aşina olmanızı öneririm .


48
USB Aygıtı (dis) bağlantısı olayları bu mesaj döngüsünden geliyor gibi görünüyor, bu nedenle
WPF'den

7
@Noldorin: "API'nin tasarım ideolojisi ve yapısı WPF'de WinForms'tan çok farklı, ... neden WndProc'un eşdeğeri yok" bölümünü anlamama yardımcı olabilecek referanslar (makaleler / kitaplar) sağlayabilir misiniz?
atiyar

2
WM_MOUSEWHEELörneğin, bu mesajları güvenilir bir şekilde yakalamanın tek yolu WndProc, bir WPF penceresine eklemekti . Bu benim için işe yaradı, oysa görevli MouseWheelEventHandlerbeklendiği gibi çalışmadı. Güvenilir davranış elde etmek için doğru şekilde sıralanan doğru WPF takyonlarını alamadım MouseWheelEventHandler, bu nedenle WndProc.
Chris O

4
Gerçek şu ki, birçok (çoğu?) WPF uygulaması standart masaüstü Windows'ta çalıştırılıyor. WPF mimarisinin Win32'nin tüm temel yeteneklerini açığa çıkarmamayı seçmesi, Microsoft açısından kasıtlı, ancak yine de başa çıkmak için can sıkıcı. Yalnızca masaüstü Windows'u hedefleyen, ancak @flq'da belirtildiği gibi USB cihazlarıyla entegre olan bir WPF uygulaması oluşturuyorum ve cihaz bildirimlerini almanın tek yolu mesaj döngüsüne erişmek. Bazen soyutlamayı bozmak kaçınılmazdır.
NathanAldenSr

1
Panoyu izlemek, WndProc'a ihtiyaç duymamızın bir nedenidir. Bir diğeri, mesajları işleyerek uygulamanın boşta olmadığını tespit etmektir.
user34660

135

Bunu, System.Windows.Interopadlandırılmış bir sınıfı içeren ad alanı aracılığıyla yapabilirsiniz HwndSource.

Bunu kullanma örneği

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;
        }
    }
}

Tamamen mükemmel blog gönderisinden alınmıştır: Steve Rands tarafından WPF uygulamalarında özel bir WndProc kullanma


1
Bağlantı koptu. Lütfen düzeltebilir misin?
Martin Hennings

1
@Martin, çünkü Steve Rand'ın web sitesi artık mevcut değil. Aklıma gelen tek düzeltme onu kaldırmaktır. Site gelecekte geri gelirse yine de değer katacağını düşünüyorum, bu yüzden onu kaldırmayacağım - ancak katılmıyorsanız düzenlemekten çekinmeyin.
Robert MacLean

WndProc mesajlarını penceresiz almak mümkün mü?
Mo0gles

8
@ Mo0gles - ne sorduğunuzu dikkatlice düşünün ve cevabınızı alacaksınız.
Ian Kemp

1
@ Mo0gles Ekranda çizilen ve kullanıcı tarafından görülebilen bir pencere olmadan mı? Evet. Bu nedenle, bazı programlarda, programın durumu bozulduğunda bazen görünür hale gelen garip boş Windows vardır.
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'a başvurmaktan çekinmiyorsanız, servisi görünümle birleştirmeyen daha MVVM odaklı bir çözüm kullanabilirsiniz. İletileri alabilen hafif bir pencere olan System.Windows.Forms.NativeWindow oluşturmanız ve başlatmanız gerekir.

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;
    }
}

İlgilendiğiniz mesajlara kaydolmak için SpongeHandle'ı kullanın ve ardından bunları işlemek için WndProc'u geçersiz kılın:

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

Tek dezavantajı, System.Windows.Forms referansını dahil etmeniz gerektiğidir, ancak aksi takdirde bu çok kapsüllenmiş bir çözümdür.

Bununla ilgili daha fazla bilgiyi buradan okuyabilirsiniz


1

Davranışları kullanarak WindProc'u geçersiz kılmaya ilişkin bir bağlantı: http://10rem.net/blog/2010/01/09/a-wpf-behavior-for-window-resize-events-in-net-35

[Düzenleme: hiç olmamasından iyidir geç] Aşağıda, yukarıdaki bağlantıya dayalı uygulamam verilmiştir. Bunu tekrar ziyaret etsem de AddHook uygulamalarını daha çok beğeniyorum. Ben buna geçebilirim.

Benim durumumda, pencerenin ne zaman yeniden boyutlandırıldığını ve birkaç başka şeyi bilmek istedim. Bu uygulama Window xaml'e bağlanır ve olayları gönderir.

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>

Bu bağlantı soruyu cevaplayabilirken, cevabın temel kısımlarını buraya eklemek ve referans için bağlantıyı sağlamak daha iyidir. Bağlantılı sayfa değişirse, yalnızca bağlantı yanıtları geçersiz hale gelebilir.
Maksimum

@max> bunun için muhtemelen biraz geç.
Rook

1
@Rook Bence StackOverflow'un inceleme hizmeti garip davranıyor, sadece 20 tane tam Here is a link...cevap aldım: yukarıdaki gibi.
Maksimum

1
@Max Biraz geç ama cevabımı ilgili kodu içerecek şekilde güncelledim.
Wes

0

Yerleşik Win32 sınıfının 'SystemEvents' sınıfına ekleyebilirsiniz:

using Microsoft.Win32;

bir WPF pencere sınıfında:

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'de bir WndProc ile mesajları işlemenin yolları vardır (örneğin, bir HwndSource kullanarak, vb.), Ancak genellikle bu teknikler, WPF aracılığıyla doğrudan işlenemeyen mesajlarla birlikte çalışmak için ayrılmıştır. Çoğu WPF denetimi Win32 (ve uzantı olarak Windows.Forms) anlamında pencere bile değildir, bu nedenle WndProcs'a sahip olmazlar.


-1 / Hatalı. WPF formlarının WinForm olmadığı ve bu nedenle WndProcgeçersiz kılmaya maruz kalmadığı doğru olsa da , özel olarak biçimlendirilmiş bir temsilciye bağlanabileceğiniz veya yoluyla System.Windows.Interopbir HwndSourcenesne almanıza olanak tanır . Bu temsilci, bir Message nesnesiyle aynı bağımsız değişkenlerin çoğuna sahiptir . HwndSource.FromHwndPresentationSource.FromVisual(someForm) as HwndSourceWndProc
Andrew Gray

Cevapta HwndSource'tan bahsettim mi? Kesinlikle en üst seviye pencerenizde bir HWND olacaktır, ancak çoğu kontrolün olmadığını söylemek yine de doğrudur .
Logan Capaldo


-13

Kısa cevap, yapamazsınız. WndProc, iletileri Win32 düzeyinde bir HWND'ye ileterek çalışır. WPF pencerelerinde HWND yoktur ve bu nedenle WndProc mesajlarına katılamaz. Temel WPF mesaj döngüsü WndProc'un üstüne oturur, ancak onları çekirdek WPF mantığından soyutlar.

Bir HWndHost kullanabilir ve bunun için bir WndProc'a girebilirsiniz. Ancak bu neredeyse kesinlikle yapmak istediğiniz şey değil. Çoğu amaç için, WPF, HWND ve WndProc üzerinde çalışmaz. Çözümünüz neredeyse kesinlikle WndProc'da değil, WPF'de bir değişiklik yapmaya dayanır.


13
"WPF pencerelerinde HWND yoktur" - Bu kesinlikle doğru değildir.
Scott Solmer
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.