当前位置:  开发笔记 > 前端 > 正文

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

如何解决《从.NET设置剪贴板时出现CLIPBRD_E_CANT_OPEN错误》经验,为你挑选了5个好方法。

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

Clipboard.SetText(str);

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



1> Robert Wagne..:

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

解决方案是在循环内多次尝试并在两者之间休眠.

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


@Mike:System.Windows.Forms.Clipboard有一个重试,但WPF的System.Windows.Clipboard没有.
`catch {}`是一种不好的做法.替换为`catch(COMException ex){const uint CLIPBRD_E_CANT_OPEN = 0x800401D0; if((uint)ex.ErrorCode!= CLIPBRD_E_CANT_OPEN)throw; }`
如果你看一下Clipboard.SetText的内部结构,至少在.NET 2.0 SP1上,你会发现它已经有了一个重试/等待循环.延迟100ms重试最多10次.
:D我是疯了,纯粹的疯狂……:D我花了2个小时没有任何参考,试图弄清楚如何使这件血腥的东西起作用,您告诉我,我应该尝试直到它在血腥的回路中起作用了?疯狂!:)

2> Tadmas..:

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

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

它恰好是终端服务跟踪剪贴板,在旧版本的Windows(Vista之前),你必须打开剪贴板,看看里面是什么......最终会阻止你.唯一的解决方案是等到终端服务关闭剪贴板并再试一次.

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



3> pr0gg3r..:

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

解:

从不使用

Clipboard.SetText(str);

改用

Clipboard.SetDataObject(str);


这很棒.为什么SetDataObject可以工作呢?

4> Yishai Galat..:

实际上可能还有另一个问题.框架调用(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以在应用程序关闭后保留数据之外,还没有找到一个好的解决方案.



5> Mar..:

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

在我制作的包装类之下.任何人都可以审查,并判断它是否正确.特别是当托管代码作为x64 app运行时(我在项目选项中使用Any 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;
    }
}

推荐阅读
coco2冰冰
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有