• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dapplo / Dapplo.Windows / 21516486103

30 Jan 2026 12:53PM UTC coverage: 35.757% (-0.4%) from 36.141%
21516486103

push

github

Lakritzator
Changes tests to WpfFact and commented it out to skip test.

670 of 2054 branches covered (32.62%)

Branch coverage included in aggregate %.

1882 of 5083 relevant lines covered (37.03%)

21.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.34
/src/Dapplo.Windows.Icons/CursorHelper.cs
1
// Copyright (c) Dapplo and contributors. All rights reserved.
2
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3

4
#if !NETSTANDARD2_0
5
using Dapplo.Windows.Common.Structs;
6
using Dapplo.Windows.Dpi;
7
using Dapplo.Windows.Icons.Structs;
8
using Dapplo.Windows.Kernel32;
9
using Dapplo.Windows.User32.Structs;
10
using Microsoft.Win32;
11
using System;
12
using System.ComponentModel;
13
using System.Drawing;
14
using System.Runtime.InteropServices;
15
using System.Windows;
16
using System.Windows.Interop;
17
using System.Windows.Media;
18
using System.Windows.Media.Imaging;
19

20
namespace Dapplo.Windows.Icons;
21

22
/// <summary>
23
/// Helper methods for using cursor information
24
/// </summary>
25
public static class CursorHelper
26
{
27
    /// <summary>
28
    /// Gets the base size of the mouse cursor, in pixels, as configured by the user in the system settings.
29
    /// </summary>
30
    /// <remarks>This method reads the 'CursorBaseSize' value from the Windows Registry under 'Control
31
    /// Panel\Cursors'. If the value is not found or an error occurs while accessing the registry, a default size of 32
32
    /// pixels is returned.</remarks>
33
    /// <returns>The size of the mouse cursor in pixels. Returns 32 if the value cannot be retrieved from the system settings.</returns>
34
    public static int GetCursorBaseSize()
35
    {
36
        // Reads the "Make mouse pointer bigger" slider value from Registry
37
        try
38
        {
39
            using (var key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Cursors"))
2✔
40
            {
41
                if (key?.GetValue("CursorBaseSize") is int size)
2!
42
                {
43
                    return size;
2✔
44
                }
45
            }
×
46
        }
×
47
        catch
×
48
        {
49
            // Empty by design
50
        }
×
51
        return 32; // Default
×
52
    }
2✔
53

54
    /// <summary>
55
    /// This method will capture the current Cursor by using User32 Code and will try to get the actual size of the cursor as set in Accessibility settings.
56
    /// </summary>
57
    /// <param name="returnBitmap">The cursor image represented by a Bitmap or BitmapSource</param>
58
    /// <param name="hotSpot">NativePoint with the hotspot information, this is the offset where the click on the screen happens</param>
59
    /// <returns>bool true if it worked</returns>
60
    public static bool TryGetCurrentCursor<TBitmapType>(out TBitmapType returnBitmap, out NativePoint hotSpot) where TBitmapType : class
61
    {
62
        var cursorInfo = CursorInfo.Create();
2✔
63
        returnBitmap = null;
2✔
64
        hotSpot = NativePoint.Empty;
2✔
65
        if (!NativeCursorMethods.GetCursorInfo(ref cursorInfo))
2!
66
        {
67
            return false;
×
68
        }
69

70
        if (!cursorInfo.IsShowing)
2!
71
        {
72
            return false;
×
73
        }
74

75
        var iconInfoEx = IconInfoEx.Create();
2✔
76

77
        if (NativeIconMethods.GetIconInfoEx(cursorInfo.CursorHandle, ref iconInfoEx))
2!
78
        {
79
            try
80
            {
81
                // Ignore what the handle says. Calculate what it SHOULD be.
82
                // "CursorBaseSize" is the raw size (32, 48, 64) set in Accessibility settings.
83
                int baseSize = GetCursorBaseSize();
2✔
84
                uint dpi = NativeDpiMethods.GetDpiForSystem();
2✔
85
                int targetSize = (int)(baseSize * (dpi / 96.0f));
2✔
86

87
                // We ask Windows: "Load this specific resource again, but make it exactly [targetSize] pixels."
88
                IntPtr hRealCursor = IntPtr.Zero;
2✔
89
                bool isSharedHandle = false;
2✔
90

91
                var moduleName = iconInfoEx.ModuleName;
2✔
92
                if (iconInfoEx.ResourceId != 0 && !string.IsNullOrEmpty(moduleName))
2!
93
                {
94
                    // It's a System Resource (like the standard Arrow)
95
                    IntPtr hModule = Kernel32Api.GetModuleHandle(moduleName);
2✔
96
                    hRealCursor = NativeCursorMethods.LoadImage(hModule, (IntPtr)iconInfoEx.ResourceId, 2, targetSize, targetSize, 0);
2✔
97
                }
98
                else if (!string.IsNullOrEmpty(moduleName))
×
99
                {
100
                    // It's a Custom File (like a downloaded theme)
101
                    hRealCursor = NativeCursorMethods.LoadImage(IntPtr.Zero, moduleName, 2, targetSize, targetSize, 0x0010); // LR_LOADFROMFILE
×
102
                }
103

104
                // If reload failed (dynamic cursor), fallback to the original 32px handle
105
                if (hRealCursor == IntPtr.Zero)
2✔
106
                {
107
                    hRealCursor = cursorInfo.CursorHandle;
2✔
108
                    isSharedHandle = true;
2✔
109
                }
110

111
                // This renders the (potentially huge) cursor into a transparent bitmap
112
                if (targetSize > 0)
2✔
113
                {
114
                    if (typeof(TBitmapType) == typeof(BitmapSource) || typeof(TBitmapType) == typeof(ImageSource))
2✔
115
                    {
116
                        using (iconInfoEx.BitmaskBitmapHandle)
1✔
117
                        using (iconInfoEx.ColorBitmapHandle)
1✔
118
                        {
119
                            // Pass the BitmapSource to the called
120
                            returnBitmap = Imaging.CreateBitmapSourceFromHIcon(hRealCursor, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()) as TBitmapType;
1✔
121
                        }
1✔
122
                    }
123
                    else if (typeof(TBitmapType) == typeof(Bitmap) || typeof(TBitmapType) == typeof(Image))
1!
124
                    {
125
                        var bmp = new Bitmap(targetSize, targetSize, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
1✔
126
                        using (Graphics g = Graphics.FromImage(bmp))
1✔
127
                        {
128
                            g.Clear(System.Drawing.Color.Transparent);
1✔
129
                            NativeIconMethods.DrawIconEx(g.GetHdc(), 0, 0, hRealCursor, targetSize, targetSize, 0, IntPtr.Zero, 0x0003); // DI_NORMAL
1✔
130
                            g.ReleaseHdc();
1✔
131
                        }
1✔
132
                        // Pass the bitmap to the called
133
                        returnBitmap = bmp as TBitmapType;
1✔
134
                    } else
135
                    {
136
                        throw new NotSupportedException(typeof(TBitmapType).Name);
×
137
                    }
138
                }
139

140
                // The original hotspot is for the 32px version. Scale it up.
141
                if (targetSize > 0 && baseSize > 0) // Avoid divide by zero
2!
142
                {
143
                    float scaleFactor = (float)targetSize / 32.0f; // Assuming 32 is standard base
2✔
144

145
                    // Refine: If we know the original was actually, say, 48, we should use that. 
146
                    // But 32 is the standard logical unit for Windows cursors.
147
                    hotSpot = new NativePoint((int)(iconInfoEx.Hotspot.X * scaleFactor), (int)(iconInfoEx.Hotspot.Y * scaleFactor));
2✔
148
                }
149
                else
150
                {
151
                    hotSpot = iconInfoEx.Hotspot;
×
152
                }
153

154
                // Cleanup cursor, but only if it's not a shared handle (in the other case. we clean up elsewhere)
155
                if (!isSharedHandle && hRealCursor != IntPtr.Zero) NativeCursorMethods.DestroyCursor(hRealCursor);
2!
156
            }
2✔
157
            finally
158
            {
159
                // Cleanup icon
160
                iconInfoEx.BitmaskBitmapHandle.Dispose();
2✔
161
                iconInfoEx.ColorBitmapHandle.Dispose();
2✔
162
            }
2✔
163
        } else {
164
            throw new Win32Exception(Marshal.GetLastWin32Error());
×
165
        }
166

167
        return true;
2✔
168
    }
169
}
170
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc