1

I want to change the cursor when the mouse is over a certain control. I'm having the png of my cursor. How do I achieve it in C++ ?

I tried like this as described here

HCURSOR hcur;
hcur = ::LoadCursorFromFile("cursor.png");
::SetSystemCursor(hcur,OCR_NORMAL);

but it says OCR_NORMAL is undefined.

 HINSTANCE hInst;           
 hInst = GetModuleHandle(NULL);

 HCURSOR hCurs;    
 hCurs = LoadCursor(hInst, MAKEINTRESOURCE(2));

::SetSystemCursor(hCurs,OCR_NORMAL); 

I tried like that also but its generating weird linker errors like :

Error   2   error LNK2019: unresolved external symbol "extern "C" struct HICON__ * __stdcall LoadCursorW(struct HINSTANCE__ *,wchar_t const *)" (?LoadCursorW@@$$J18YGPAUHICON__@@PAUHINSTANCE__@@PB_W@Z) referenced in function "int __cdecl main(void)" (?main@@$$HYAHXZ) C:\Users\Diozz\Documents\Visual Studio 2013\Projects\Scroller\Scroller\main.obj

I'm placing the png in the project directory, hoping its right.

So, how will I set the cursor?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Divins Mathew
  • 2,908
  • 4
  • 22
  • 34
  • So you want a custom cursor only when the cursor is, let's say, over a button? Or should the cursor be the custom one for the whole form? – Rakete1111 Jun 17 '16 at 16:57
  • @Rakete1111. Yeah..that's correct. A picturebox, to be precise. (not for the entire form) – Divins Mathew Jun 17 '16 at 17:06
  • Have a look at [this](http://stackoverflow.com/questions/10277966/how-to-change-the-cursor-on-a-button) – Rakete1111 Jun 17 '16 at 17:07
  • 1
    For an application to use any of the `OCR_` constants, the constant `OEMRESOURCE` must be defined before the `Windows.h` header file is included. (see https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx) – Marvin Sielenkemper Jun 17 '16 at 17:17
  • @Rakete1111 but, I need to set a custom cursor ..not the ones already available in vs.. – Divins Mathew Jun 17 '16 at 17:20
  • Are you actually using WinForms and .NET? Or is this just a regular Win32 application? The answer is conceptually similar, but the code is fundamentally different. – Cody Gray - on strike Jun 17 '16 at 17:21
  • @Cody Gray Its a Win32 application. Could you help me with some code? – Divins Mathew Jun 17 '16 at 17:28

1 Answers1

3

If you want to change the cursor when it's over a particular control, you need to handle the WM_SETCURSOR message for that control's window. Upon receipt of this message, you will call the SetCursor function to set the cursor that should be displayed. This function takes a single parameter, a handle to the cursor (HCURSOR). For more background on this, you should definitely read Raymond Chen's article, "What is the process by which the cursor gets set?"

Under no circumstances, then, would you be calling the SetSystemCursor function. That function gives you a way to change the global cursor settings—you know, the same ones you change in the Mouse control panel. That's up to the user to change, if she wants to customize her desktop. Applications should leave that alone. It's totally fine if you want to display a funky cursor over a control in your application, but it is not okay if you replace the system-wide arrow cursor with a funky one!

With that out of the way, we don't really have to worry about the right way to call SetSystemCursor. So let's look at loading cursors. You have already found the LoadCursorFromFile function, and indeed, this one does exactly what its name suggests. You give it a path to a CUR file, and it loads it right up as a cursor, passing you a handle to that cursor (HCURSOR). But, other than for testing purposes, you probably won't find yourself ever using LoadCursorFromFile. Why? Because you don't want to have to deploy a CUR file along with your application. If that file ever gets deleted, or isn't included, your application stops working.

Instead, a cursor should be linked directly into your application's binary. Fortunately, Windows provides a way to do this as part of an binary's resources. You've surely seen the resource file if you've done any Windows programming before. To an RC file, you can add a cursor resource, which amounts to specifying the path to the ICO file. The resource compiler then does the rest, embedding that cursor directly into your EXE. With that done, at runtime, you don't have to rely on a fragile path anymore, you just call LoadCursor to load the cursor from a resource. (All resources are given a numerical ID, defined in a header file called Resource.h. Let's assume yours has the ID IDC_FUNKY.)

HINSTANCE hInstance = ::GetModuleHandle(NULL);  // get a handle to the app's instance
HCURSOR   hCursor   = ::LoadCursor(hInstance, MAKEINTRESOURCE(IDC_FUNKY));

You have now loaded your funky cursor from a resource embedded into your EXE. Of course, LoadCursor can also be used to load pre-defined system cursors. To do this, you pass NULL for the first parameter, because rather than loading it from an application's resource, you're loading it from the system. For example, let's load the help cursor:

HCURSOR hCursorHelp = ::LoadCursor(NULL, IDC_HELP);

Great—now we know how to load a cursor. All except for one thing: all of the custom cursors we've dealt with have been stored as CUR (or ANI) files. You mention in the question that you want to load a cursor from a PNG file. Honestly, my suggestion would be just not to do that. Use a cursor creation program that can convert your PNG file into a CUR file, and simply use the CUR file. Otherwise, you're going to be busy writing a bunch of pointless code to load a PNG file, convert it to a bitmap, and then convert that bitmap into a cursor. You'll hit a brick wall as soon as you start; there is no obvious way to load a PNG image using the Win32 API. You'll have to either use GDI+, the Windows Imaging Component, or a third-party library that can deal with PNG files. Totally outside the scope of this answer. See here and here if you want to go down this rabbit-hole. Otherwise, download something like Greenfish Icon Editor to do the conversion once and get on with your life.

Putting it all together, then, here's what you should do:

  1. Convert your PNG file to an ICO file, and add this ICO file to your application as a resource. You can do this easily from within Visual Studio.

  2. Write code that calls the LoadCursor function to load the cursor from a resource, giving you an HCURSOR. It would be sensible to do this when your application first starts up, in the initialization routine. Cache the returned handle so that you can use it throughout your application's lifetime. If the control lives on a dialog, you could do this in WM_INITDIALOG.

  3. Handle the WM_SETCURSOR message for your control. Although you can do this by subclassing, in most cases, it is easiest just to put the code in the parent's window procedure:

    static HCURSOR hCursorFunky;
    
    ...
    
    case WM_SETCURSOR:
    {
        // If we're the control that should get the cursor treatment...
        if (static_cast<HWND>(wParam) == hwndYourControl)
        {
            ::SetCursor(hCursorFunky);
            return TRUE;  // indicate we processed this message
        }
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);  // do default handling
    }
    

Or, if your control lives in a dialog, a slight variation:

    case WM_SETCURSOR:
    {
        if (static_cast<HWND>(wParam) == ::GetDlgItem(hWnd, IDC_YOURCONTROL))
        {
            ::SetCursor(hCursorFunky);
            ::SetWindowLongPtr(hWnd, DWLP_MSGRESULT, TRUE);
            return TRUE;  // indicate we processed this message
        }
        return FALSE;  // do default handling
    }

One final note: you show a linker error in your question, which suggests that you have not properly told the linker where to find the Windows SDK. All of this business gets set up for you automatically by the "Win32 Application" template in Visual Studio. You should use that to create new projects. If you have not done so, you will need to go into your project's settings and tell the linker to use (at a minimum) kernel32.lib, user32.lib, and gdi32.lib. Otherwise, the linker won't be able to find the Windows API functions that you are trying to call.

Joey Foo
  • 5
  • 3
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • *"Upon receipt of this message, you will return a handle to the cursor that should be displayed"* - no, the handle to the cursor isn't returned. `SetCursor` needs to be called before return. – Jonathan Potter Jun 17 '16 at 21:29
  • @Cody Gray Thanks a lot for your time. Though this makes a neat and complete answer, a problem still persists. I can't find the resource view in my visual studio and have no idea how to add the cursor as a resource. I just figured that express editions (which I use) lacks this feature. So, Is there a way to add the cursor or should I be switching to another edition ? – Divins Mathew Jun 18 '16 at 06:46
  • @jonathan Thanks, I have no idea what I was thinking there. I got it right two paragraphs later, and in the sample code. Haha, oh well. I've updated the answer to remove that incorrect assertion. – Cody Gray - on strike Jun 19 '16 at 08:07
  • @Divins Ah yes, using the Express version will be a problem. [The Express versions don't have the resource editor included.](http://stackoverflow.com/a/8623133/366904) You could either use a different edition of Visual Studio (if cost is not a factor), download the new [Visual Studio Community 2015](https://www.visualstudio.com/) (which includes the same features as the Professional version), or use a free external resource editor, like [ResEdit](http://www.resedit.net/). – Cody Gray - on strike Jun 19 '16 at 08:09
  • Using an external resource editor will work because the Express versions still include the resource *compiler* (actually, that is part of the Windows SDK, not Visual Studio), just not the resource *editor*. But that's not going to be an easy way to go if you are not very experienced. You'll have to make sure that the project settings are configured to invoke the resource editor on your RC file. Definitely better to upgrade to a version of VS that has a built-in resource editor. – Cody Gray - on strike Jun 19 '16 at 08:11
  • @Cody Gray Hmm.. Actually, till now to set an icon, an external resource editor was what that was helping me! Mm..as I'm not much experienced I think I'll go with changing the version. Thanks for the help :) – Divins Mathew Jun 19 '16 at 08:28