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

dapplo / Dapplo.Windows / 21564682539

01 Feb 2026 02:37PM UTC coverage: 35.711% (-0.05%) from 35.757%
21564682539

push

github

Lakritzator
Changed code due to a compile conflict.

675 of 2070 branches covered (32.61%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 1 file covered. (100.0%)

24 existing lines in 2 files now uncovered.

1888 of 5107 relevant lines covered (36.97%)

21.63 hits per line

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

60.0
/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.Gdi32;
8
using Dapplo.Windows.Gdi32.Structs;
9
using Dapplo.Windows.Icons.Structs;
10
using Dapplo.Windows.Kernel32;
11
using Dapplo.Windows.User32.Structs;
12
using Microsoft.Win32;
13
using System;
14
using System.ComponentModel;
15
using System.Drawing;
16
using System.Runtime.InteropServices;
17
using System.Windows;
18
using System.Windows.Interop;
19
using System.Windows.Media;
20
using System.Windows.Media.Imaging;
21

22
namespace Dapplo.Windows.Icons;
23

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

56
    /// <summary>
57
    /// 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.
58
    /// </summary>
59
    /// <param name="returnBitmap">The cursor image represented by a Bitmap or BitmapSource</param>
60
    /// <param name="hotSpot">NativePoint with the hotspot information, this is the offset where the click on the screen happens</param>
61
    /// <returns>bool true if it worked</returns>
62
    public static bool TryGetCurrentCursor<TBitmapType>(out TBitmapType returnBitmap, out NativePoint hotSpot) where TBitmapType : class
63
    {
64
        var cursorInfo = CursorInfo.Create();
2✔
65
        returnBitmap = null;
2✔
66
        hotSpot = NativePoint.Empty;
2✔
67
        if (!NativeCursorMethods.GetCursorInfo(ref cursorInfo))
2!
68
        {
UNCOV
69
            return false;
×
70
        }
71

72
        if (!cursorInfo.IsShowing)
2!
73
        {
UNCOV
74
            return false;
×
75
        }
76

77
        var iconInfoEx = IconInfoEx.Create();
2✔
78

79
        if (NativeIconMethods.GetIconInfoEx(cursorInfo.CursorHandle, ref iconInfoEx))
2!
80
        {
81
            try
82
            {
83
                int targetWidth = 0;
2✔
84
                int targetHeight = 0;
2✔
85
                bool forceSystemScaling = false;
2✔
86

87
                var moduleName = iconInfoEx.ModuleName;
2✔
88
                // Check if this is a System Cursor (Windows standard)
89
                if (IsSystemCursor(moduleName))
2!
90
                {
91
                    // It is a standard cursor (Arrow, Hand, etc).
92
                    // We MUST apply the registry sizing, because Windows might be "faking" the handle size.
93
                    // "CursorBaseSize" is the raw size (32, 48, 64) set in Accessibility settings.
UNCOV
94
                    int baseSize = GetCursorBaseSize();
×
UNCOV
95
                    uint dpi = NativeDpiMethods.GetDpiForSystem();
×
96
                    // Ignore what the handle says. Calculate what it SHOULD be.
UNCOV
97
                    targetWidth = targetHeight = (int)(baseSize * (dpi / 96.0f));
×
UNCOV
98
                    forceSystemScaling = true;
×
99
                }
100
                else
101
                {
102
                    // It is an App-Specific cursor (Photoshop Brush, Game Crosshair). TRUST THE APP. Do not inflate it.
103
                    // We need to read the actual size of the bitmap handle we received.
104
                    var bmpInfo = new GdiBitmap();
2✔
105
                    IntPtr hBitmapToMeasure = iconInfoEx.ColorBitmapHandle.IsInvalid ? iconInfoEx.BitmaskBitmapHandle.DangerousGetHandle(): iconInfoEx.ColorBitmapHandle.DangerousGetHandle();
2!
106
                    Gdi32Api.GetObject(hBitmapToMeasure, Marshal.SizeOf(typeof(Gdi32.Structs.GdiBitmap)), ref bmpInfo);
2✔
107

108
                    targetWidth = bmpInfo.Width;
2✔
109
                    targetHeight = bmpInfo.Height;
2✔
110

111

112
                    // If monochrome, height is doubled in the mask
113
                    if (iconInfoEx.ColorBitmapHandle.IsInvalid) targetHeight /= 2;
2!
114
                }
115

116
                IntPtr hRealCursor = IntPtr.Zero;
2✔
117
                bool isSharedHandle = false;
2✔
118
                if (forceSystemScaling)
2!
119
                {
UNCOV
120
                    if (iconInfoEx.ResourceId != 0 && !string.IsNullOrEmpty(moduleName))
×
121
                    {
122
                        // It's a System Resource (like the standard Arrow)
UNCOV
123
                        IntPtr hModule = Kernel32Api.GetModuleHandle(moduleName);
×
UNCOV
124
                        hRealCursor = NativeCursorMethods.LoadImage(hModule, (IntPtr)iconInfoEx.ResourceId, 2, targetWidth, targetHeight, 0);
×
125
                    }
UNCOV
126
                    else if (!string.IsNullOrEmpty(moduleName))
×
127
                    {
128
                        // It's a Custom File (like a downloaded theme)
UNCOV
129
                        hRealCursor = NativeCursorMethods.LoadImage(IntPtr.Zero, moduleName, 2, targetWidth, targetHeight, 0x0010); // LR_LOADFROMFILE
×
130
                    }
131
                }
132

133
                // If reload failed (dynamic cursor), fallback to the original 32px handle
134
                if (hRealCursor == IntPtr.Zero)
2✔
135
                {
136
                    hRealCursor = cursorInfo.CursorHandle;
2✔
137
                    isSharedHandle = true;
2✔
138
                }
139

140
                // This renders the (potentially huge) cursor into a transparent bitmap
141
                if (targetWidth > 0 && targetHeight > 0)
2✔
142
                {
143
                    if (typeof(TBitmapType) == typeof(BitmapSource) || typeof(TBitmapType) == typeof(ImageSource))
2✔
144
                    {
145
                        using (iconInfoEx.BitmaskBitmapHandle)
1✔
146
                        using (iconInfoEx.ColorBitmapHandle)
1✔
147
                        {
148
                            // Pass the BitmapSource to the caller
149
                            returnBitmap = Imaging.CreateBitmapSourceFromHIcon(hRealCursor, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()) as TBitmapType;
1✔
150
                        }
1✔
151
                    }
152
                    else if (typeof(TBitmapType) == typeof(Bitmap) || typeof(TBitmapType) == typeof(Image))
1!
153
                    {
154
                        var bmp = new Bitmap(targetWidth, targetHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
1✔
155
                        using (Graphics g = Graphics.FromImage(bmp))
1✔
156
                        {
157
                            g.Clear(System.Drawing.Color.Transparent);
1✔
158
                            NativeIconMethods.DrawIconEx(g.GetHdc(), 0, 0, hRealCursor, targetWidth, targetHeight, 0, IntPtr.Zero, 0x0003); // DI_NORMAL
1✔
159
                            g.ReleaseHdc();
1✔
160
                        }
1✔
161
                        // Pass the bitmap to the called
162
                        returnBitmap = bmp as TBitmapType;
1✔
163
                    } else
164
                    {
165
                        throw new NotSupportedException(typeof(TBitmapType).Name);
×
166
                    }
167
                }
168

169
                // The original hotspot is for the 32px version. Scale it up.
170
                if (forceSystemScaling && targetWidth > 0)
2!
171
                {
UNCOV
172
                    float scaleFactor = targetWidth / 32.0f; // Assuming 32 is standard base
×
UNCOV
173
                    hotSpot = new NativePoint((int)(iconInfoEx.Hotspot.X * scaleFactor), (int)(iconInfoEx.Hotspot.Y * scaleFactor));
×
174
                }
175
                else
176
                {
177
                    hotSpot = iconInfoEx.Hotspot;
2✔
178
                }
179

180
                // Cleanup cursor, but only if it's not a shared handle (in the other case. we clean up elsewhere)
181
                if (!isSharedHandle && hRealCursor != IntPtr.Zero) NativeCursorMethods.DestroyCursor(hRealCursor);
2!
182
            }
2✔
183
            finally
184
            {
185
                // Cleanup icon
186
                iconInfoEx.BitmaskBitmapHandle.Dispose();
2✔
187
                iconInfoEx.ColorBitmapHandle.Dispose();
2✔
188
            }
2✔
189
        } else {
UNCOV
190
            throw new Win32Exception(Marshal.GetLastWin32Error());
×
191
        }
192

193
        return true;
2✔
194
    }
195

196
    /// <summary>
197
    /// Determines whether the specified module name corresponds to a known system cursor provider.
198
    /// </summary>
199
    /// <remarks>This method checks for common Windows system cursor sources, such as 'user32.dll', the
200
    /// Windows cursors directory, and 'main.cpl' (mouse settings).</remarks>
201
    /// <param name="moduleName">The name of the module to check. This parameter cannot be null or empty.</param>
202
    /// <returns>true if the module name is associated with a standard Windows system cursor provider; otherwise, false.</returns>
203
    private static bool IsSystemCursor(string moduleName)
204
    {
205
        if (string.IsNullOrEmpty(moduleName))
2!
206
        {
UNCOV
207
            return false;
×
208
        }
209

210
        string lower = moduleName.ToLowerInvariant();
2✔
211

212
        // Standard Windows cursors live here
213
        if (lower.Contains("user32.dll"))
2!
214
        {
UNCOV
215
            return true;
×
216
        }
217

218
        if (lower.Contains(@"\windows\cursors\"))
2!
219
        {
UNCOV
220
            return true;
×
221
        }
222

223
        // Sometimes they come from main.cpl (mouse settings)
224
        return lower.Contains("main.cpl");
2✔
225
    }
226
}
227
#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