SetSystemCursor()用于多个游标行为

时间:2015-04-21 19:20:55

标签: c# winforms

我正在尝试将多个游标更改为Cross cursor。这是我正在使用的代码:

[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;

然后我使用了我做的这两个函数:

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);
}

如果我只使用SetSystemCursor(LoadCursor(IntPtr.Zero, (int)OCR_CROSS), OCR_NORMAL);,一切正常。 Normal cursor替换为Cross cursor

我的问题是当我尝试将多个游标更改为Cross cursor时。如果我致电ChangeCursors(),预期结果将为Normal cursorI-beam cursor将被Cross cursor取代。但结果真是奇怪。

当软件启动时,根据程序启动时光标的当前状态启动,会发生以下奇怪的事情:

  • 如果光标在软件启动时为Normal,则会更改为Cross(这很好)。此外,I-beam已替换为Normal(该错误,应为Cross)。
  • 如果光标在软件启动时为I-beam,则它会保持I-beam(这很糟糕,因为它应该是Cross)。然后,通过将光标悬停到之前光标应为Normal的位置,它现在是Cross(这很好)。然后,如果我在1秒前将鼠标悬停在光标所在的位置I-beam,它会神奇地变为Normal(这很奇怪)并保持这种状态。

所以,我的问题是,如何使用Cursors将2个或更多Cross cursor更改为SetSystemCursor()

2 个答案:

答案 0 :(得分:5)

不要对这种奇怪的行为感到困惑。每次分配时,只是游标被交换。

起初

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

您指定正常=交叉

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

现在分配IBeam = Cross(现在是正常的)

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

因此,为了不让它被交换,你必须保留所有游标的副本。我会给你一个例子,将Normal和IBeam改为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);
    }
}

答案 1 :(得分:1)

SetSystemCursor 用第一个参数中的光标替换第二个参数(本例中为OCR_CROSS)给出的系统游标。因此,首先将OCR_CROSS光标设置为Normal,然后设置为IBeam,因此实际上交叉光标设置为看起来像IBeam。

文档还特别说明了

  

系统通过调用DestroyCursor函数来销毁hcur [第一个参数]。因此,hcur不能是使用LoadCursor函数加载的游标。要指定从资源加载的游标,请使用CopyCursor函数复制游标,然后将副本传递给SetSystemCursor。

您的代码执行此操作,因此此处可能出现问题,或至少泄漏处理。


深入了解

SetSystemCursor比您想象的更具侵略性。它实际上是交换第一个参数中游标对象在第二个参数中指定游标的全局游标数据。

这会产生后果。假设您已将IDC_WAIT替换为IDC_CROSS,然后IDC_ARROW替换为IDC_WAIT(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.

问题:系统的箭头光标是十字光标还是等待光标?

回答:它是一个十字光标。

发生这种情况的原因是因为您实际交换了基础游标数据,而不仅仅是某些引用,因此LoadCursor会读取替换的游标数据。

这也说明了当你不首先复制光标时它会变得非常混乱的原因:然后两个光标全局交换。