从.NET设置剪贴板时出现CLIPBRD_E_CANT_OPEN错误

时间:2008-09-16 01:50:35

标签: .net wpf clipboard

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

Clipboard.SetText(str);

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

7 个答案:

答案 0 :(得分:35)

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

解决方案是在循环内多次尝试并在两者之间睡觉。

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

答案 1 :(得分:32)

实际上,我认为这是fault of the Win32 API

要在剪贴板中设置数据,您必须先open it。只有一个进程可以一次打开剪贴板。因此,当您检查时,如果另一个进程因任何原因打开了,则您尝试打开它将失败。

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

重要的是要意识到这不是特定于终端服务,但它可以发生任何事情。在Win32中使用剪贴板是一个巨大的竞争条件。但是,由于设计你只应该用剪贴板来回应用户输入,这通常不会出现问题。

答案 2 :(得分:18)

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

解决方案:

永远不要使用

Clipboard.SetText(str);

改为使用

Clipboard.SetDataObject(str);

答案 3 :(得分:7)

实际上可能还有另一个问题。框架调用(WPF和winform风格)就像这样(代码来自反射器):

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);
}

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

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

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

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

答案 4 :(得分:5)

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

在我制作的包装类之下。任何人都可以查看它并告诉它是否正确。特别是当托管代码作为x64 app运行时(我在项目选项中使用Any CPU)。 当我从x64 app链接到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;
    }
}

答案 5 :(得分:1)

使用WinForms版本(是的,在WPF应用程序中使用WinForms没有危害),它可以处理您需要的所有内容:

System.Windows.Forms.SetDataObject(yourText, true, 10, 100);

这将尝试将您的文本复制到剪贴板,在您的应用存在后仍保留,最多尝试10次,每次尝试之间等待100毫秒。

参考https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.clipboard.setdataobject?view=netframework-4.7.2#System_Windows_Forms_Clipboard_SetDataObject_System_Object_System_Boolean_System_Int32_System_Int32 _

答案 6 :(得分:0)

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

我用

ApplicationCommands.Copy.Execute(null, myDataGrid);

解决方案是首先清除剪贴板

Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);