Search code examples
c#custom-controlsidisposablecursors

Do I need to dispose or destroy custom cursors


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?
}

Solution

  • 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.