I am trying to change multiple cursors to Cross cursor
. This is the code I am using for that matter:
[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 I made:
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. The Normal cursor
gets replaced by Cross cursor
.
My problem is when I try to change multiple cursors to Cross cursor
. If I call ChangeCursors()
, the expected result would be Normal cursor
AND I-beam cursor
gets replaced by Cross cursor
. But the result is something really weird.
When the software starts depending on the current state of the cursor when the program started, the following strange things happen:
Normal
when the software started, it changes to Cross
(that's good). Also, I-beam
is replaced with Normal
(that's bad, it should be Cross
).I-beam
when the software started, it stays I-beam
(that's bad, because it should be Cross
). Then, by hovering over to where previously the cursor should be Normal
it is now Cross
(that's good). Then, if I hover over to where the cursor was I-beam
1 second ago, it magically changes to Normal
(that's weird) and stays that way.So, my question is, how can I change 2 or more Cursors
to Cross cursor
, using SetSystemCursor()
?
Dont get confused about the weird behaviour. It's just the cursors getting swapped each time when you Assign.
At 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
So for not letting it get swapped, you have to keep copies of all the cursors. I'll give you an example having 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);
}
}
SetSystemCursor
replaces the system cursor given 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 effect the cross-cursor is set to look like an IBeam.
The documentation also specifically says
The system destroys hcur [the 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 pass the copy to SetSystemCursor.
Your code does this, so things could go wrong here, or at the least leak handles.
The SetSystemCursor
is far more invasive then you might think. It actually swaps the global cursor-data of the specified cursor in the second argument by the cursor object in the first argument.
This has consequences. Say that you replaced IDC_WAIT
by IDC_CROSS
and then IDC_ARROW
by IDC_WAIT
(code in C):
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 Arrow cursor of the system a Cross or a Wait cursor?
Answer: It's a Cross cursor.
The reason this happens is because you actually swap the underlying cursor data, not just some reference, so LoadCursor
reads the replaced cursor data.
This also shows why it gets very confusing when you don't make a copy of the cursor first: then the two cursors are getting swapped globally.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With