在创建自定义光标时,如何避免GDI+中的一般错误



我使用以下代码生成一个自定义光标,该光标取决于win窗体控件内的鼠标位置。光标变成一条指向控件中心的线。在几秒钟内,一切都很好,然后我收到了一条毫无帮助的消息:-

引发异常:System.Drawing.Common.dll中的"System.Runtime.InteropServices.ExternalException"System.Drawing.Common.dll中出现类型为"System.Runtime.InteropServices.ExternalException"的异常,但未在用户代码中进行处理GDI+中出现一般性错误。

此错误似乎与试图在指针仍在使用时清理指针的垃圾收集器有关(尽管可能是其他原因(。正如你将看到的,我已经尝试将指针作为一个属性,这样它就不会被清理,但这似乎没有帮助。

任何关于如何避免这个错误的想法都是非常受欢迎的。

public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}

public partial class CursorTest : UserControl
{
public CursorTest()
{
InitializeComponent();
}

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
[DllImport("user32.dll")]
static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
private static extern bool DestroyIcon(IntPtr hIcon);        
bool IsBusy { get; set; } = false;
IntPtr ptr { get; set; }
/// <summary>
/// Create a 32x32 cursor from a bitmap, with the hot spot in the middle
/// </summary>
public void CreateCursor(Bitmap bmp)
{
ptr = bmp.GetHicon();
IconInfo tmp = new IconInfo();
GetIconInfo(ptr, ref tmp);
tmp.xHotspot = 16;
tmp.yHotspot = 16;
tmp.fIcon = false;
ptr = CreateIconIndirect(ref tmp);
this.Cursor = new Cursor(ptr);// Error Happens here
DestroyIcon(ptr);            
}

private void GenerateCursorFromPostion(Point e)
{
if (IsBusy)
{
System.Diagnostics.Trace.WriteLine("Busy!!!");
return;
}
IsBusy = true;
float x = 16 * (Width / 2.0f - e.X);
float y = 16 * (Height / 2.0f - e.Y);
PointF st = new PointF(x + 16, y + 16);
PointF ed = new PointF(16 - x, 16 - y);
Bitmap bmp = new Bitmap(32, 32);
Graphics g = Graphics.FromImage(bmp);
g.DrawLine(Pens.Black, st, ed);

CreateCursor(bmp);
g.Dispose();           
bmp.Dispose();
IsBusy = false;
}
private void CursorTest_MouseMove(object sender, MouseEventArgs e)
{
GenerateCursorFromPostion(e.Location);
}
}

您当前的代码有很多问题。

您的主要问题似乎是您正在破坏光标的图标。使用句柄创建Cursor时,句柄不会被复制,而是直接使用。相反,当控件被释放时,或者当光标被另一个替换时,您需要释放它。

您还需要从GetHicon中处理句柄,BitmapGraphics需要一个using,并且您需要处理各种错误。

public partial class CursorTest : UserControl
{
public CursorTest()
{
InitializeComponent();
}

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetIconInfo(IntPtr hIcon, out IconInfo pIconInfo);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr CreateIconIndirect(in IconInfo icon);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyIcon(IntPtr hIcon);        
bool IsBusy { get; set; } = false;
IntPtr ptr { get; set; }
protected override Dispose(bool disposing)
{
base.Dispose(disposing);
DestroyIcon(ptr);
}
/// <summary>
/// Create a 32x32 cursor from a bitmap, with the hot spot in the middle
/// </summary>
public void CreateCursor(Bitmap bmp)
{
var original = IntPtr.Zero;
try
{
original = bmp.GetHicon();
if(!GetIconInfo(original, out var tmp))
throw new Win32Exception(Marshal.GetLastWin32Error());
tmp.xHotspot = 16;
tmp.yHotspot = 16;
tmp.fIcon = false;
var newPtr = CreateIconIndirect(in tmp);
if (newPtr != IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
this.Cursor = new Cursor(newPtr);
DestroyIcon(ptr);
ptr = newPtr;
}
finally
{
if(original != IntPtr.Zero)
DestroyIcon(original);
}
}
private void GenerateCursorFromPostion(Point e)
{
if (IsBusy)
{
System.Diagnostics.Trace.WriteLine("Busy!!!");
return;
}
IsBusy = true;
float x = 16 * (Width / 2.0f - e.X);
float y = 16 * (Height / 2.0f - e.Y);
PointF st = new PointF(x + 16, y + 16);
PointF ed = new PointF(16 - x, 16 - y);
using (Bitmap bmp = new Bitmap(32, 32))
{
using (Graphics g = Graphics.FromImage(bmp))
g.DrawLine(Pens.Black, st, ed);

CreateCursor(bmp);
}
IsBusy = false;
}
private void CursorTest_MouseMove(object sender, MouseEventArgs e)
{
GenerateCursorFromPostion(e.Location);
}
}

最新更新