使用P/调用GlobalSize时堆损坏

本文关键字:损坏 GlobalSize 调用 使用 | 更新日期: 2023-09-27 18:15:28

我正在用c#开发一个与Windows剪贴板相关的应用程序。由于Dot Net剪贴板库有很多缺点(STA,无法打开剪贴板等),我决定直接使用系统API。

我想做的是备份每种格式的数据(尽可能多的,如果不是全部的话),将它们放入堆栈中,然后再次弹出剪贴板。如果你试过自动热键,这就是ClipboardAll的作用

当我试图获取数据时出现问题,这是我如何为Pinvoke编写的:

[DllImport("user32.dll", SetLastError = true)]
public static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool CloseClipboard();
[DllImport("user32.dll")]
public static extern IntPtr GetClipboardData(uint uFormat);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint EnumClipboardFormats(uint format);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("kernel32.dll")]
public static extern UIntPtr GlobalSize(IntPtr hMem);

我是这样使用它们的:

var x = GetFormats(); // a list returned from EnumClipboardFormats
foreach (uint format in x)
{
    IntPtr p = NativeMethods.GetClipboardData(format);
    int length = (int) NativeMethods.GlobalSize(p);
    IntPtr memPtr = NativeMethods.GlobalLock(p);
    byte[] buffer = new byte[length];
    Marshal.Copy(memPtr, buffer, 0, length);
    NativeMethods.GlobalFree(p);
    dataObject.Data[format] = buffer;//My data object
}
WinApi.CloseClipBoard();
return dataObject;

然后当我在Microsoft Office Excel中复制一些东西时,我得到:"GlobalSize"方法的堆损坏异常。

在我尝试跳过触发异常的格式之后:

if(format==14||format==2) continue;

那么一切都很好。

格式2代表CF_BITMAP和格式14是CF_ENHMETAFILE,我猜他们可能在剪贴板中使用所谓的"延迟渲染"技术,也就是说他们的数据在第一次创建时为空,当有人试图获得它们时被填充,我想知道数据的句柄在渲染调用时是否会改变,但我不确定。

有没有人可以帮忙,找出原因,并告诉我如何修复

使用P/调用GlobalSize时堆损坏

来自文档:

剪贴板控制GetClipboardData函数返回的句柄,而不是应用程序。应用程序应该立即复制数据。应用程序不能释放句柄,也不能将其锁定。应用程序不能在调用EmptyClipboard或CloseClipboard函数之后使用句柄,也不能在调用SetClipboardData函数之后使用相同的剪贴板格式。

你没有遵守这些规则。而不是解锁你释放手柄。别松开把手,把它打开。

根本不执行任何错误检查。因此,也许有些API调用失败了,而您并不知道。也许GetClipboardData返回NULL。你怎么知道?始终检查返回值是否存在错误条件。

程序中可能有其他错误,您没有显示所有的代码。例如,我们看不到你打开剪贴板的位置。