17

我希望我的程序能够覆盖最大施加的鼠标大小 32x32,就像附图中的程序一样,图中的光标是 72x72。这是ProcMon显示光标更改时发生的情况的捕获。

但是,如果我尝试自己更改光标文件的注册表值,然后使用推送更改

SystemParametersInfo(SPI.SPI_SETCURSORS, 0, IntPtr.Zero, SPIF.SPIF_SENDCHANGE);

然后光标会改变,但它仍然限制在 32x32 的最大尺寸。这个程序是如何绕过这个限制的?此外,光标在程序结束后仍然存在,因此它不能是它在运行时正在做的事情,但必须覆盖某处的设置。

在此处输入图像描述

感谢您的帮助,我一直无法在网上找到这样的任何东西,所以我什至不知道是否有人会给出答案。

编辑:我看到一些对名为C:\Windows\SysWOW64\Imageres.dll. 它们只是读取,但也许这些游标存储在这里,或者他们以某种方式修改了这个文件。但我认为这可能会让比我更有经验的人走上正轨。

编辑 2:我相信大小由 SM_CXCURSOR 和 SM_CYCURSOR 变量决定。如果我能找到一种方法来设置这些,我可能会做生意。打算编写一个快速程序,让我在程序运行和巨大的鼠标光标的情况下在 PC 上获取这些值,然后看看它返回什么......

编辑3:没有运气;带有大光标的 PC 为 SM_CXCURSOR 和 SM_CYCURSOR 返回 32x32。

4

4 回答 4

7

使用SetSystemCursor它可以将光标设置为比标准光标大得多的图像。

这是我用于调整系统光标大小的类:

using System;
using System.Drawing;
using System.Reflection;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    class SystemCursors
    {
        [DllImport("user32.dll")]
        static extern bool SetSystemCursor(IntPtr hcur, uint id);

        enum CursorShift
        {
            Centered,
            LowerRight,
        }

        public static void SetSystemCursorsSize(int newSize)
        {
            ResizeCursor(System.Windows.Forms.Cursors.AppStarting, newSize, CursorShift.LowerRight);
            ResizeCursor(System.Windows.Forms.Cursors.Arrow, newSize, CursorShift.LowerRight);
            ResizeCursor(System.Windows.Forms.Cursors.Cross, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.Hand, newSize, CursorShift.LowerRight);
            ResizeCursor(System.Windows.Forms.Cursors.Help, newSize, CursorShift.LowerRight);
            ResizeCursor(System.Windows.Forms.Cursors.HSplit, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.IBeam, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.No, newSize, CursorShift.LowerRight);
            ResizeCursor(System.Windows.Forms.Cursors.NoMove2D, newSize, CursorShift.LowerRight);
            ResizeCursor(System.Windows.Forms.Cursors.NoMoveHoriz, newSize, CursorShift.LowerRight);
            ResizeCursor(System.Windows.Forms.Cursors.NoMoveVert, newSize, CursorShift.LowerRight);
            ResizeCursor(System.Windows.Forms.Cursors.PanEast, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.PanNE, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.PanNorth, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.PanNW, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.PanSE, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.PanSouth, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.PanSW, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.PanWest, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.SizeAll, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.SizeNESW, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.SizeNS, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.SizeNWSE, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.SizeWE, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.UpArrow, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.VSplit, newSize, CursorShift.Centered);
            ResizeCursor(System.Windows.Forms.Cursors.WaitCursor, newSize, CursorShift.LowerRight);
        }

        private static void ResizeCursor(System.Windows.Forms.Cursor cursor, 
            int newSize, CursorShift cursorShift)
        {
            Bitmap cursorImage = GetSystemCursorBitmap(cursor);
            cursorImage = ResizeCursorBitmap(cursorImage, new Size(newSize, newSize), cursorShift);
            SetCursor(cursorImage, getResourceId(cursor));
        }

        public static Bitmap GetSystemCursorBitmap(System.Windows.Forms.Cursor cursor)
        {
            Bitmap bitmap = new Bitmap(
                cursor.Size.Width, cursor.Size.Height,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            Graphics graphics = Graphics.FromImage(bitmap);

            cursor.Draw(graphics,
                new Rectangle(new Point(0, 0), cursor.Size));

            bitmap = Crop(bitmap);

            return bitmap;
        }

        private static Bitmap Crop(Bitmap bmp)
        {
            //code from http://stackoverflow.com/a/10392379/935052

            int w = bmp.Width;
            int h = bmp.Height;

            Func<int, bool> allWhiteRow = row =>
            {
                for (int i = 0; i < w; ++i)
                    if (bmp.GetPixel(i, row).A != 0)
                        return false;
                return true;
            };

            Func<int, bool> allWhiteColumn = col =>
            {
                for (int i = 0; i < h; ++i)
                    if (bmp.GetPixel(col, i).A != 0)
                        return false;
                return true;
            };

            int topmost = 0;
            for (int row = 0; row < h; ++row)
            {
                if (allWhiteRow(row))
                    topmost = row;
                else break;
            }

            int bottommost = 0;
            for (int row = h - 1; row >= 0; --row)
            {
                if (allWhiteRow(row))
                    bottommost = row;
                else break;
            }

            int leftmost = 0, rightmost = 0;
            for (int col = 0; col < w; ++col)
            {
                if (allWhiteColumn(col))
                    leftmost = col;
                else
                    break;
            }

            for (int col = w - 1; col >= 0; --col)
            {
                if (allWhiteColumn(col))
                    rightmost = col;
                else
                    break;
            }

            if (rightmost == 0) rightmost = w; // As reached left
            if (bottommost == 0) bottommost = h; // As reached top.

            int croppedWidth = rightmost - leftmost;
            int croppedHeight = bottommost - topmost;

            if (croppedWidth == 0) // No border on left or right
            {
                leftmost = 0;
                croppedWidth = w;
            }

            if (croppedHeight == 0) // No border on top or bottom
            {
                topmost = 0;
                croppedHeight = h;
            }

            try
            {
                var target = new Bitmap(croppedWidth, croppedHeight);
                using (Graphics g = Graphics.FromImage(target))
                {
                    g.DrawImage(bmp,
                      new RectangleF(0, 0, croppedWidth, croppedHeight),
                      new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
                      GraphicsUnit.Pixel);
                }
                return target;
            }
            catch (Exception ex)
            {
                throw new Exception(
                  string.Format("Values are topmost={0} btm={1} left={2} right={3} croppedWidth={4} croppedHeight={5}", topmost, bottommost, leftmost, rightmost, croppedWidth, croppedHeight),
                  ex);
            }
        }

        private static Bitmap ResizeCursorBitmap(Bitmap bitmap, Size size, CursorShift cursorShift)
        {
            if (size.Width > 32)
            {
                //shifting must occur
                Bitmap intermediateBitmap = new Bitmap(64, 64);
                Graphics intermediateGraphics = Graphics.FromImage(intermediateBitmap);
                if (cursorShift == CursorShift.LowerRight)
                    //place the mouse cursor in the lower right hand quadrant of the bitmap
                    intermediateGraphics.DrawImage(bitmap,
                        intermediateBitmap.Width / 2, intermediateBitmap.Height / 2);
                else if (cursorShift == CursorShift.Centered)
                    intermediateGraphics.DrawImage(bitmap,
                        intermediateBitmap.Width / 2 - bitmap.Width / 2,
                        intermediateBitmap.Height / 2 - bitmap.Height / 2);

                //now we have a shifted bitmap; use it to draw the resized cursor
                //Bitmap finalBitmap = new Bitmap(intermediateBitmap, size);    //normal quality
                Bitmap finalBitmap = new Bitmap(size.Width, size.Height);
                Graphics finalGraphics = Graphics.FromImage(finalBitmap);
                finalGraphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                finalGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                finalGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                finalGraphics.DrawImage(intermediateBitmap, 0, 0, finalBitmap.Width, finalBitmap.Height);
                return finalBitmap;
            }
            else
            {
                Bitmap newBitmap = new Bitmap(bitmap, size);
                return newBitmap;
            }
        }

        private static uint getResourceId(System.Windows.Forms.Cursor cursor)
        {
            FieldInfo fi = typeof(System.Windows.Forms.Cursor).GetField(
                "resourceId", BindingFlags.NonPublic | BindingFlags.Instance);
            object obj = fi.GetValue(cursor);
            return Convert.ToUInt32((int)obj);
        }

        private static void SetCursor(Bitmap bitmap, uint whichCursor)
        {
            IntPtr ptr = bitmap.GetHicon();
            bool retval = SetSystemCursor(ptr, whichCursor);
        }

    }
}

它的工作原理是获取提供的当前系统光标System.Windows.Forms.Cursors并使用Cursor.Draw. 然后将图像调整为所需的大小。这需要将光标图像移动到右下角(如箭头指针)或将光标图像置于较大图像内的中心(如 Cross 和 IBeam)。

如果需要,您可以使用自己的图像作为光标,绕过所有调整大小的代码。只需将位图提供给SetCursor.

一旦新的光标图像准备好,最后需要的数据就是我们要替换的光标的 ID。每个System.Windows.Forms.Cursor都包含此信息,但在私有变量中,因此使用反射来获取值。如果您希望避免反射,则可以构建这些值的表。有关值的列表,请参阅MSDN SetSystemCursor 。

要使用该类,只需调用

SystemCursors.SetSystemCursorsSize(128);
于 2013-11-22T17:43:06.907 回答
6

如果您正在使用 WPF,您可以创建自己的鼠标光标并分配它。但这不是系统范围的鼠标光标更改。这是一些可以解决问题的代码。在下面的代码中,我创建了一个 50x50 像素的光标。您可以将自己的形状绘制到RenderTargetBitmap

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Mouse.OverrideCursor = CreateCursor(50,50, Brushes.Gold, null);
    }

    Cursor CreateCursor(double rx, double ry, SolidColorBrush brush, Pen pen)
    {
        var vis = new DrawingVisual();
        using (var dc = vis.RenderOpen())
        {
            dc.DrawRectangle(brush, new Pen(Brushes.Black, 0.1), new Rect(0, 0, rx, ry));
            dc.Close();
        }
        var rtb = new RenderTargetBitmap(64, 64, 96, 96, PixelFormats.Pbgra32);
        rtb.Render(vis);

        using (var ms1 = new MemoryStream())
        {
            var penc = new PngBitmapEncoder();
            penc.Frames.Add(BitmapFrame.Create(rtb));
            penc.Save(ms1);

            var pngBytes = ms1.ToArray();
            var size = pngBytes.GetLength(0);

            //.cur format spec http://en.wikipedia.org/wiki/ICO_(file_format)
            using (var ms = new MemoryStream())
            {
                {//ICONDIR Structure
                    ms.Write(BitConverter.GetBytes((Int16)0), 0, 2);//Reserved must be zero; 2 bytes
                    ms.Write(BitConverter.GetBytes((Int16)2), 0, 2);//image type 1 = ico 2 = cur; 2 bytes
                    ms.Write(BitConverter.GetBytes((Int16)1), 0, 2);//number of images; 2 bytes
                }

                {//ICONDIRENTRY structure
                    ms.WriteByte(32); //image width in pixels
                    ms.WriteByte(32); //image height in pixels

                    ms.WriteByte(0); //Number of Colors in the color palette. Should be 0 if the image doesn't use a color palette
                    ms.WriteByte(0); //reserved must be 0

                    ms.Write(BitConverter.GetBytes((Int16)(rx / 2.0)), 0, 2);//2 bytes. In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left.
                    ms.Write(BitConverter.GetBytes((Int16)(ry / 2.0)), 0, 2);//2 bytes. In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top.

                    ms.Write(BitConverter.GetBytes(size), 0, 4);//Specifies the size of the image's data in bytes
                    ms.Write(BitConverter.GetBytes((Int32)22), 0, 4);//Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file
                }

                ms.Write(pngBytes, 0, size);//write the png data.
                ms.Seek(0, SeekOrigin.Begin);
                return new Cursor(ms);
            }
        }
    }

}
于 2012-12-03T17:48:50.420 回答
2

将这些导入添加到您的类中:

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr LoadImage(IntPtr hinst, string lpszName, uint uType, int cxDesired, int cyDesired, uint fuLoad);

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

阅读有关LoadImageSetSystemCursor的 msdn 文章,尤其是有关它们使用的参数的文章。

然后在您的代码中使用函数:

// "cursor.cur" - cursor image of any size you want
// 2 == IMAGE_CURSOR (from Winuser.h)
// cxDesired = 0 and cyDesired = 0, using original image size
// 0x8010 == LR_DEFAULTCOLOR | LR_SHARED | LR_LOADFROMFILE (from Winuser.h)
var ptr = LoadImage(IntPtr.Zero, "cursor.cur", 2, 0, 0, 0x8010);
if(ptr != IntPtr.Zero)
{
    SetSystemCursor(ptr, 32512); // 32512 == OCR_NORMAL (from Winuser.h)
}
于 2012-12-10T00:10:35.727 回答
0

我通过使用intruder的方式解决了这个问题。补充,API LoadImage不能Load.png。使用 System.drawing.BitMap. 像这样:Bitmap bmp = new Bitmap(str, true); IntPtr p = bmp.GetHicon(); SetSystemCursor(p, OCR_NORMAL);

于 2017-09-12T07:22:33.563 回答