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

x33g5p2x  于 2022-11-26  发布在  .NET
关注(0)|答案(8)|浏览(330)

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

Clipboard.SetText(str);

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

q9yhzks0

q9yhzks01#

这是由终端服务剪贴板中的错误/功能(以及可能的其他原因)和剪贴板的.NET实现引起的。打开剪贴板时的延迟导致错误,通常在几毫秒内消失。
解决方案是在循环中尝试多次,并在中间休眠。

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

23c0lvtd2#

实际上,我认为这是fault of the Win32 API
要在剪贴板中设置数据,您必须先执行open it。一次只能有一个进程打开剪贴板。因此,当您检查时,如果另一个进程 * 由于任何原因 * 打开了剪贴板,您打开它的尝试将失败。
终端服务会跟踪剪贴板,而在旧版本的Windows(pre-Vista)上,您必须打开剪贴板才能看到里面的内容...这最终会阻止您。唯一的解决方案是等待终端服务关闭剪贴板,然后重试。
但是,必须认识到这并不特定于终端服务:在Win32中使用剪贴板是一个巨大的竞争条件。但是,由于根据设计,您只应该在响应用户输入时使用剪贴板,因此这通常不会出现问题。

vq8itlhq

vq8itlhq3#

我知道这个问题是旧的,但问题仍然存在。如前所述,当系统剪贴板被另一个进程阻塞时,就会出现此异常。不幸的是,有许多剪切工具、截图程序和文件复制工具都可以阻塞Windows剪贴板。所以当您的PC上安装了Clipboard.SetText(str)时,每次尝试使用该工具时都会出现此异常。
解决方法:
从不使用

Clipboard.SetText(str);

改用

Clipboard.SetDataObject(str);
n3h0vuf2

n3h0vuf24#

实际上,可能还有另一个问题。框架调用(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调用。
在内部,这会触发两个对win32api的调用,一个用于设置数据,另一个用于从应用程序中刷新数据,以便在应用程序关闭后可用。
我见过几个应用程序(一些chrome插件和一个下载管理器)监听剪贴板事件。一旦第一个调用命中,应用程序将打开剪贴板查看数据,第二个调用flush将失败。
除了编写我自己的剪贴板类(使用直接的win32 API)或使用false直接调用setDataObject以在应用程序关闭后保留数据外,还没有找到一个好的解决方案。

q3aa0525

q3aa05255#

我使用原生Win32函数为自己的应用解决了这个问题:打开剪贴板()、关闭剪贴板()和设置剪贴板数据()。
下面是我制作的 Package 类。任何人查看它并告诉它是否正确。特别是当托管代码作为x64应用运行时(我在项目选项中使用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;
    }
}
ruarlubt

ruarlubt6#

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

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

这将尝试将yourText复制到剪贴板,在应用程序存在后将保留,最多尝试10次,每次尝试之间将等待100毫秒。
请参阅https://learn.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_

xvw2m8pv

xvw2m8pv7#

我在WPF应用程序中遇到了这种情况。OpenClipboard失败(HRESULT异常:0x800401D0(夹线无法打开))。
我使用

ApplicationCommands.Copy.Execute(null, myDataGrid);

解决方法是先清除剪贴板

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

dgtucam18#

在WPF中,Cliboard.SetText和Cliboard.SetDataObject的区别在于文本没有被复制到剪贴板,只有指针。我检查了源代码。如果我们调用SetDataObject(data,true)剪贴板.同花()也将被调用。由于这一点,文本或数据是可用的,即使在关闭应用程序。我认为Windows应用程序只调用Flush()。由于这一点,它节省了内存,同时允许在没有活动应用程序的情况下访问数据。
复制到剪贴板:

IDataObject CopyStringToClipboard(string s)
{
  var dataObject = new DataObject(s);
  Clipboard.SetDataObject(dataObject, false);
  return dataObject;
}

应用程序或窗口关闭时的代码:

try
{
  if (Clipboard.IsCurrent(clipboardData))
    Clipboard.Flush();
}
catch (COMException ex) {}

clipboardData是窗口类字段或静态变量。

相关问题