0

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 ToolTips are shown right under the cursor.

This is how the ToolTip is shown:
standard tooltip
This is how my information is shown:
my tooltip

Artholl
  • 1,291
  • 1
  • 19
  • 38
  • Maybe instead of `Cursor.Size` you can check `Cursor.Current.Size` ? – mrogal.ski Dec 13 '16 at 11:05
  • @m.rogalski It returns the same result. – Artholl Dec 13 '16 at 11:06
  • Just wanted to be sure. You can check [this example](http://stackoverflow.com/questions/3509951/how-to-get-mouse-cursor-icon-vs-c) and then find the most bottom-right pixel offset. Just an quick idea for now. EDIT: wrong link, fixed – mrogal.ski Dec 13 '16 at 11:08
  • @m.rogalski thank you for your help. Your link helped me. I created a solution and posted it as an answer. – Artholl Dec 13 '16 at 14:32
  • " I need to show it the whole time the mouse is over the Control" You can adjust a lot of settings (including ShowDuration) with the [ToolTipService Attached Properties](https://msdn.microsoft.com/en-us/library/system.windows.controls.tooltipservice(v=vs.110).aspx): **EDIT** Nevermind, I just saw you are using WinForms. Try [this](http://stackoverflow.com/questions/8225807/c-sharp-tooltip-doesnt-display-long-enough) instead. – Manfred Radlwimmer Dec 13 '16 at 14:34
  • @ManfredRadlwimmer Thank you. I forgot the `ShowAlways` property, but I am still not able to format text inside so at the end I have to create new class anyway. – Artholl Dec 13 '16 at 14:48

1 Answers1

1

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();
Community
  • 1
  • 1
Artholl
  • 1,291
  • 1
  • 19
  • 38