如何在不使用互操作的情况下创建动态鼠标光标
本文关键字:创建 情况下 动态 鼠标 光标 互操作 | 更新日期: 2023-09-27 18:10:03
我有一个应用程序,我有兴趣最终移植到mono,所以我试图避免使用p/invoke来完成这个任务。
我想动态加载游标,就像我在应用程序中动态生成的位图一样。据我所知,在不使用p/invoke的情况下,最安全的方法是创建一个.cur文件,然后我可以将其加载到内存流并使用Cursor(stream)构造函数。但是我不知道如何创建。cur文件。
我在微软知识库上找到了这篇文章,它解释了这种格式,但我不确定在没有互操作调用的情况下如何使用它。如何在Windows XP中创建Alpha混合光标或图标
其他人有我可以用来完成这项任务的托管解决方案吗?
我参考了这个帖子:如何在winform
中将光标替换为位图你可以创建一个数组静态游标,并使用Timer来改变它
使动态鼠标光标生效!
从位图创建静态游标是如此简单和不使用互操作:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Icon icon = this.Icon;
Bitmap bmp = icon.ToBitmap();
Cursor cur = new Cursor(bmp.GetHicon());
this.Cursor = cur;
}
}
下面是一个用VB编写的示例。NET动态创建位图并将其转换为光标。不知道7年多以后谁会从中受益,但这里是:
Private Function GetCircleCursor(iDiameter As Integer) As Cursor
Dim oBitmap As Bitmap = New Bitmap(Convert.ToInt32(iDiameter), Convert.ToInt32(iDiameter), System.Drawing.Imaging.PixelFormat.Format32bppArgb)
Using g As System.Drawing.Graphics = Graphics.FromImage(oBitmap)
g.Clear(Color.Transparent)
g.DrawEllipse(New System.Drawing.Pen(Color.White, 3), New Rectangle(0, 0, iDiameter, iDiameter))
g.DrawEllipse(New System.Drawing.Pen(Color.Black, 1), New Rectangle(0, 0, iDiameter, iDiameter))
End Using
Return New Cursor(oBitmap.GetHicon)
End Function
下面的代码创建了一个充满.cur格式数据的流,我在Ubuntu和Mono中成功地测试了它。但是。net在不使用Win32 API的情况下,只支持自定义游标的黑色和白色:
Cursor类不支持动画游标。(ANI文件)或使用非黑色和白色的光标。
因此在Unix的Mono中,一个游标被奖励为
- 只显示黑白两色
- 没有半透明像素,只有完全不透明/完全透明的像素
¯'_()_/¯
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace StackoverflowExample
{
public static class CursorHelper
{
public static Cursor CreateCursor(Bitmap bmp, Point hotSpot)
{
// https://en.wikipedia.org/wiki/ICO_(file_format)
var bmpData = bmp.LockBits(new Rectangle(default, bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
try
{
int numBytes = bmpData.Stride * bmpData.Height;
var bgraValues = new byte[numBytes];
Marshal.Copy(bmpData.Scan0, bgraValues, 0, numBytes);
int max = Math.Max(bmp.Width, bmp.Height);
if (max > 256)
throw new NotSupportedException();
byte iconSizeByte = _sizes.FirstOrDefault(s => s >= max); // 0 means 256
int iconSizeI = iconSizeByte == 0 ? 256 : iconSizeByte;
const int bytesPerPixel = 4;
const int bytesPerPixelSource = 4;
byte[] emptyPixel = new byte[bytesPerPixel];
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
writer.Write((ushort)0); // idReserved
writer.Write((ushort)2); // idType, 1 = .ico 2 = .cur
writer.Write((ushort)1); // idCount
writer.Write(iconSizeByte);
writer.Write(iconSizeByte);
writer.Write((byte)0); // colorCount
writer.Write((byte)0); // reserved
writer.Write((ushort)hotSpot.X);
writer.Write((ushort)hotSpot.Y);
var pixelsCount = iconSizeI * iconSizeI;
var xorLength = pixelsCount * bytesPerPixel;
var andLength = pixelsCount / 8 * 2;
writer.Write((uint)(40 + xorLength + andLength)); // sizeInBytes
writer.Write((uint)stream.Position + sizeof(uint)); // fileOffset = 22 = 0x16
writer.Write(40u); // cursorInfoHeader.biSize
writer.Write((int)iconSizeI); // cursorInfoHeader.biWidth
writer.Write((int)iconSizeI * 2); // cursorInfoHeader.biHeight
writer.Write((ushort)1); // cursorInfoHeader.biPlanes
writer.Write((ushort)(8 * bytesPerPixel)); // cursorInfoHeader.biBitCount
writer.Write(0u); // cursorInfoHeader.biCompression
writer.Write(0u); // cursorInfoHeader.biSizeImage
writer.Write(0); // cursorInfoHeader.biXPelsPerMeter;
writer.Write(0); // cursorInfoHeader.biYPelsPerMeter;
writer.Write(0u); // cursorInfoHeader.biClrUsed = binaryReader2.ReadUInt32();
writer.Write(0u); // cursorInfoHeader.biClrImportant = binaryReader2.ReadUInt32();
using (var andMask = new MemoryStream(andLength))
{
byte def = 255;
for (int j = 0; j < iconSizeI; j++)
{
int y = iconSizeI - 1 - j;
byte curByte = def;
for (int i = 0; i < iconSizeI; i++)
{
var bitIndex = 7 - i % 8;
if (i < bmp.Width && y < bmp.Height)
{
var p = y * bmpData.Stride + i * bytesPerPixelSource;
stream.Write(bgraValues, p, bytesPerPixel);
if (bgraValues[p + 3] > 0)
curByte = (byte)(curByte & ~(1 << bitIndex));
}
else
stream.Write(emptyPixel, 0, emptyPixel.Length);
if (bitIndex == 0)
{
andMask.WriteByte(curByte);
curByte = def;
}
}
}
for (int j = 0; j < iconSizeI; j++)
for (int b = 0; b < iconSizeI / 8; b++)
andMask.WriteByte(def);
andMask.Seek(0, SeekOrigin.Begin);
andMask.CopyTo(stream);
}
stream.Seek(0, SeekOrigin.Begin);
// debug
// File.WriteAllBytes("/home/kolia/Documents/stream", stream.ToArray());
// stream.Seek(0, SeekOrigin.Begin);
var cursor = new Cursor(stream);
return cursor;
}
}
finally
{
bmp.UnlockBits(bmpData);
}
}
private static readonly byte[] _sizes = { 16, 32, 64, 128 };
}
}
Simple: YOU CAN NOT -你要求的功能不是。net框架的一部分,所以你需要使用native。
如果您的应用程序需要移植到mono,将这些代码隔离在一个类中,这样您就可以像使用编译器开关一样关闭它-不难。