How to load a cursor from embedded resources in C#

Last night, being the last night my son was staying with his cousins, I was bored and spent some time looking at some old code. I don’t know if anyone else still uses Windows Forms applications, but in case you do, I’m sharing this…

Via some code that I found and then modified, my image viewer, when viewing an image that’s larger than the viewable area, allows you to use the mouse to drag around and pan to different parts of the image. When doing this, the program changes the mouse cursor, to one of two cursor images, one for a hand that’s up and one for a hand that’s down.

But as it so often happened to me when building that application, dot Net has no way of loading a cursor from the application’s embedded resources. It can load normal images, but not CUR files. That shit just isn’t implemented. So I had to do so myself, and it took a while to figure out because there are several different, confusing API’s that work with cursors, and the MSDN documentation is confusing.

Here’s a screenshot what it looks like when the hand down cursor is displayed. (Image viewer code not included here. This is just about the code to load the cursor.)

viewer

And here’s the code that loads the cursor, which is saved as a file resource, from the applications resources… (The plugin I have used for ages, that shows syntax highlighting in the code, doesn’t seem to work properly anymore. Also the code is wrapped on the blog, but you can copy it to a file.)

using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Windows.Forms;

namespace Romy.Core
{
    public static class CursorResourceLoader
    {
        #region Methods

        public static Cursor LoadEmbeddedCursor(byte[] cursorResource, int imageIndex = 0)
        {
            var resourceHandle = GCHandle.Alloc(cursorResource, GCHandleType.Pinned);
            var iconImage = IntPtr.Zero;
            var cursorHandle = IntPtr.Zero;

            try
            {
                var header = (IconHeader)Marshal.PtrToStructure(resourceHandle.AddrOfPinnedObject(), typeof(IconHeader));

                if (imageIndex >= header.count)
                    throw new ArgumentOutOfRangeException("imageIndex");

                var iconInfoPtr = resourceHandle.AddrOfPinnedObject() + Marshal.SizeOf(typeof(IconHeader)) + imageIndex * Marshal.SizeOf(typeof(IconInfo));
                var info = (IconInfo)Marshal.PtrToStructure(iconInfoPtr, typeof(IconInfo));

                iconImage = Marshal.AllocHGlobal(info.size + 4);
                Marshal.WriteInt16(iconImage + 0, info.hotspot_x);
                Marshal.WriteInt16(iconImage + 2, info.hotspot_y);
                Marshal.Copy(cursorResource, info.offset, iconImage + 4, info.size);

                cursorHandle = NativeMethods.CreateIconFromResource(iconImage, info.size + 4, false, 0x30000);
                return new Cursor(cursorHandle);
            }
            finally
            {
                if (cursorHandle != IntPtr.Zero)
                    NativeMethods.DestroyIcon(cursorHandle);

                if (iconImage != IntPtr.Zero)
                    Marshal.FreeHGlobal(iconImage);

                if (resourceHandle.IsAllocated)
                    resourceHandle.Free();
            }
        }

        #endregion Methods

        #region Native Methods

        [SuppressUnmanagedCodeSecurity]
        static class NativeMethods
        {
            [DllImportAttribute("user32.dll", CharSet = CharSet.Unicode)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool DestroyIcon(IntPtr hIcon);

            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr CreateIconFromResource(IntPtr pbIconBits, int dwResSize, bool fIcon, int dwVer);
        }

        #endregion Native Methods

        #region Native Structures

        [StructLayout(LayoutKind.Explicit, Pack = 1)]
        struct IconHeader
        {
            [FieldOffset(0)]
            public short reserved;

            [FieldOffset(2)]
            public short type;

            [FieldOffset(4)]
            public short count;
        }

        /// <summary>Union structure for icons and cursors.</summary>
        /// <remarks>For icons, field offset 4 is used for planes and field offset 6 for 
        /// bits-per-pixel, while for cursors field offset 4 is used for the x coordinate 
        /// of the hotspot, and field offset 6 is used for the y coordinate.</remarks>
        [StructLayout(LayoutKind.Explicit, Pack = 1)]
        struct IconInfo
        {
            [FieldOffset(0)]
            public byte width;

            [FieldOffset(1)]
            public byte height;

            [FieldOffset(2)]
            public byte colors;

            [FieldOffset(3)]
            public byte reserved;

            [FieldOffset(4)]
            public short planes;

            [FieldOffset(6)]
            public short bpp;

            [FieldOffset(4)]
            public short hotspot_x;

            [FieldOffset(6)]
            public short hotspot_y;

            [FieldOffset(8)]
            public int size;

            [FieldOffset(12)]
            public int offset;
        }

        #endregion Native Structures
    }
}

The code that uses it, in another class, is just this:

/// <summary>Loads the two hand cursors from project resources.</summary>
private static void InitializePanBoxCursors()
{
    if (handUpCursor == null && handDownCursor == null)
    {
        handUpCursor = CursorResourceLoader.LoadEmbeddedCursor(Properties.Resources.Hand_up);
        handDownCursor = CursorResourceLoader.LoadEmbeddedCursor(Properties.Resources.Hand_down);
    }
}

Note that those cursors were added to the project resources as file resources, with the names Hand_down and Hand_up. They are then accessible by name in code that Visual Studio generates. Those resources give you each cursor as a byte array, and my code wraps the Windows API CreateIconFromResource function.

Edit: Now I see there is a “Community Addition” at the bottom of that MSDN article linked immediately above, that tells you not to destroy the loaded icon using DestroyIcon. Ignore that annoying bullshit comment. Of course you must release the memory of the icon image. My code creates a new cursor from the loaded resource handle, and then releases the handle properly, and also releases the other memory it allocates. (A pinned handle of the unmanaged resource, unmanaged memory for the icon image which is used to create the cursor resource, and the Windows handle of the cursor itself.) One should always release such handles and any memory allocated explicitly. This is common sense. Honestly, it was better before MSDN allowed idiot programmers to make random “Community Addition” bullshit comments on their documentation. The comment complains about leaks when allocating thousands of cursors, while contradicting itself by telling you not to free the memory you allocate. Yes, that is a sure way to leak memory…

Advertisements

About Jerome

I am a senior C# developer in Johannesburg, South Africa. I am also a recovering addict, who spent nearly eight years using methamphetamine. I write on my recovery blog about my lessons learned and sometimes give advice to others who have made similar mistakes, often from my viewpoint as an atheist, and I also write some C# programming articles on my programming blog.
This entry was posted in Programming and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s