I want to show some information similar as ToolTip
is shown - I can't use the ToolTip
because it doesn't provide enough customization and I need to show it the whole time the mouse is over the Control
.
The problem is that the Cursor.Size
is set to 32x32, but the visible part of the cursor is only 12x19 pixels so there is big gap (20x13 pixels) between the visible part of the cursor and the information I want to show (it is may be different on other Windows versions or mouse cursor settings).
I know that the bigger size is there for different types of cursor (arrow with clocks/question mark, hand, ...), but how to calculate the size of the visible part?
There should be some way, because the ToolTip
s are shown right under the cursor.
This is how the ToolTip
is shown:
This is how my information is shown:
Thanks to m.rogalski (pointed me to this question) and this answer and this answer and this article I was able to create code which counts the visible part of the mouse cursor. Since it uses unmanaged code the biggest problem was with the memory management so I hope that I free everything at the end. Please let me know if not.
External
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
}
}
Size calculation
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);
}
}
}
And then simply call
Size cursorSize = CursorActualSize.GetActualSize();