Windows 光标可见部分大小

Windows cursor visible part size

我想显示一些类似于 ToolTip 的信息 - 我不能使用 ToolTip 因为它没有提供足够的自定义,我需要一直显示它鼠标悬停在 Control.
问题是 Cursor.Size 设置为 32x32,但光标的可见部分只有 12x19 像素,因此光标的可见部分与我要显示的信息之间存在很大差距(20x13 像素)(在其他 Windows 版本或鼠标光标设置上可能有所不同。

我知道不同类型的光标(带clocks/question标记的箭头,手,...)有更大的尺寸,但是如何计算可见部分的尺寸?
应该有一些方法,因为 ToolTips 显示在光标的正下方。

这是 ToolTip 的显示方式:

我的信息是这样显示的:

感谢 m.rogalski (pointed me to this question) and this answer and this answer and this article 我能够创建计算鼠标光标可见部分的代码。由于它使用非托管代码,最大的问题是内存管理,所以我希望我最后能释放所有东西。如果没有请告诉我。
外部

using System;
using System.Runtime.InteropServices;

namespace MouseCursorHelper
{
    /// <summary>
    /// Source: https://www.codeproject.com/kb/cs/desktopcapturewithmouse.aspx
    /// + DestroyIcon, DeleteObject
    /// </summary>
    class ExternalDlls
    {
        #region Class Variables

        public const Int32 CURSOR_SHOWING = 0x00000001;

        [StructLayout(LayoutKind.Sequential)]
        public struct ICONINFO
        {
            public bool fIcon;         // Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies 
            public Int32 xHotspot;     // Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot 
            public Int32 yHotspot;     // Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot 
            public IntPtr hbmMask;     // (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, 
            public IntPtr hbmColor;    // (HBITMAP) Handle to the icon color bitmap. This member can be optional if this 
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public Int32 x;
            public Int32 y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CURSORINFO
        {
            public Int32 cbSize;        // Specifies the size, in bytes, of the structure. 
            public Int32 flags;         // Specifies the cursor state. This parameter can be one of the following values:
            public IntPtr hCursor;          // Handle to the cursor. 
            public POINT ptScreenPos;       // A POINT structure that receives the screen coordinates of the cursor. 
        }

        #endregion

        #region Class Functions

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

        [DllImport("user32.dll", EntryPoint = "CopyIcon")]
        public static extern IntPtr CopyIcon(IntPtr hIcon);

        [DllImport("user32.dll", EntryPoint = "GetIconInfo")]
        public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);

        [DllImport("user32.dll", EntryPoint = "DestroyIcon")]
        public static extern bool DestroyIcon(IntPtr hIcon);

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        #endregion
    }
}

尺寸计算

using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace MouseCursorHelper
{
    class CursorActualSize
    {
        public static Size GetActualSize()
        {
            Bitmap bmp;
            IntPtr hicon;
            ExternalDlls.CURSORINFO ci = new ExternalDlls.CURSORINFO();
            ExternalDlls.ICONINFO icInfo;
            ci.cbSize = Marshal.SizeOf(ci);
            if (ExternalDlls.GetCursorInfo(out ci))
            {
                if (ci.flags == ExternalDlls.CURSOR_SHOWING)
                {
                    hicon = ExternalDlls.CopyIcon(ci.hCursor);
                    if (ExternalDlls.GetIconInfo(hicon, out icInfo))
                    {
                        bmp = Bitmap.FromHbitmap(icInfo.hbmMask);

                        var x = 0;
                        var y = 0;

                        for (var i = 0; i < bmp.Width; i++)
                        {
                            for (var j = 0; j < bmp.Height; j++)
                            {
                                var a = bmp.GetPixel(i, j);

                                if (a.R == 0 && a.G == 0 && a.B == 0)
                                {
                                    if (i > x)
                                    {
                                        x = i;
                                    }

                                    if (j > y)
                                    {
                                        y = j;
                                    }
                                }
                            }
                        }

                        bmp.Dispose();
                        if (hicon != IntPtr.Zero)
                        {
                            ExternalDlls.DestroyIcon(hicon);
                        }
                        if (icInfo.hbmColor != IntPtr.Zero)
                        {
                            ExternalDlls.DeleteObject(icInfo.hbmColor);
                        }
                        if (icInfo.hbmMask != IntPtr.Zero)
                        {
                            ExternalDlls.DeleteObject(icInfo.hbmMask);
                        }
                        if (ci.hCursor != IntPtr.Zero)
                        {
                            ExternalDlls.DeleteObject(ci.hCursor);
                        }

                        return new Size(x, y);
                    }

                    if (hicon != IntPtr.Zero)
                    {
                        ExternalDlls.DestroyIcon(hicon);
                    }
                    if (icInfo.hbmColor != IntPtr.Zero)
                    {
                        ExternalDlls.DeleteObject(icInfo.hbmColor);
                    }
                    if (icInfo.hbmMask != IntPtr.Zero)
                    {
                        ExternalDlls.DeleteObject(icInfo.hbmMask);
                    }
                }
            }

            if (ci.hCursor != IntPtr.Zero)
            {
                ExternalDlls.DeleteObject(ci.hCursor);
            }

            return new Size(0, 0);
        }
    }
}

然后直接调用

Size cursorSize = CursorActualSize.GetActualSize();