WPF에서 WndProc 메시지를 처리하는 방법은 무엇입니까?


112

Windows Forms에서는을 재정의 WndProc하고 메시지가 들어 오자마자 처리를 시작했습니다.

누군가가 WPF에서 동일한 작업을 수행하는 방법에 대한 예를 보여줄 수 있습니까?

답변:


62

사실, 내가 이해하는 한 WPF에서 HwndSourceHwndSourceHook. 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의 디자인 이데올로기와 API의 특성은 WinForms와 매우 다릅니다. 따라서 WndProc에 해당하는 것이없는 이유를 정확히 알아 보려면 WPF에 더 익숙해 지시기 바랍니다 .


48
음, USB 장치 (연결 끊기) 이벤트가이 메시지 루프를 통해 오는 것처럼 보이므로 WPF에서 연결하는 방법을 아는 것은 나쁘지 않습니다
flq

7
@Noldorin : "디자인 이데올로기와 API의 특성이 WinForms와 WPF에서 매우 다릅니다 .... WndProc에 상응하는 것이없는 이유"부분을 이해하는 데 도움이되는 참고 자료 (기사 / 책)를 제공해 주시겠습니까?
atiyar

2
WM_MOUSEWHEEL예를 들어 이러한 메시지를 안정적으로 트랩하는 유일한 방법 WndProc은 WPF 창 에를 추가하는 것입니다. 이것은 나를 위해 일했지만 공식은 MouseWheelEventHandler단순히 예상대로 작동하지 않았습니다. 신뢰할 수있는 동작을 얻기 위해 올바른 WPF 타키온을 정렬 할 수 없었기 MouseWheelEventHandler때문에 WndProc.
Chris O

4
사실 대부분의 WPF 응용 프로그램은 표준 데스크톱 Windows에서 실행됩니다. WPF 아키텍처가 Win32의 모든 기본 기능을 노출하지 않기로 선택한 것은 Microsoft 측에서 의도적이지만 처리하기에는 여전히 성가신 일입니다. 데스크톱 Windows 만 대상으로하지만 @flq가 언급 한대로 USB 장치와 통합되는 WPF 응용 프로그램을 만들고 있으며 장치 알림을받는 유일한 방법은 메시지 루프에 액세스하는 것입니다. 때때로 추상화를 깨는 것은 불가피합니다.
NathanAldenSr

1
클립 보드를 모니터링하는 것은 WndProc이 필요한 이유 중 하나입니다. 또 다른 방법은 메시지를 처리하여 응용 프로그램이 유휴 상태가 아님을 감지하는 것입니다.
user34660

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의 웹 사이트가 더 이상 존재하지 않기 때문입니다. 내가 생각할 수있는 유일한 해결책은 그것을 제거하는 것입니다. 나중에 사이트가 다시 돌아 오더라도 여전히 가치가 있다고 생각하므로 제거하지 않을 것입니다. 동의하지 않으면 자유롭게 편집하십시오.
Robert MacLean 2011 년

창없이 WndProc 메시지를받을 수 있습니까?
Mo0gles 2013 년

8
@ Mo0gles-질문에 대해 신중하게 생각하면 답변을 얻을 수 있습니다.
Ian Kemp

1
@ Mo0gles 화면에 그려지고 사용자에게 보이는 창이 없습니까? 예. 그렇기 때문에 일부 프로그램에는 프로그램 상태가 손상되면 가끔 보이는 이상한 빈 Windows가 있습니다.
피터

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 구현이 더 좋습니다. 나는 그것으로 전환 할 수 있습니다.

제 경우에는 창 크기가 조정되는시기와 몇 가지 다른 사항을 알고 싶었습니다. 이 구현은 Window 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

@max> 아마 조금 늦었을 것입니다.
Rook

1
@Rook 나는 StackOverflow의 리뷰 서비스가 이상하게 행동한다고 ​​생각합니다 Here is a link....
Max

1
@Max 조금 늦었지만 관련 코드를 포함하도록 답변을 업데이트했습니다.
Wes

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 양식이 더 노출 된, 따라서 윈폼하지 않습니다, 그리고 것은 사실이지만 WndProc대체하려면는 System.Windows.Interop당신이 얻을 수 HwndSource의 방법으로 개체를 HwndSource.FromHwnd또는 PresentationSource.FromVisual(someForm) as HwndSource당신이 특수 패턴 대리자를 결합 할 수 있음. 이 대리자는 WndProcMessage 개체 와 동일한 인수가 많이 있습니다.
Andrew Gray

대답에서 HwndSource를 언급합니까? 확실히 최상위 창에는 HWND가 있지만 대부분의 컨트롤이 그렇지 않다고 말하는 것이 여전히 정확합니다 .
Logan Capaldo


-13

짧은 대답은 할 수 없다는 것입니다. WndProc는 Win32 수준의 HWND에 메시지를 전달하는 방식으로 작동합니다. WPF 창에는 HWND가 없으므로 WndProc 메시지에 참여할 수 없습니다. 기본 WPF 메시지 루프는 WndProc 위에 위치하지만 핵심 WPF 논리에서 추상화합니다.

HWndHost를 사용하고 WndProc에서 얻을 수 있습니다. 그러나 이것은 당신이 원하는 것이 거의 확실하지 않습니다. 대부분의 경우 WPF는 HWND 및 WndProc에서 작동하지 않습니다. 솔루션은 거의 확실히 WndProc이 아닌 WPF에서 변경하는 데 의존합니다.


13
"WPF 창에는 HWND가 없습니다."-이것은 사실이 아닙니다.
Scott Solmer 2014
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.