C# 检测光标是否隐藏

4

我知道这类问题已经有很多人提问了,但是它们对我都没有起作用。

我需要检查系统光标是否隐藏。

我尝试过使用Cursor.Current == null,但是它从来没有做过任何事情。(我在全屏的 Youtube、Discord 和 Krita 中隐藏了光标)。

我还尝试使用user32.dll中的GetCursorInfo并检查标志是否为0(隐藏),但仍然没有结果。

[StructLayout( LayoutKind.Sequential )]
struct POINT {
    public Int32 x;
    public Int32 y;
}

[StructLayout( LayoutKind.Sequential )]
struct CURSORINFO {
    public Int32 cbSize;        

    public Int32 flags;         

    public IntPtr hCursor;
    public POINT ptScreenPos;
}

[DllImport( "user32.dll" )]
static extern bool GetCursorInfo ( out CURSORINFO pci );

public static bool CursorHidden () {
    CURSORINFO cinfo;
    cinfo.cbSize = Marshal.SizeOf( typeof( CURSORINFO ) );
    GetCursorInfo( out cinfo );
    return cinfo.flags == 0;
}

我的目标是在光标隐藏时隐藏屏幕覆盖UI界面。

那么,我该如何检查系统光标是否以其他任何方式隐藏?

编辑:

好的,借助Ahmed Abdelhameed的帮助,我发现了一个问题。基本上,浏览器等应用程序不会渲染光标,但它仍然被系统视为可见。但是,处理句柄已更改。系统默认提供有限数量的句柄(Cursors.<Name>.Handle),而不可见的浏览器光标没有使用其中任何一个句柄。

enum CursorTypes { ... }
...
CURSORINFO cinfo;
... // see code above
Cursor[] cursors = new Cursor[] { ... }; // listed all of them in the same order as CursorTypes
int type = 1;
foreach ( Cursor cursor in cursors ) {
    if ( cinfo.hCursor == cursor.Handle ) {
        return (CursorTypes)type;
    }
    type++;
}
return CursorTypes.Other;

然而,这并不是完整的解决方案,因为浏览器和其他软件可能会创建除了隐形之外的新类型光标(例如在VS中向后倾斜),这也将返回CursorTypes.Other

正如Herohtar所指出的那样,隐藏的光标通常具有较大的指针值(在我的情况下,Math.Abs(pointer) > 10000000 大部分时间都有效)。这涵盖了大多数浏览器。

我真的认为我们在检测隐藏光标方面已经不能做更多了。在极少数情况下,如果光标没有被隐藏但我的应用程序认为它已隐藏或相反,我决定只提供一个选项让用户在设置中点击一个按钮,在一段时间后将当前光标句柄添加到例外中。


@Angelo,这与光标是否隐藏有何关系?我的窗口是透明的、可以点击的覆盖整个屏幕的叠加层。 - Peri
3
大多数程序隐藏光标时,CURSORINFO 结构体的 flagshCursor(句柄)值应该都为零。然而,当光标被浏览器隐藏时,flags 的值似乎不会改变(仍为1),并且 hCursor 的值被设置为一个自定义句柄,每个浏览器的句柄似乎都不同。我不确定是否有一种方法可以检测到这一点。如果我找到了什么,我会告诉你。 - 41686d6564 stands w. Palestine
1
网页大多使用CSS(即cursor: none)来暂时“隐藏”光标。 cursor: none的作用是告诉浏览器将光标渲染为不可见。浏览器似乎是通过使用自定义(不可见)光标来实现这一点的。换句话说,实际的操作系统光标仍然存在并且有一个句柄。还有其他方法可以通过Web应用程序隐藏光标,实际上删除操作系统光标,但这不是cursor: none的作用... - 41686d6564 stands w. Palestine
2
作为一种解决方法,您可以获取常见浏览器的不可见光标句柄,并将其与 CURSORINFO 的句柄进行比较,_每当活动窗口是浏览器时_。虽然这显然很麻烦且不完美,但希望有人能提出更好的解决方案。 - 41686d6564 stands w. Palestine
1
查看我的系统值时,“常规”光标处理程序的值为655396554165555。然而,当浏览器隐藏光标时,我会得到像-977859735 (0xFFFFFFFFC5B70B69)(64位指针)这样的值。我不确定那是否是有效的指针,但即使它是,它与通常的值完全不同,似乎应该能够检查类似于那样极其不同的东西? - Herohtar
显示剩余5条评论
1个回答

0

检测光标是否隐藏并不只有一种方法。有时,系统会被告知不要渲染它,有时软件本身不会渲染它,有时它会呈现为不是光标的东西(例如数字艺术画笔轮廓)。

在最常见的情况下,当系统本身不渲染光标时,您只需要检查光标标志是否为0(隐藏)。 (或者采取捷径并检查Cursor.Current == null

[StructLayout( LayoutKind.Sequential )]
struct POINT {
    public Int32 x;
    public Int32 y;
}

[StructLayout( LayoutKind.Sequential )]
struct CURSORINFO {
    public Int32 cbSize;        

    public Int32 flags;         

    public IntPtr hCursor;
    public POINT ptScreenPos;
}

[DllImport( "user32.dll" )]
static extern bool GetCursorInfo ( out CURSORINFO pci );

CURSORINFO GetCinfo () {
    CURSORINFO cinfo;
    cinfo.cbSize = Marshal.SizeOf( typeof( CURSORINFO ) );
    GetCursorInfo( out cinfo );
    return cinfo;
}

public bool FlagHidden () {
    CURSORINFO cinfo = GetCinfo();
    return cingo.flags == 0 || Cursor.Current == null; // using both for extra safety
}

现在我们遇到了网络浏览器。浏览器不会渲染隐藏的光标,但它们会改变光标的句柄。大多数情况下,但并非总是,句柄的值会很大。

public bool BrowserHidden () {
    CURSORINFO cinfo = GetCinfo();
    return Math.Abs( cinfo.hCursor.ToInt64() ) > 10000000;
}

现在,我们几乎完成了,但不要忘记会有异常情况。例如,Krita 在隐藏光标的同时呈现笔刷轮廓,但就系统而言,光标仍然可见。没有一种简单通用的方法来处理这种情况,因此我们必须明确告诉我们的软件注意这些特定情况。
List<IntPtr> exceptions = new List<IntPtr>{ ... }; // put all the common exceptions here, implement some way to add / remove them
public bool IsException () {
    CURSORINFO cinfo = GetCinfo();
    return exceptions.Contains( cinfo.hCursor );
}

public bool CursorHidden () {
    bool hidden = FlagHidden() || BrowserHidden();
    return hidden != IsException(); // hidden xor exception ( inverts hidden if its an exception )
}

可能存在这样一种情况,指针X对于软件B来说意味着A,但对于软件D来说意味着C。虽然这种情况很少见,但如果您想要更加安全,那么异常列表应该是一个由应用程序和句柄组成的列表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接