0

I am using code similar to the accepted answer to this question to make a custom cursor. I supposed I could just ask, "What do I do when I am done with the custom cursor?" but to be specific, my Question has two parts:

One: I see the code that uses reflection to set the "ownHandle" field of the cursor. Does that make it so the (native) handle gets destroyed when the Cursor object is disposed? If not what does it do?

Two: Do I have to manually dispose the cursor object or does assigning a new cursor to a control cause the control to dispose the cursor for me? For instance:

private void customCursorButton_Clicked(object sender, EventArgs e)
{
    this.Cursor = NativeMethods.LoadCustomCursor(@"c:\windows\cursors\aero_busy.ani");
}

private void defaultCursorButton_Clicked(object sender, EventArgs e)
{
    var tmp = this.Cursor; // do I have to do this
    this.Cursor = Cursors.Default;
    tmp.Dispose();         // and this?
}
Community
  • 1
  • 1
Jeremy Sorensen
  • 1,100
  • 1
  • 7
  • 14
  • The general idea about `IDisposable` is if you have ownership of it, and you're done with it, you should dispose of it. I don't know if there's anything internal to the cursor object that needs disposing, but if it exposes a `Dispose` method, you should probably call it. – Matthew Jun 26 '14 at 20:57

2 Answers2

7

You should only ever call Dispose() on an object if you know 100% for sure that the object is not used anywhere else. Calling Dispose() is optional, the finalizer of the object ensures that the cleanup will always occur. And it is 100% sure that it isn't used anywhere else. It is merely a bit slow at getting around to doing the job.

There is little point to it for a single cursor object, a cursor is at most a handful of kilobytes of memory. But your code creates the cursor over and over again for each click, and is liable to dispose the Parent's cursor (the Cursor property is an ambient property) that won't win a lot of prizes. So proper code that makes the effort ought to resemble this:

private Cursor CustomCursor;

private void customCursorButton_Clicked(object sender, EventArgs e)
{
    if (CustomCursor == null) CustomCursor = NativeMethods.LoadCustomCursor(@"c:\windows\cursors\aero_busy.ani");
    this.Cursor = CustomCursor;
}

private void defaultCursorButton_Clicked(object sender, EventArgs e)
{
    var prev = this.Cursor;
    this.Cursor = Cursors.Default;
    if (prev == CustomCursor) {
        CustomCursor.Dispose();
        CustomCursor = null;
    }
}

protected override OnFormClosed(FormClosedEventArgs e) 
{
    base.OnFormClosed(e);
    if (CustomCursor != null) CustomCursor.Dispose();
}

There's a simple diagnostic available to know that you're getting it wrong btw. Task Manager isn't usually much good for profiling .NET apps but it is great to show you whether missing Dispose() calls is getting you into trouble in this case. Use View + Select Columns and tick "GDI Objects", it very accurately traces cursor objects (in addition to other GDI objects). Getting the displayed value to go over a couple of hundred is a sign of trouble, give or take.

Note that you must use Environment.GetFolderPath() to retrieve the install location of Windows. And deal with failure, there's no hard guarantee that the cursor will always be available. Details, details.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • **Calling Dispose() is optional, the finalizer of the object ensures that the cleanup will always occur.** Ser, I would like to disagree. – AgentFire Jun 26 '14 at 21:50
  • You are certainly entitled to your opinion, even if it is a wrong one :) I just prefer to focus on technical accuracy. – Hans Passant Jun 26 '14 at 22:02
  • @HansPassant: I agree with your solution as pertains to my question part 2. Regarding part 1, can you confirm that calling Dispose is enough to destroy the handle when using your LoadCustomCursor function? By the way, I actually ported your function to C++/CLI and modified it to load the cursors from embedded resources, so I never have to use any paths, and I think the cursors are essentially guaranteed to be there. – Jeremy Sorensen Jun 26 '14 at 23:55
  • As long as you also implemented the reflection trick (fi.SetValue) then, yes, Dispose() gets the job done. And do not hesitate to use Task Manager as I documented to feel good about it. – Hans Passant Jun 27 '14 at 00:07
1

You should dispose the object you are creating, and your way of doing it seems fine.

Just don't forget to Dispose your custom cursor whenever the form is closed as well.

I see the code that uses reflection to set the "ownHandle" field of the cursor. Does that make it so the (native) handle gets destroyed when the Cursor object is disposed? If not what does it do?

It should release the handle. Disposing is designed to release unmanaged resources.

Do I have to manually dispose the cursor object or does assigning a new cursor to a control cause the control to dispose the cursor for me?

Losing the reference to a object containing the unmanaged resources is a bad idea. It will eventually be GC'ed and the desctuctor will be called, disposing the object, but still.

Reassigning the property Cursor however is a different thing. The setter of the property might be created to dispose of the old value by itself, however, I have a strong feeling that it does not do that.

AgentFire
  • 8,944
  • 8
  • 43
  • 90
  • `It should release the handle. Disposing is designed to release unmanaged resources.` Normally I would agree, but since the cursor is being created using PInvoke and there is reflection involved (see my link to a previous answer in the question to see what I mean) I am specifically asking about this unique situation. – Jeremy Sorensen Jun 26 '14 at 23:49
  • I marked Hans's response as the answer because I used his solution in my code. I think your answer is correct as well though, particularly that one *should* dispose unused objects, thanks for the answer – Jeremy Sorensen Jun 27 '14 at 00:13