Programmatically change custom mouse cursor in windows? - c#

I am trying to change the windows cursors (the default is Windows Custom Scheme) to my custom cursors (It named Cut the rope):
Is there any idea to change all of cursors (Arrow, Busy, Help Select, Link select,...) to my Cut the rope?

If you want to change the default Mouse Cursor theme:
You can just change it in the registry:
There are three main registry keys that come into play.
The registry key HKEY_CURRENT_USER\Control Panel\Cursors contains the active user cursors
1a) The values underneath this are the different types of cursors
1b) The Scheme Source specifies the type of cursor scheme that is currently being used.
The different values are:
"0" – Windows Default
"1" – User Scheme
"2" – System Scheme
The registry key HKEY_CURRENT_USER\Control Panel\Cursors contains the user defined cursor schemes (i.e. Scheme Source = 1)
The registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Schemes contains the system cursor schemes (i.e. Scheme Source = 2)
If you already changed the path to one of the cursor type in HKCU\Control Panel\Cursors and realized that it did not do anything. You are correct, just updating a key – HKCU\Control Panel\Cursors\Arrow, for instance – isn’t enough. You have to tell windows to load the new cursor.
This is where the SystemParametersInfo call comes in. To try this out let’s go ahead and change HKCU\Control Panel\Cursors\Arrow to C:\WINDOWS\Cursors\appstar3.ani (assuming you have this icon) and then make a call to SystemParametersInfo.
In AutoHotKey Script:
SPI_SETCURSORS := 0x57
result := DllCall("SystemParametersInfo", "UInt", SPI_SETCURSORS, "UInt", 0, "UInt", 0, "UInt", '0')
MsgBox Error Level: %ErrorLevel% `nLast error: %A_LastError%`nresult: %result%
Translated to C#:
[DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, uint pvParam, uint fWinIni);
const int SPI_SETCURSORS = 0x0057;
const int SPIF_UPDATEINIFILE = 0x01;
const int SPIF_SENDCHANGE = 0x02;
To call it:
SystemParametersInfo(SPI_SETCURSORS, 0, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
Changing to the Default Windows Cursor
Now the tricky part. If you look at HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Control Panel\Schemes you will notice that “Windows Default” is defined as “,,,,,,,,,,,,,” or in other words no pointers to actual cursors!
What to do now? Don’t worry. All you have to do is set the different cursor types to empty string and then make the SystemParametersInfo call as usual. In fact, you can set any of the cursor type to empty string in any scheme and Windows will default it to it’s equivalent in the “Windows Default” scheme.
REF:
https://thebitguru.com/articles/programmatically-changing-windows-mouse-cursors/3
https://social.msdn.microsoft.com/Forums/vstudio/en-US/977e2f40-3222-4e13-90ea-4e8d0cdf289c/faq-item-how-to-change-the-systems-cursor-using-visual-cnet?forum=csharpgeneral

You can do like this. Get the Cursor.cur file to load custom cursor. On MouseLeave set the Default cursor for form.
public static Cursor ActuallyLoadCursor(String path)
{
return new Cursor(LoadCursorFromFile(path));
}
[DllImport("user32.dll")]
private static extern IntPtr LoadCursorFromFile(string fileName);
Button btn = new Button();
btn.MouseLeave += Btn_MouseLeave;
btn.Cursor = ActuallyLoadCursor("Cursor.cur");
private static void Btn_MouseLeave(object sender, EventArgs e)
{
this.Cursor = Cursors.Default;
}

Related

Does the value of APPCOMMAND_VOLUME_UP and APPCOMMAND_VOLUME_DOWN really matter as long as it starts with 0xA and 0x9?

I'm using the below code to modify system volume programmatically. I would be honest with you that I got the same code from the internet from a lot of sources.
//private const int APPCOMMAND_VOLUME_UP = 0xA0000;
private const int APPCOMMAND_VOLUME_UP = 0xAFFFF;
private const int APPCOMMAND_VOLUME_DOWN = 0x90000;
private const int WM_APPCOMMAND = 0x319;
[DllImport("user32.dll")]
public static extern IntPtr SendMessageW(IntPtr hWnd, int Msg,
IntPtr wParam, IntPtr lParam);
private void btnVolumeUp_Click(object sender, RoutedEventArgs e)
{
SendMessageW(new WindowInteropHelper(this).Handle, WM_APPCOMMAND, new WindowInteropHelper(this).Handle,
(IntPtr)APPCOMMAND_VOLUME_UP);
}
I've observed that the values of the two variables APPCOMMAND_VOLUME_UP and APPCOMMAND_VOLUME_DOWN don't really matter as long as the up value starts with 0xA and the down value starts with 0x9. I have tried with many different values between 0000 and FFFF for both UP and DOWN scenarios. Is it really true that the values don't matter?
I don't have much knowledge about the interop calls. Can someone please explain the significance of those values?
Thank you in advance!
There is no "value". The LPARAM argument encodes three distinct properties:
The command, for example APPCOMMAND_VOLUME_UP is 10 (0x0A).
The device, indicates how the command was generated and can be key, mouse or oem.
The "keys", indicates which modifiers were in effect when the command was generated. Like shift/ctrl for a keyboard and the clicked mouse button for a mouse
These three properties are encoded the way bitfields work in the C language. The bit pattern in hex is 0xDCCCKKKK where C is command, D is device, K is keys.
Since you synthesize the message yourself, you have no meaningful way to report the device or the keys. Nor does it matter, you should simply use 0 (device = keyboard, no modifier keys). Do note that the value you use now is not correct, 0xAFFFF does not use a correct keys value and you are saying that the CTRL and SHIFT keys are down. Probably not enough to terminally confuse the shell, these modifier keys don't affect the way that particular command works.
So a sane implementation would look like:
public enum AppCommand {
VolumeDown = 9
VolumeUp = 10,
// etc..
}
private void SendAppCommand(AppCommand cmd) {
var hwnd = new WindowInteropHelper(this).Handle;
SendMessageW(hwnd, WM_APPCOMMAND, hwnd, (int)cmd << 16);
}

SetSystemCursor() for multiple cursors behavior

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:
If cursor was 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).
If cursor was 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.
In-depth look
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.

Disable touch visual feedback on windows 8.1 (programmatically) [Desktop App]

I have a C# WPF application intended specifically for Win8.1 (a desktop app, NOT metro).
I want users to be able to use touch injections but I'm trying to disable the visualization feedback that windows creates for a Tap gesture Press Hold and Drag (like selecting multiple files on desktop) and other gestures (zooming scrolling etc).
After Searching the web for a long time I've found this post:
How do I disable Windows 8 touch contact visualizations for my application?
So I tried to do the same...
I tried this (Will put my Win32 class at the end)
public void DisableGestureVisualization()
{
const int SPI_SETCONTACTVISUALIZATION = 0x2019;
const int SPI_SETGESTUREVISUALIZATION = 0x201B;
ulong gv = 0;
Logger.Debug(!Win32.SystemParametersInfo(SPI_SETGESTUREVISUALIZATION, 0, ref gv, 0)
? #"Failed SystemParametersInfo SPI_SETGESTUREVISUALIZATION"
: #"Successfuly returned from SystemParametersInfo SPI_SETGESTUREVISUALIZATION");
Logger.Debug(!Win32.SystemParametersInfo(SPI_SETCONTACTVISUALIZATION, 0, ref gv, 0)
? #"Failed SystemParametersInfo SPI_SETCONTACTVISUALIZATION"
: #"Successfuly returned from SystemParametersInfo SPI_SETCONTACTVISUALIZATION");
}
And also this:
public void TryDisableWindowsVisualFeedback(IntPtr hWnd)
{
bool enable = false;
foreach (Win32.FEEDBACK_TYPE type in Enum.GetValues(typeof(Win32.FEEDBACK_TYPE)))
{
if (type == Win32.FEEDBACK_TYPE.FEEDBACK_MAX)
{
continue;
}
Logger.Debug(!Win32.SetWindowFeedbackSetting(hWnd, type, 0, 4, ref enable)
? #"Failed to SetWindowFeedbackSetting for " + type
: #"Successfuly returned from SetWindowFeedbackSetting for " + type);
}
}
And I call this from my WPF app like this:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Window window = Window.GetWindow(this);
var wih = new WindowInteropHelper(window);
IntPtr hWnd = wih.Handle;
TryDisableWindowsVisualFeedback(hWnd);
}
This is my auxiliary Win32 class:
internal class Win32
{
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SystemParametersInfo(
uint uiAction,
uint uiParam,
ref ulong pvParam,
uint fWinIni
);
public enum FEEDBACK_TYPE : uint
{
FEEDBACK_TOUCH_CONTACTVISUALIZATION = 1,
FEEDBACK_PEN_BARRELVISUALIZATION = 2,
FEEDBACK_PEN_TAP = 3,
FEEDBACK_PEN_DOUBLETAP = 4,
FEEDBACK_PEN_PRESSANDHOLD = 5,
FEEDBACK_PEN_RIGHTTAP = 6,
FEEDBACK_TOUCH_TAP = 7,
FEEDBACK_TOUCH_DOUBLETAP = 8,
FEEDBACK_TOUCH_PRESSANDHOLD = 9,
FEEDBACK_TOUCH_RIGHTTAP = 10,
FEEDBACK_GESTURE_PRESSANDTAP = 11,
FEEDBACK_MAX = 0xFFFFFFFF
}
[DllImport("user32.dll")]
public static extern bool SetWindowFeedbackSetting(
IntPtr hwnd,
FEEDBACK_TYPE feedback,
uint dwFlags,
uint size,
[In] ref bool configuration
);
}
Non of the above disabled the round gray tap visual feedback nor did it disable the small white circle that appears when holding and dragging.
I even tried using the C# example in the blog:
Windows.UI.Input.PointerVisualizationSettings.
GetForCurrentView().
IsContactFeedbackEnabled = false;
This code works for a metro app, so I tried This SO post and got the Windows namespace but when running the code I get
"An unhandled exception of type 'System.Exception' occurred in MyProg.exe
WinRT information: Element not found.
Additional information: Element not found."
From what I could figure out, the PointerVisualizationSettings is not supported from a desktop application so this way is doomed...
If anyone can help me with this issue, please do.
Thanks
I had a similar problem and I was able to remove my tap gesture feedback by adding
Stylus.IsTapFeedbackEnabled = "False" to my root window.
Your TryDisableWindowsVisualFeedback method looks like it has the wrong pinvoke signature so you may be setting the visual feedback instead of clearing it. The configuration argument is a BOOL* not a bool*, and BOOL is a 4 byte integer. You can fix this with the MarshalAs attribute:
[In , MarshalAs(UnmanagedType.Bool)] ref bool configuration
You can call GetWindowFeedbackSetting to confirm that it was set correctly.
With the right pinvoke and hWnd, SetWindowFeedbackSetting should work. I confirmed that it does for me in a native app. WPF handles touch a bit oddly. I wouldn't expect it to affect this, but I haven't looked at WPF in depth for several years.
For your other methods, the Windows.UI.Input classes are documented to work only in Windows Store apps, so errors calling them from a desktop app are expected. Under the covers they'll make the same changes as SetWindowFeedbackSetting.
Using SystemParametersInfo to affect global UI is overkill: you don't want to solve a local problem by causing a global one. That said, it would probably work if you fire change notifications. Using SetWindowFeedbackSetting to target just your window is a much better solution though.
I don't know if this would resolve the OP's original issue (or even makes a difference), but I can confirm that I have successfully disabled all touch related visual feedback for my own Control-derived class selectively with the following method (almost like the one suggested by the OP) - at least on my Windows 10 machine:
public class MyTouchControl: Control
{
// ...a lot of other touch related stuff going on...
enum FEEDBACK_TYPE
{
TOUCH_CONTACTVISUALIZATION = 1,
PEN_BARRELVISUALIZATION = 2,
PEN_TAP = 3,
PEN_DOUBLETAP = 4,
PEN_PRESSANDHOLD = 5,
PEN_RIGHTTAP = 6,
TOUCH_TAP = 7,
TOUCH_DOUBLETAP = 8,
TOUCH_PRESSANDHOLD = 9,
TOUCH_RIGHTTAP = 10,
GESTURE_PRESSANDTAP = 11
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetWindowFeedbackSetting(IntPtr hWnd, FEEDBACK_TYPE feedback, int dwFlags, int size, ref int config);
void disableAllTouchVisualFeedback()
{
int enable = 0;
foreach (FEEDBACK_TYPE type in Enum.GetValues(typeof(FEEDBACK_TYPE)))
{
SetWindowFeedbackSetting(Handle, type, 0, 4, ref enable);
}
}
protected override void OnHandleCreated(EventArgs e)
{
disableAllTouchVisualFeedback();
base.OnHandleCreated(e);
}
}

Cursor.Postition and Mouse_Event being Blocked

I'm working on an app to automate some input into another application. And i'm running into a problem. Below is the function code i'm using
public class MouseClick
{
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
}
public enum MouseButton
{
MOUSEEVENTF_LEFTDOWN = 0x02,
MOUSEEVENTF_LEFTUP = 0x04,
MOUSEEVENTF_RIGHTDOWN = 0x08,
MOUSEEVENTF_RIGHTUP = 0x10
}
and here is the code i'm using to move and click
Point LocPoint = GetLocation(Column, Row, Item);
Console.WriteLine("Column: {0}\tRow: {1}\tItem: {2}\tPoints: {3}\tCursor: {4}", Column, Row, Item, Points, LocPoint.X + "," + LocPoint.Y);
Thread.Sleep(200);
Cursor.Position = LocPoint;
Thread.Sleep(10);
MouseClick.mouse_event((int)MouseButton.MOUSEEVENTF_LEFTDOWN | (int)MouseButton.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Thread.Sleep(200);
However here is where things get interesting when I don't have the application I want to input into as the active window but say mspaint the code runs fine and I get dots from the paint brush where I want to click however when the application I want to run this in is active the mouse doesn't ever move and no click is registered it is as if the application is intercepting these calls and ignoring them. So this leads me to two questions
Is this possible? How does it detect difference between a mouse and setting the cords
Is there a way around this or another method to use?
Ok so everyone is telling me to use SendInput instead. Ok so I changed the code to use SendInput. I've also tried C# SendKeys as a test as well. Currently I've gone back to the basic and i'm just trying to input the letter A into a text input box that I make the target manually. When I run it in Notepad both SendInput and SendKeys both type the letter A however when i'm inside the other application i'm trying to automate this to nothing shows up. Here is the SendInput code i'm using.
INPUT[] Inputs = new INPUT[2];
Inputs[0].type = WindowsAPI.INPUT_KEYBOARD;
Inputs[0].ki.wVk = 0;
Inputs[0].ki.dwFlags = WindowsAPI.KEYEVENTF_UNICODE;
Inputs[0].ki.wScan = 0x41;
Inputs[0].type = WindowsAPI.INPUT_KEYBOARD;
Inputs[0].ki.wVk = 0;
Inputs[1].ki.dwFlags = WindowsAPI.KEYEVENTF_KEYUP;
Inputs[0].ki.wScan = 0x41;
WindowsAPI.SendInput((uint)Inputs.Length, Inputs, Marshal.SizeOf(Inputs[0]));
So after just frustration I decided to run the app outside of Visual Studio's debug mode which resulted in the same result nothing. However I decided to run the app "As Administrator" even through I have UAC turned all the down and to my surprise the application properly moved the mouse, clicked and inputted text. I do not know why this is required as I've pinvoked methods before and never had to do this however that seems to be the solution.

Two Problems with scroll bars in C#

I hate two richTextBoxs.
I have two problems:
no matter how I define the scroll bar - it doesn't apear. if I write more than the length of the line - the cursor goes to the line below - I want everything to be writen in the same row and the user could move right-left with the scroll bar.
I want one scroll bar to control both richtextboxes.
one horizontal for both, and one vertical for both.
You'll need to set the MultiLine property to false (the default is true for RichTextBoxes) if you just want one line, or else set the WordWrap property to false (this also defaults to true).
I guess you'll need to handle the HScroll and VScroll events of each RichTextBox, and in the event handlers scroll the other textbox by the same amount. Not sure how easy this would be, though...
Best way to solve your second Problem is to use GetScrollInfo and SetScrollInfo. I think you should be able to handle the HScrolland VScrollevents, when they fire you'll have to set the new ScrollInfo in the second RichTextBox.
You'll need WM_SendMessage too for this job. (All usuable with imports of User32.dll)
Usual work to do:
Getting informed when the user scrolls (Scrollevents), and setting the new ScrollInfo to the second RichTextBox. After setting the ScrollInfo you'll need to send a Message to the Scrollbar / Control to Scroll.
First of all you need your "own" SCROLLINFO struct
[StructLayout(LayoutKind.Sequential)]
struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
Then get the Scrollinfo at ScrollingEvent in the first TextBox:
GetScrollInfo(this.Handle, SB_VERT, ref _si);
(where _si is your SCROLLINFO-instance). Then send the message to the scrollbar
_si.nPos = 0815; //Your wanted new ScrollbarPosition (I think you can use the value of the first Scrollbar)
//// Reposition scroller
SetScrollInfo(Handle, SB_VERT, ref _si, true);
//// Send a WM_VSCROLL scroll message using SB_THUMBTRACK as wParam
//// SB_THUMBTRACK: low-order word of wParam, si.nPos high-order word of wParam
IntPtr ptrWparam = new IntPtr(SB_THUMBTRACK + 0x10000 * _si.nPos);
IntPtr ptrLparam = new IntPtr(0);
SendMessage(Handle, WM_VSCROLL, ptrWparam, ptrLparam);
Where HANDLE is the Handle of your RichTextBox that has to be scrolled - you can get the handle at anytime using
RichTextBox.Handle
You should really have a look at http://msdn.microsoft.com/en-us/library/bb787537%28VS.85%29.aspx and http://msdn.microsoft.com/en-us/library/ms644950%28VS.85%29.aspx
There are also some good examples at pinvoke.net (for example GetScrollInfo)

Categories