Search code examples
c#windowswinapimouseeventpinvoke

How to change mouse cursor on left and right click system wide?


Due to a change in requirements, this question is no longer relevant to me. I gave an answer here which is the best answer I got by the time I no longer needed it to be answered. Thanks.

I would like to change the mouse cursor of the entire system (not just a form) and set a different cursor to the left click and a different cursor to the right click.

been seeing all kinds of examples but nothing like that.

I am using C# on VS2015, help would be much appreciated.

UPDATE:

I have a working code now. almost fully. I am listening to windows messages and responding to mouse clicks with SetSystemCursor of PInvoke. (This first problem might be fixed, but I have another one) I can replace the default cursor with another cursor that I load. I can also left click and change the cursor and it will also revert back to the first cursor I loaded. The problem is, after left clicking for the second time and on, the cursor will not change and will remain stuck on the the first cursor I loaded. interestingly, the messagebox at each part of setting the cursor pops up, but the cursor will not change, so I know it's something about the cursors. I also try to catch for errors but nothing comes up. after going over that part of code, ret_val returns 0 (failed). any idea how to fix this? here is my code:

       public partial class Form1 : Form
{

    [DllImport("user32.dll")]
    static extern IntPtr LoadCursorFromFile(string lpFileName);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool SetSystemCursor(IntPtr hcur, uint id);

    private const uint OCR_NORMAL = 32512;

    private const int WM_LBUTTONDOWN = 0x0201;
    private const int WM_LBUTTONUP = 0x0202;

    string path1;
    string path2;
    IntPtr cursor;
    IntPtr cursorLMB;

    bool ret_val;

    public Form1()
    {
        InitializeComponent();

        path1 = @"C:\cursor.cur";
        path2 = @"C:\cursorLMB.cur";

        cursor = LoadCursorFromFile(path1);
        cursorLMB = LoadCursorFromFile(path2);
        //IntPtr cursorRMB = LoadCursorFromFile(path); not implemented yet

       }
 [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_LBUTTONDOWN:

                try
                {
                    ret_val = SetSystemCursor(cursorLMB, OCR_NORMAL);
                }
                catch (Win32Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
                //  MessageBox.Show("WM_LBUTTONDOWN"); *This works all the time!
                break;

            case WM_LBUTTONUP:
                try
                {
                    ret_val = SetSystemCursor(cursor, OCR_NORMAL);
                   //   MessageBox.Show("WM_LBUTTONUP"); *This works all the time!
                }
                catch (Win32Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }

                break;
             }
        base.WndProc(ref m);
    }
}

I know that there is a function called CopyCursorinfo here and also read and understood the solution I am looking for could involve that function, but I don't know what is happening and how to fix the problem. UPDATE 2: I read on PInvoke that CopyCursor will not work in managed code and one should use CopyIcon instead, I tried to play around with it and I've managed to get it to work with this code I wrote:

 switch (m.Msg)
        {
            case WM_LBUTTONDOWN:

                try
                {
                    if (cursorLMB != IntPtr.Zero)
                        SetSystemCursor(CopyIcon(cursorLMB), OCR_NORMAL);
                    else
                        SetSystemCursor(IntPtr.Zero, OCR_NORMAL);
                }
                catch (Win32Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
                break;

            case WM_LBUTTONUP:
                try
                {
                    if (cursor != IntPtr.Zero)
                        SetSystemCursor(CopyIcon(cursor), OCR_NORMAL);
                    else
                        SetSystemCursor(IntPtr.Zero, OCR_NORMAL);
                }
                catch (Win32Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }

                break;
             }
        base.WndProc(ref m);

but the problem now is it only works on my form, outside it doesn't show the second cursor. I need to show them all the time and no matter where the cursor is.. Thanks for the help.


Solution

  • For the answer I've asked the only way to do this from C# is by using PInvoke as the other users said.

    as you see in my question, I listen to windows messages, intercepting the left click down message and the left click up message and setting other cursors for them both. and also to the idle cursor.

    the last question I was left with was that the changing of cursors didn't work globally on windows, only the idle cursor changed. though it did changed to the other cursor while over my application form. before my CopyIcon addition- it worked on all windows but only once and not more after.

    This question is not relevant to me anymore since I changed what I would like to accomplish. hoping some of this code and explanation will help someone in the future. Thanks.