关于wpf:从.NET设置剪贴板时出现CLIPBRD_E_CANT_OPEN错误

关于wpf:从.NET设置剪贴板时出现CLIPBRD_E_CANT_OPEN错误

CLIPBRD_E_CANT_OPEN error when setting the Clipboard from .NET

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

1
Clipboard.SetText(str);

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


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

解决方案是在一个循环中尝试多次,然后在两者之间睡眠。

1
2
3
4
5
6
7
8
9
10
for (int i = 0; i < 10; i++)
{
    try
    {
        Clipboard.SetText(str);
        return;
    }
    catch { }
    System.Threading.Thread.Sleep(10);
}

实际上,我认为这是Win32 API的错误。

要在剪贴板中设置数据,您必须先打开它。一次只能打开一个进程剪贴板。因此,当您检查时,如果另一个进程由于某种原因打开了剪贴板,则您尝试打开它的尝试将失败。

碰巧的是,终端服务跟踪剪贴板,而在Windows的较早版本(Vista之前的版本)上,您必须打开剪贴板才能查看其中的内容...最终将您拒之门外。唯一的解决方案是等待终端服务关闭剪贴板,然后重试。

重要的是要意识到,这并非特定于终端服务:它可以发生在任何事情上。在Win32中使用剪贴板是一个巨大的竞争条件。但是,由于设计使您只能响应用户输入而随意修改剪贴板,因此通常不会出现问题。


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

解:

永不使用

1
Clipboard.SetText(str);

用代替

1
Clipboard.SetDataObject(str);

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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来保存应用程序关闭后的数据外,没有找到一个好的解决方案。


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

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

谢谢!

这是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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))。

我用

1
ApplicationCommands.Copy.Execute(null, myDataGrid);

解决方法是先清除剪贴板

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

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

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

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

参考https://docs.microsoft.com/zh-cn/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_


推荐阅读

    linux命令设置密码?

    linux命令设置密码?,密码,系统,服务,软件,地址,电脑,流程,管理,用户,命令,问

    linux命令行设置语言?

    linux命令行设置语言?,系统,管理,环境,国家,工具,电脑,软件,文化,底部,语言,l

    linux设置壁纸的命令?

    linux设置壁纸的命令?,图片,系统,电脑,照片,位置,终端,颜色,字体,单击,壁纸,

    linux显示错误命令?

    linux显示错误命令?,信息,系统,电脑,状态,时间,环境,命令,搜狐,密码,异常,虚

    linux退出错误命令的?

    linux退出错误命令的?,系统,电脑,环境,命令,位置,管理,工具,设备,终端,进程,L

    linux恢复命令行设置?

    linux恢复命令行设置?,系统,工作,密码,信息,工具,地址,电脑,命令,情况,地方,

    linux命令提示设置?

    linux命令提示设置?,系统,工作,地址,信息,命令,软件,目录,管理,变量,文件,Lin

    linux设置命令ip?

    linux设置命令ip?,地址,系统,代码,命令,密码,网卡,终端,计算机,测试,网关,lin

    linux设置man命令?

    linux设置man命令?,信息,系统,工具,工作,地址,命令,基础,地方,基本知识,技术

    linux鼠标设置命令?

    linux鼠标设置命令?,系统,软件,环境,产品,设备,数据,网络,网址,工具,情况,lin

    linux参数设置命令?

    linux参数设置命令?,网络,系统,地址,工作,信息,管理,服务,名称,状态,命令,在l

    linux设置电源的命令?

    linux设置电源的命令?,系统,信息,管理,设备,扩大,时间,设计,电脑,代码,位置,l

    linux没有该命令错误?

    linux没有该命令错误?,系统,第一,环境,命令,分析,软件,异常,文件,目录,空格,

    linux设置路由器命令?

    linux设置路由器命令?,网络,信息,代码,地址,电脑,工作,环境,系统,密码,路由,l

    linux设置根命令过短?

    linux设置根命令过短?,系统,工作,软件,地址,命令,代码,情况,管理,基础,位置,3

    linux设置路由器命令?

    linux设置路由器命令?,网络,信息,代码,地址,电脑,工作,环境,系统,密码,路由,l

    linux命令行设置字体?

    linux命令行设置字体?,系统,工具,终端,软件,电脑,数字,位置,等级,字体,命令,L

    linux命令错误代码?

    linux命令错误代码?,系统,密码,电脑,网络,手机,网址,软件,代码,设备,老板,Lin

    linux命令快捷设置?

    linux命令快捷设置?,第一,系统,名称,终端,发行,首页,命令,快捷键,窗口,桌面,l

    linux命令设置扩展屏?

    linux命令设置扩展屏?,工具,系统,电脑,技术,软件,装备,情况,工作,命令,显示