.NET 隱藏/自定義windows系統游標

唐宋元明清2188發表於2024-10-22

本文介紹如何操作windows系統游標。正常我們設定/隱藏游標,只能改變當前窗體或者控制元件範圍,無法全域性操作windows游標。接到一個需求,想隱藏windows全域性的滑鼠游標顯示,下面講下如何操作

先了解下系統滑鼠游標,在滑鼠屬性-自定義列表中可以看到一共有13種型別,對應13種工作狀態:

作業系統提供了一組預定義的游標,如箭頭、手形、沙漏等,位於 C:\Windows\Cursors目錄下。

對應的Windows.Input.CursorType列舉:

 1   public enum CursorType
 2   {
 3     None,
 4     No,
 5     Arrow,
 6     AppStarting,
 7     Cross,
 8     Help,
 9     IBeam,
10     SizeAll,
11     SizeNESW,
12     SizeNS,
13     SizeNWSE,
14     SizeWE,
15     UpArrow,
16     Wait,
17     Hand,
18     Pen,
19     ScrollNS,
20     ScrollWE,
21     ScrollAll,
22     ScrollN,
23     ScrollS,
24     ScrollW,
25     ScrollE,
26     ScrollNW,
27     ScrollNE,
28     ScrollSW,
29     ScrollSE,
30     ArrowCD,
31   }

游標顯示邏輯:

  • 全域性游標設定:在桌面或非控制元件區域,使用預設系統游標。
  • 視窗控制元件的設定:每個視窗控制元件可以設定自己的游標型別。當滑鼠移動到該控制元件上時,將自動切換到該設定的游標。如果未設定則顯示系統游標
  • 當滑鼠移動、點選或執行其他操作時,系統會檢測並相應更新游標形狀。應用程式也可以改變拖放等操作的游標

對當前滑鼠狀態有獲取需求的,可以透過GetCursorInfo獲取,當前滑鼠游標id以及控制代碼:

1     private IntPtr GetCurrentCursor()
2     {
3         CURSORINFO cursorInfo;
4         cursorInfo.cbSize = Marshal.SizeOf(typeof(CURSORINFO));
5         GetCursorInfo(out cursorInfo);
6         var cursorId = cursorInfo.hCursor;
7         var cursorHandle = CopyIcon(cursorId);
8         return cursorHandle;
9     }

那如何隱藏系統游標呢?系統游標可以透過SetSystemCursor function (winuser.h) - Win32 apps | Microsoft Learn函式設定,不過貌似沒有隱藏游標的入口

可以換個思路,建立一個空白游標即可。我做了一個blank.cur:自己動手製作 windows滑鼠游標檔案(.cur格式)-CSDN部落格

然後隱藏系統游標:

1   private void HideCursor()
2   {
3       _cursorHandle = GetCurrentCursor();
4       //替換為空白滑鼠游標
5       var cursorFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Blank.cur");
6       IntPtr cursor = LoadCursorFromFile(cursorFile);
7       SetSystemCursor(cursor, OcrNormal);
8  }

恢復系統游標的顯示,將之前游標Handle設定回去:

1 var success = SetSystemCursor(_cursorHandle, OcrNormal);

以上是實現了當前游標的替換。但上面有介紹過滑鼠游標狀態有13種,會根據應用程式狀態進行切換,所以其它游標也要處理。

對13種游標都替換為空白游標,13種游標CursorId值在setSystemCursor文件有說明:

 1     private readonly int[] _systemCursorIds = new int[] { 32512, 32513, 32514, 32515, 32516, 32642, 32643, 32644, 32645, 32646, 32648, 32649, 32650 };
 2     private readonly IntPtr[] _previousCursorHandles = new IntPtr[13];
 3     private void HideCursor()
 4     {
 5         for (int i = 0; i < _systemCursorIds.Length; i++)
 6         {
 7             var cursor = LoadCursor(IntPtr.Zero, _systemCursorIds[i]);
 8             var cursorHandle = CopyIcon(cursor);
 9             _previousCursorHandles[i] = cursorHandle;
10             //替換為空白滑鼠游標
11             var cursorFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Blank.cur");
12             IntPtr blankCursor = LoadCursorFromFile(cursorFile);
13             SetSystemCursor(blankCursor, (uint)_systemCursorIds[i]);
14         }
15     }

執行驗證:系統桌面、應用窗體如VisualStudio以及網頁等游標編輯狀態,都成功隱藏

還原游標狀態:

1     private void ShowCursor()
2     {
3         for (int i = 0; i < _systemCursorIds.Length; i++)
4         {
5             SetSystemCursor(_previousCursorHandles[i], (uint)_systemCursorIds[i]);
6         }
7     }

用到的User32及引數類:

.NET 隱藏/自定義windows系統游標
 1    [DllImport("user32.dll", SetLastError = true)]
 2    public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
 3    [DllImport("user32.dll")]
 4    public static extern IntPtr CopyIcon(IntPtr cusorId);
 5    [DllImport("user32.dll")]
 6    public static extern IntPtr LoadCursorFromFile(string lpFileName);
 7    [DllImport("user32.dll")]
 8    public static extern bool SetSystemCursor(IntPtr hcur, uint id);
 9    [DllImport("user32.dll")]
10    static extern bool GetCursorInfo(out CURSORINFO pci);
11 
12    [StructLayout(LayoutKind.Sequential)]
13    public struct POINT
14    {
15        public Int32 x;
16        public Int32 y;
17    }
18 
19    [StructLayout(LayoutKind.Sequential)]
20    public struct CURSORINFO
21    {
22        public Int32 cbSize;        // Specifies the size, in bytes, of the structure. 
23                                    // The caller must set this to Marshal.SizeOf(typeof(CURSORINFO)).
24        public Int32 flags;         // Specifies the cursor state. This parameter can be one of the following values:
25                                    //    0             The cursor is hidden.
26                                    //    CURSOR_SHOWING    The cursor is showing.
27        public IntPtr hCursor;          // Handle to the cursor. 
28        public POINT ptScreenPos;       // A POINT structure that receives the screen coordinates of the cursor. 
29    }
View Code

需要說明的是,系統游標修改請謹慎處理,游標修改後人工操作不太容易恢復,對應用程式退出、崩潰等情況做好游標恢復操作。

以上demo程式碼見:kybs00/HideSystemCursorDemo: 隱藏windows系統游標 (github.com)

參考資料:

AllAPI.net - Your #1 source for using API-functions in Visual Basic! (mentalis.org)

createCursor 函式 (winuser.h) - Win32 apps | Microsoft Learn

SetSystemCursor function (winuser.h) - Win32 apps | Microsoft Learn

關鍵字:自定義游標、隱藏/顯示游標、windows系統游標顯示

相關文章