[英]How to hook WndProc in WINUI
多年来,我在 WinForms 中从 VB3 到 C# 的所有内容中都使用了 WndProc,没有任何问题,但是 WINUI-3 和 C# 给我带来了问题。
我的 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);
实际的子类/非子类等,看起来像这样:
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);
}
我遇到的问题是一个模糊的 SystemEngineException (-2146233082) 错误,包含所有 null 信息。
所以,我的实施显然出了问题。 在 WINUI 中是否有一种经过验证的方法可以做到这一点?
对于任何想知道我为什么要这样做的人,我需要处理 WM_COPYDATA 消息。
提前谢谢了,
杰森
有一篇博客文章描述了这个确切的任务和错误消息。
在那种情况下,作者使用SetWindowLongPtr
来存储指向GetFunctionPointerForDelegate
结果的指针,并在 C# 检测到委托不可访问后调用它。
您通过将委托存储到成员变量来使委托保持活动状态。 GCHandle.Alloc
也可以。 所以这很好……至少在Subclass
function 中是这样。
但是,您的Unsubclass
已损坏。 您似乎假设GetFunctionPointerForDelegate
是 GetDelegateForFunctionPointer 的逆GetDelegateForFunctionPointer
。 不是,它创建了一个链接到托管委托实例的全新蹦床。 您需要保留原始的oldDelegatePtr
并将其放回去。
事实上,您根本没有理由使用GetDelegateForFunctionPointer
,因为旧的 wndproc 只会传回 WinApi 个函数。 只需将其保留为IntPtr
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.