首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 陷阱:Cursor 建構函數無法載入彩色游標的解決方案

     編輯:關於.NET

現象:自定義游標載入時而成功、時而失敗

專案將 .cur/.ani 游標檔案嵌入資源,透過 new Cursor(new MemoryStream(Resource.MyCursor)) 載入。測試發現:黑白單色游標總能載入,但含色彩/透明通道的游標「時而替換成功,時而替換不成功」,行為極不穩定。

根因:Cursor 建構函數的隱性限制

.NET Cursor 類別的建構函數 (Cursor(Stream)) 底層調用 Win32 CreateIconFromResourceEx,**僅支援單色 (黑白) 游標格式** 。彩色游標雖然檔案格式合法,但傳入此建構函數會靜默失敗 (回傳預設游標或拋出例外),且行為隨作業系統版本、桌面合成 (DWM) 狀態不同而異,造成「時而成功」的假象。

解決方案:繞過 Cursor 建構函數,直接呼叫 LoadCursorFromFile

Win32 API LoadCursorFromFile 完整支援彩色、動畫、高解析度游標。策略:將資源流寫入暫存檔,再呼叫此 API 取得 HCURSOR,最後包裝為 Cursor 物件。

完整工具方法

public static class CursorHelper {
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr LoadCursorFromFile(string fileName);

    public static Cursor LoadResourceCursor(byte[] cursorData) {
        // 1. 寫入暫存檔 (副檔名 .cur 或 .ani 均可)
        string tempPath = Path.GetTempFileName();
        try {
            File.WriteAllBytes(tempPath, cursorData);
            // 2. 呼叫 Win32 API 載入
            IntPtr hCursor = LoadCursorFromFile(tempPath);
            if (hCursor == IntPtr.Zero) {
                int err = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception(err, "LoadCursorFromFile 失敗");
            }
            // 3. 包裝為 .NET Cursor (不擁有控制代碼所有權,不會自動銷毀 HCURSOR)
            return new Cursor(hCursor);
        } finally {
            // 4. 清理暫存檔 (Cursor 已複製內部資源,檔案可刪)
            try { File.Delete(tempPath); } catch { /* 忽略 */ }
        }
    }
}

使用範例

// 載入嵌入資源
byte[] cursorBytes = Properties.Resources.MyColorCursor; 
Cursor myCursor = CursorHelper.LoadResourceCursor(cursorBytes);

// 套用至控件
myControl.Cursor = myCursor;

注意事項與最佳實踐

  • 暫存檔路徑:Path.GetTempFileName() 保證唯一性,避免並發衝突。
  • 錯誤處理:檢查 IntPtr.Zero 並拋出 Win32Exception 獲取具體錯誤碼 (如檔案格式錯誤、找不到檔案)。
  • 資源清理:Cursor 建構函數 new Cursor(hCursor) **不會取得 HCURSOR 擁有權**,銷毀 Cursor 物件不會銷毀系統游標控制代碼。系統會在進程結束時自動回收。若需精確控制生命週期,可自行 DestroyCursor (需 P/Invoke)。
  • 效能:啟動時一次性載入並快取 Cursor 實例,避免重複 I/O 與 P/Invoke 開銷。
  • 替代方案:WPF 專案可直接使用 Cursor 類別的 Cursor(Stream) 建構函數 (WPF 內部已處理彩色游標),或使用 XAML <Cursor> 資源。本文方案針對 WinForms / 核心 .NET Framework 通用場景。

總結

.NET Cursor(Stream) 建構函數對彩色游標支援缺陷是長久存在的已知限制。透過 LoadCursorFromFile 暫存檔中轉,可完美支援所有 Windows 原生游標格式 (含動畫 .ani、高 DPI、Alpha 通道),解決「時而失效」的詭異行為,是 WinForms 開發中處理自定義游標的標準最佳實踐。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved