简体   繁体   English

WINUI中如何hook WndProc

[英]How to hook WndProc in WINUI

Over the years, I've hooked the WndProc in everything from VB3 to C# in WinForms with no issue, but WINUI-3 and C# is giving me problems.多年来,我在 WinForms 中从 VB3 到 C# 的所有内容中都使用了 WndProc,没有任何问题,但是 WINUI-3 和 C# 给我带来了问题。

My DLL Imports are:我的 DLL 导入的是:


        /// <summary>
        /// Sets window data.
        /// </summary>
        /// <param name="hWnd">The handle of the window to set.</param>
        /// <param name="nIndex">The index of the item to set.</param>
        /// <param name="dwNewLong">The new value.</param>
        /// <returns></returns>
        [DllImport("user32.dll",
            EntryPoint = "SetWindowLongPtr",
            CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, IntPtr dwNewLong);

        // Defines our window WndProcDelegate.
        private delegate IntPtr WndProcDelegate(IntPtr hwnd, uint message, IntPtr wParam, IntPtr lParam);

        // This handles calling the underlying base WndProc.
        [DllImport("user32.dll", 
            CallingConvention = CallingConvention.Cdecl,
            CharSet=CharSet.Auto)]
        static extern IntPtr CallWindowProc(WndProcDelegate lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

The actual subclass/unsubclass etc, look like this:实际的子类/非子类等,看起来像这样:

 public IntPtr GetHwnd()
        {
            IntPtr handle = WinRT.Interop.WindowNative.GetWindowHandle(this.m_MainWindow);
            return handle;
        }

// Subclass the window.
        public void Subclass(object window)
        {
            if (this.m_isSubclassed)
            {
                return;
            }

            this.m_MainWindow = window;
            this.m_NewWndProcDelegate = new WndProcDelegate(this.ReplacementWndProc);
            IntPtr hWnd = this.GetHwnd();
            HandleRef handleRef = new(this, hWnd);
            IntPtr newDelegatePtr = Marshal.GetFunctionPointerForDelegate(this.m_NewWndProcDelegate);
            IntPtr oldDelegatePtr = SetWindowLongPtr64(handleRef, GWLP_WNDPROC, newDelegatePtr);
            this.m_OldWndProcDelegate = (WndProcDelegate)Marshal.GetDelegateForFunctionPointer(
                oldDelegatePtr,
                typeof(WndProcDelegate));
            this.m_isSubclassed = true;
        }

// Unsubclass the window.
        public void Unsubclass()
        {
            if (!this.m_isSubclassed)
            {
                return;
            }
            IntPtr hWnd = this.GetHwnd();
            SetWindowLongPtr64(new HandleRef(this, hWnd), GWLP_WNDPROC, Marshal.GetFunctionPointerForDelegate(this.m_OldWndProcDelegate));
            this.m_isSubclassed = false;
        }

        public bool IsSubclassed()
        {
            return this.m_isSubclassed;
        }


        /// <summary>
        /// This is the replacement WndProc.
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="message"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private IntPtr ReplacementWndProc(IntPtr hwnd, uint message, IntPtr wParam, IntPtr lParam)
        {
            // Our custom Windows messages code...
            /*if (message == WM_COPYDATA)
            {
                CopyDataStruct copyStruct = (CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(CopyDataStruct));
                string messageText = Utf8PtrToString(copyStruct.lpData);
                int cbData = copyStruct.cbData;
                // Do something with the messageText and cbData.
            }*/

            // Finally, call the original WndProc.
            return CallWindowProc(this.m_OldWndProcDelegate, hwnd, message, wParam, lParam);
        }

The issue I have is a nebulous SystemEngineException (-2146233082) error, with all null information.我遇到的问题是一个模糊的 SystemEngineException (-2146233082) 错误,包含所有 null 信息。

So, something has clearly gone wrong in my implementation.所以,我的实施显然出了问题。 Is there a tried/tested way to do this with WINUI?在 WINUI 中是否有一种经过验证的方法可以做到这一点?

For anyone wondering why I'm trying to do this, I need to handle a WM_COPYDATA message.对于任何想知道我为什么要这样做的人,我需要处理 WM_COPYDATA 消息。

Many thanks in advance,提前谢谢了,

Jason杰森

There's a blog article describing this exact task, and error message.有一篇博客文章描述了这个确切的任务和错误消息。

In that case the author was using SetWindowLongPtr to storing a pointer to the result of GetFunctionPointerForDelegate , and calling it after C# has detected the delegate is unreachable.在那种情况下,作者使用SetWindowLongPtr来存储指向GetFunctionPointerForDelegate结果的指针,并在 C# 检测到委托不可访问后调用它。

You are keeping the delegate alive by storing it to a member variable.您通过将委托存储到成员变量来使委托保持活动状态。 GCHandle.Alloc would also have worked. GCHandle.Alloc也可以。 So that's fine... at least in the Subclass function.所以这很好……至少在Subclass function 中是这样。

However, your Unsubclass is broken.但是,您的Unsubclass已损坏。 You seem to have assumed that GetFunctionPointerForDelegate is an inverse function for GetDelegateForFunctionPointer .您似乎假设GetFunctionPointerForDelegate是 GetDelegateForFunctionPointer 的逆GetDelegateForFunctionPointer It's not, it creates a brand new trampoline linked to the managed delegate instance.不是,它创建了一个链接到托管委托实例的全新蹦床。 You need to preserve the original oldDelegatePtr and put it back.您需要保留原始的oldDelegatePtr并将其放回去。

In fact, you have no reason to use GetDelegateForFunctionPointer at all, since the old wndproc is only ever passed back to WinApi functions.事实上,您根本没有理由使用GetDelegateForFunctionPointer ,因为旧的 wndproc 只会传回 WinApi 个函数。 Just leave it as an IntPtr .只需将其保留为IntPtr

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM