为什么以下代码有时会导致内容为“CLIPBRD_E_CANT_OPEN”的异常:

Clipboard.SetText(str);

这通常发生在应用程序中第一次使用剪贴板时,而不是之后。

有帮助吗?

解决方案

其实我觉得这就是 Win32 API 的错误.

要在剪贴板中设置数据,您必须 打开它 第一的。一次只有一个进程可以打开剪贴板。因此,当您检查另一个进程是否打开了剪贴板时 出于任何原因, ,您尝试打开它将会失败。

碰巧终端服务会跟踪剪贴板,并且在旧版本的 Windows(Vista 之前)上,您必须打开剪贴板才能查看里面的内容...这最终会阻止你。唯一的解决方案是等到终端服务关闭剪贴板并重试。

不过,重要的是要认识到这并不是特定于终端服务的:任何事情都可能发生。在 Win32 中使用剪贴板是一个巨大的竞争条件。但是,由于按照设计,您只应该使用剪贴板来响应用户输入,因此这通常不会出现问题。

其他提示

这是由终端服务剪贴板(以及可能的其他问题)和剪贴板的 .NET 实现中的错误/功能引起的。打开剪贴板的延迟会导致错误,该错误通常会在几毫秒内消失。

解决方案是在一个循环内尝试多次并在中间休眠。

for (int i = 0; i < 10; i++)
{
    try
    {
        Clipboard.SetText(str);
        return;
    }
    catch { }
    System.Threading.Thread.Sleep(10);
} 

我知道这个问题很老了,但问题仍然存在。如前所述,当系统剪贴板被另一个进程阻塞时,会发生此异常。不幸的是,有许多截图工具、屏幕截图程序和文件复制工具可能会阻止 Windows 剪贴板。所以每次你尝试使用时都会遇到异常 Clipboard.SetText(str) 当您的电脑上安装了此类工具时。

解决方案:

从不使用

Clipboard.SetText(str);

改用

Clipboard.SetDataObject(str);

实际上可能还有另一个问题。框架调用(WPF 和 winform 风格)如下所示(代码来自 Reflector):

private static void SetDataInternal(string format, object data)
{
    bool flag;
    if (IsDataFormatAutoConvert(format))
    {
        flag = true;
    }
    else
    {
        flag = false;
    }
    IDataObject obj2 = new DataObject();
    obj2.SetData(format, data, flag);
    SetDataObject(obj2, true);
}

请注意,在这种情况下,SetDataObject 始终以 true 调用。

在内部触发对 win32 api 的两次调用,一次用于设置数据,另一次用于从应用程序中刷新数据,以便在应用程序关闭后可用。

我见过几个监听剪贴板事件的应用程序(一些 Chrome 插件和一个下载管理器)。一旦第一次调用命中,应用程序将打开剪贴板以查看数据,第二次调用刷新将失败。

除了编写我自己的使用直接 win32 API 的剪贴板类或直接使用 false 调用 setDataObject 以在应用程序关闭后保留数据之外,还没有找到好的解决方案。

我使用本机 Win32 函数为我自己的应用程序解决了这个问题:OpenClipboard()、CloseClipboard() 和 SetClipboardData()。

在我制作的包装类下面。任何人都可以吗 审查它并 告诉它是否正确. 。特别是当托管代码作为 x64 应用程序运行时(我在项目选项中使用任何 CPU)。 当我从 x64 应用程序链接到 x86 库时会发生什么?

谢谢你!

这是代码:

public static class ClipboardNative
{
    [DllImport("user32.dll")]
    private static extern bool OpenClipboard(IntPtr hWndNewOwner);

    [DllImport("user32.dll")]
    private static extern bool CloseClipboard();

    [DllImport("user32.dll")]
    private static extern bool SetClipboardData(uint uFormat, IntPtr data);

    private const uint CF_UNICODETEXT = 13;

    public static bool CopyTextToClipboard(string text)
    {
        if (!OpenClipboard(IntPtr.Zero)){
            return false;
        }

        var global = Marshal.StringToHGlobalUni(text);

        SetClipboardData(CF_UNICODETEXT, global);
        CloseClipboard();

        //-------------------------------------------
        // Not sure, but it looks like we do not need 
        // to free HGLOBAL because Clipboard is now 
        // responsible for the copied data. (?)
        //
        // Otherwise the second call will crash
        // the app with a Win32 exception 
        // inside OpenClipboard() function
        //-------------------------------------------
        // Marshal.FreeHGlobal(global);

        return true;
    }
}

我的 WPF 应用程序中发生过这种情况。我遇到 OpenClipboard 失败(HRESULT 异常:0x800401D0(CLIPBRD_E_CANT_OPEN))。

我用

ApplicationCommands.Copy.Execute(null, myDataGrid);

解决办法是先清除剪贴板

Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top