当鼠标hover在特定的控件上时,如何设置自定义光标?

我想在鼠标超过某个控件时改变光标。 我有我的光标的PNG。 我如何在C ++中实现它?

我像这里描述的那样尝试

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

但它说OCR_NORMAL是未定义的。

  HINSTANCE hInst; hInst = GetModuleHandle(NULL); HCURSOR hCurs; hCurs = LoadCursor(hInst, MAKEINTRESOURCE(2)); ::SetSystemCursor(hCurs,OCR_NORMAL); 

我也尝试过,但它产生奇怪的链接器错误,如:

 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 

我把png放在项目目录中,希望它是正确的。

那么,我将如何设置光标?

如果要在特定控件上更改游标,则需要为该控件的窗口处理WM_SETCURSOR消息。 收到此消息后,您将调用SetCursor函数来设置应显示的光标。 这个函数接受一个参数,一个游标的句柄( HCURSOR )。 关于这方面的更多背景,你一定要阅读Raymond Chen的文章“游标设置的过程是什么?

那么在任何情况下,你都会调用SetSystemCursor函数。 该功能为您提供了更改全局光标设置的方法 – 您知道,您在鼠标控制面板中更改了相同的设置。 如果用户想要自定义桌面,那就要由用户来改变。 应用程序应该离开这一点。 如果你想在你的应用程序中的控件上显示一个时髦的光标,这是完全正确的,但是如果你用一个时髦的光标替换系统范围的箭头光标是不行的!

因此,我们不必担心调用SetSystemCursor的正确方法。 那么让我们看看加载游标。 你已经找到了LoadCursorFromFile函数,事实上,这个函数的名字就是这么做的。 你给它一个CUR文件的路径,并将它作为一个游标加载,向你传递一个该游标的句柄( HCURSOR )。 但是,除了测试目的,你可能不会发现自己曾经使用LoadCursorFromFile 。 为什么? 因为您不希望随应用程序一起部署CUR文件。 如果该文件被删除或不包含,您的应用程序将停止工作。

相反,游标应直接链接到应用程序的二进制文件中。 幸运的是,Windows提供了一种将二进制资源作为其一部分的方法。 如果你之前做过任何Windows编程,你一定会看到这个资源文件。 对于RC文件,可以添加一个游标资源,这相当于指定ICO文件的路径。 然后资源编译器完成剩下的工作,将光标直接嵌入到EXE中。 这样做,在运行时,您不必再依赖脆弱的路径,只需调用LoadCursor从资源加载游标即可。 (所有的资源都有一个数字ID,在名为Resource.h的头文件中定义,假设你的ID为IDC_FUNKY 。)

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

您现在已经从嵌入到EXE中的资源加载了您的时髦光标。 当然, LoadCursor也可以用来加载预定义的系统光标。 为此,您为第一个参数传递NULL ,因为不是从应用程序的资源加载它,而是从系统加载它。 例如,让我们加载帮助光标:

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

太棒了 – 现在我们知道如何加载游标。 除了一件事之外:我们处理的所有自定义游标都被存储为CUR(或ANI)文件。 您在提问中提到您要从PNG文件加载游标。 老实说,我的建议就是这样做。 使用光标创建程序,可以将您的PNG文件转换为CUR文件,只需使用CUR文件。 否则,你将忙于编写一堆毫无意义的代码来加载PNG文件,将其转换为位图,然后将该位图转换为光标。 你一开始就会撞到一堵砖墙; 使用Win32 API加载PNG图像没有明显的方法。 您必须使用GDI +,Windows映像组件或可以处理PNG文件的第三方库。 完全超出了这个答案的范围。 看到这里和这里,如果你想要走下这个兔子洞。 否则,请下载一些像Greenfish Icon Editor一样的转换,然后继续你的生活。

把它放在一起,那么,这是你应该做的:

  1. 将您的PNG文件转换为ICO文件,并将此ICO文件作为资源添加到您的应用程序中。 您可以在Visual Studio中轻松完成此操作。
  2. 编写调用LoadCursor函数的代码,从资源加载光标,给你一个HCURSOR 。 在初始化例程中,当您的应用程序首次启动时,这样做是明智的。 缓存返回的句柄,以便可以在整个应用程序的整个生命周期中使用它。 如果控件位于对话框上,则可以在WM_INITDIALOG执行此WM_INITDIALOG
  3. 处理您的控件的WM_SETCURSOR消息。 虽然你可以通过子类来实现,但在大多数情况下,最简单的方法就是把代码放在父窗口过程中:

     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 } 

    或者,如果你的控制生活在一个对话中,会有一点变化:

     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 } 

最后一个注意事项:你在你的问题中显示了一个链接器错误,这表明你没有正确地告诉链接器在哪里可以找到Windows SDK。 所有这些业务都由Visual Studio中的“Win32 Application”模板自动设置。 你应该使用它来创建新的项目。 如果你还没有这样做,你将需要进入你的项目设置,并告诉链接器使用(至少) kernel32.libuser32.libgdi32.lib 。 否则,链接器将无法找到您尝试调用的Windows API函数。