SetSystemCursor () for multiple cursor behavior

I am trying to change some cursors to Cross cursor . This is the code I use for this:

 [DllImport("user32.dll")] static extern bool SetSystemCursor(IntPtr hcur, uint id); [DllImport("user32.dll")] static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern Int32 SystemParametersInfo(UInt32 uiAction, UInt32 uiParam, String pvParam, UInt32 fWinIni); //Normal cursor private static uint OCR_NORMAL = 32512; //The text selection (I-beam) cursor. private static uint OCR_IBEAM = 32513; //The cross-shaped cursor. private static uint OCR_CROSS = 32515; 

Then I use these two functions that I did:

 static public void ChangeCursors() { SetSystemCursor(LoadCursor(IntPtr.Zero, (int)OCR_CROSS), OCR_NORMAL); SetSystemCursor(LoadCursor(IntPtr.Zero, (int)OCR_CROSS), OCR_IBEAM); } static public void RevertCursors() { SystemParametersInfo(0x0057, 0, null, 0); } 

If I just use SetSystemCursor(LoadCursor(IntPtr.Zero, (int)OCR_CROSS), OCR_NORMAL); everything works fine. Normal cursor is replaced by Cross cursor .

My problem is that I am trying to change multiple cursors to Cross cursor . If I call ChangeCursors() , the expected result will be Normal cursor And the I-beam cursor replaced with Cross cursor . But the result is something strange.

When a program starts depending on the current cursor state when the program starts, the following strange things happen:

  • If the cursor was Normal when starting the software, it changes to Cross (this is good). In addition, the I-beam is replaced with Normal (this is bad, it should be Cross ).
  • If the cursor was I-beam when the software started, it remains I-beam (this is bad because it must be Cross ). Then, hovering over where the cursor should be Normal before, now it is Cross (this is good). Then, if I visit where the cursor was I-beam 1 second ago, it magically changes to Normal (which is strange) and stays that way.

So my question is: how to change 2 or more Cursors to Cross cursor using SetSystemCursor() ?

+5
source share
2 answers

Do not confuse strange behavior. These are just cursors that change places every time you assign.

First

 Normal == Normal IBeam == IBeam Cross == Cross 

You assign Normal = Cross

 Normal == Cross IBeam == IBeam Cross == Normal 

And now assign IBeam = Cross (which is normal now)

 Normal == Cross IBeam == Normal Cross == IBeam 

Therefore, not allowing it to swap places, you need to save copies of all cursors. I will give you an example when Normal and IBeam changed to CROSS.

Program.cs

 static class Program { [DllImport("user32.dll")] static extern bool SetSystemCursor(IntPtr hcur, uint id); [DllImport("user32.dll")] static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern Int32 SystemParametersInfo(UInt32 uiAction, UInt32 uiParam, String pvParam, UInt32 fWinIni); [DllImport("user32.dll")] public static extern IntPtr CopyIcon(IntPtr pcur); private static uint CROSS = 32515; private static uint NORMAL = 32512; private static uint IBEAM = 32513; [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); uint[] Cursors = {NORMAL, IBEAM}; for (int i = 0; i < Cursors.Length; i++) SetSystemCursor(CopyIcon(LoadCursor(IntPtr.Zero, (int)CROSS)), Cursors[i]); Application.Run(new Form1()); SystemParametersInfo(0x0057, 0, null, 0); } } 
+4
source

SetSystemCursor replaces the system cursor specified by the second argument ( OCR_CROSS in this example) with the cursor in the first argument. So, you set the OCR_CROSS cursor first to Normal, then to IBeam, so in fact the cross-cursor will look like IBeam.

The documentation also states that

The system destroys hcur [first argument] by calling the DestroyCursor function. Therefore, hcur cannot be a cursor loaded using the LoadCursor function. To specify a cursor loaded from a resource, copy the cursor using the CopyCursor function, then transfer the copy to SetSystemCursor.

Your code does this, so things may go wrong, or at least with leak handles.


In-depth appearance

SetSystemCursor much more invasive than you think. In fact, it swaps the global cursor data of the specified cursor in the second argument with the cursor object in the first argument.

This has consequences. Let's say that you replaced IDC_WAIT with IDC_CROSS , and then IDC_ARROW with IDC_WAIT (C code):

 HCURSOR hCursor = LoadCursor(0, MAKEINTRESOURCE(IDC_CROSS)); HCURSOR hCopyCursor = CopyCursor(hCursor); SetSystemCursor(hCopyCursor, (DWORD)IDC_WAIT); // Replace Wait by Cross HCURSOR hCursor2 = LoadCursor(0, MAKEINTRESOURCE(IDC_WAIT)); // Load whatever is in Wait and put it in Arrow HCURSOR hCopyCursor2 = CopyCursor(hCursor2); SetSystemCursor(hCopyCursor2, (DWORD)IDC_ARROW); // Replace Arrow by Wait. 

Question: Is the cursor on the system cursor the "Cross" or "Wait" cursor?

Answer: This is a cross cursor.

The reason for this is because you are actually changing the underlying cursor data, not just a link, so LoadCursor reads the replaced cursor data.

It also shows why this gets very confusing when you don't make a copy of the cursor at the beginning: then two cursors get exchanged globally.

+1
source

All Articles