9
votes

When I use a MemoryStream to load a Cursor from a resources file I receive an ArgumentException. Here is the code I use to load the cursor:

Cursor myCursor
    = new Cursor(new MemoryStream(WaterforMGC.Properties.Resources.waterspray));
Cursor = myCursor;

But I get the error. I have no idea what is wrong, I even changed Cursor = myCursor; to this.Cursor = myCursor; which gave me the same error. I tried gameform.Cursor = myCursor; but that didn't work at all.

System.ArgumentException: Image format is not valid. The image file may be corrupted.
Parameter name: stream ---> System.Runtime.InteropServices.COMException (0x800A01E1): Exception from HRESULT: 0x800A01E1 (CTL_E_INVALIDPICTURE)
   at System.Windows.Forms.UnsafeNativeMethods.IPersistStream.Load(IStream pstm)
   at System.Windows.Forms.Cursor.LoadPicture(IStream stream)
   --- End of inner exception stack trace ---
   at System.Windows.Forms.Cursor.LoadPicture(IStream stream)
   at WaterforMGC.gameform.Form1_Load(Object sender, EventArgs e) in C:\Users\Jan\Documents\Visual Studio 2008\Projects\WaterforMGC\WaterforMGC\Form1.cs:line 39
   at System.Windows.Forms.Form.OnLoad(EventArgs e)
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl()
   at System.Windows.Forms.Control.WmShowWindow(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
4
What image format is your image in?Ed S.
exception messages are there to be read... Obviously you haven't done that, because you changed stuff in the line after that one that resulted in the exception to fix the error...Daniel Hilgarth
@Dan: I doubt he knew what he was looking for in that giant wall of exception text. I pared it down, hopefully it will help.user7116
@sixlettervariables: I did the same a few seconds ago. Looks like you overwrote my changes ;)Daniel Hilgarth
@six That was a nice edit. (Imaginary) Editing +1!dlev

4 Answers

11
votes

The problem is spelled out on the very first line of the exception:

System.ArgumentException: Image format is not valid. The image file may be corrupted.

Are you sure the image you're loading is in an uncorrupted state, and is compatible with the image format for cursors?

The Cursor class does not support animated cursors (.ani files) or cursors with colors other than black and white.

Do you have any other places where you load a cursor image and it works? You might be able to work off of that to determine what's going wrong here.

6
votes

In fact you can load colored cursors into .Net. You just need to use win32 do it.

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr LoadImage(IntPtr hinst, string lpszName, uint uType, int cxDesired, int cyDesired, uint fuLoad);

//........

const int IMAGE_CURSOR = 2; 
const uint LR_LOADFROMFILE = 0x00000010;
IntPtr ipImage = LoadImage(IntPtr.Zero, 
    @"c:\mycolor.cur", 
    IMAGE_CURSOR, 
    0, 
    0, 
    LR_LOADFROMFILE);

Cursor testCursor = new Cursor(ipImage);

Cursor.Current = testCursor;
3
votes

For some reason the cursor class is far too picky about what it will read. You can create the handle yourself using the windows API then pass that to the cursor class.

C#:

//(in a class)
public static Cursor ActuallyLoadCursor(String path) {
    return new Cursor(LoadCursorFromFile(path))
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr LoadCursorFromFile(string fileName);

VB.Net:

'(in a class)'
Public Shared Function ActuallyLoadCursor(path As String) As Cursor
    Return New Cursor(LoadCursorFromFile(path))
End Function
<System.Runtime.InteropServices.DllImport("user32.dll")>
Private Shared Function LoadCursorFromFile(fileName As String) As IntPtr
End Function
0
votes

Because you have your cursor as a resource of the project, you could do this:

[DllImport("User32.dll", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern IntPtr LoadCursorFromFile(String str);

public static Cursor LoadCursorFromResource(Icon icono)  // Assuming that the resource is an Icon, but also could be a Image or a Bitmap
{
    // Saving cursor icon in temp file, necessary for loading through Win API
    string fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".cur";
    using (var fileStream = File.Open(fileName, FileMode.Create))
    {
        icono.Save(fileStream);
    }

    // Loading cursor from temp file, using Win API
    Cursor result = new Cursor(LoadCursorFromFile(fileName));

    // Deleting temp file
    File.Delete(fileName);

    return result;
}

Then, for getting the cursor, you just do:

Cursor myCursor = LoadCursorFromResource(WaterforMGC.Properties.Resources.waterspray);

Reading the cursor from a file through a pointer using Win API allows you to handle animated or color cursors, despite the limitations listed in MSDN for the Cursor class.

My answer was based on this another SO answer (and happily tested on .NET 4.0).