使用P / Invoked GlobalSize时堆已损坏

时间:2016-10-28 16:04:52

标签: c# .net windows winapi pinvoke

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

我想要做的是备份每种格式的数据(尽可能多,如果不是全部),将它们推入堆栈,然后再次弹出到剪贴板。如果您尝试过AutoHotKey,那就是" 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中复制某些内容时,我得到了: A Heap corrupted Exception at "GlobalSize" Method.

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

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

然后一切正常。

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

所以有人可以提供帮助,找出原因,并告诉我如何解决。

1 个答案:

答案 0 :(得分:2)

来自文档:

  

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

您未能遵守这些规则。而不是解锁你释放手柄。不要释放手柄,解锁它。

您根本不执行任何错误检查。所以也许有些API调用失败了,你不知道。也许GetClipboardData正在返回NULL。你怎么知道的?始终检查错误条件的返回值。

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