取消

System.ComponentModel.Win32Exception (0x80004005): 无效的窗口句柄。

在 WPF 获取鼠标当前坐标的时候,可能会得到一个异常:System.ComponentModel.Win32Exception:“无效的窗口句柄。”

本文解释此异常的原因和解决方法。


异常

获取鼠标当前相对于元素 element 的坐标的代码:

1
var point = Mouse.GetPosition(element);

或者,还有其他的代码:

1
2
var point1 = e.PointFromScreen(new Point());
var point2 = e.PointToScreen(new Point());

如果在按下窗口关闭按钮的时候调用以上代码,则会引发异常:

1
2
3
System.ComponentModel.Win32Exception (0x80004005): 无效的窗口句柄。
   at Point MS.Internal.PointUtil.ClientToScreen(Point pointClient, PresentationSource presentationSource)
   at Point System.Windows.Input.MouseDevice.GetScreenPositionFromSystem()

原因

将窗口上的点转换到控件上的点的方法是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// <summary>
///     Convert a point from "client" coordinate space of a window into
///     the coordinate space of the screen.
/// </summary>
/// <SecurityNote>
///     SecurityCritical: This code causes eleveation to unmanaged code via call to GetWindowLong
///     SecurityTreatAsSafe: This data is ok to give out
///     validate all code paths that lead to this.
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
public static Point ClientToScreen(Point pointClient, PresentationSource presentationSource)
{
    // For now we only know how to use HwndSource.
    HwndSource inputSource = presentationSource as HwndSource;
    if(inputSource == null)
    {
        return pointClient;
    }
    HandleRef handleRef = new HandleRef(inputSource, inputSource.CriticalHandle);

    NativeMethods.POINT ptClient            = FromPoint(pointClient);
    NativeMethods.POINT ptClientRTLAdjusted = AdjustForRightToLeft(ptClient, handleRef);

    UnsafeNativeMethods.ClientToScreen(handleRef, ptClientRTLAdjusted);

    return ToPoint(ptClientRTLAdjusted);
}

最关键的是 UnsafeNativeMethods.ClientToScreen,此方法要求窗口句柄依然有效,然而此时窗口已经关闭,句柄已经销毁。

解决

本文会经常更新,请阅读原文: https://blog.walterlv.com/post/win32exception.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected])

登录 GitHub 账号进行评论