使用 Windows 剪贴板

Using Windows clipboard

我正在维护一个 WPF .NET 应用程序,一个用户在尝试从 DataGrid 控件进行复制时遇到了崩溃。我用谷歌搜索,发现这是因为他正在使用的其他应用程序可能没有调用 CloseClipboard()。 Excel 在相同情况下也给出了一条错误消息,消息为“我们无法在剪贴板上释放 space。另一个程序可能正在使用它。'

然后我写了另一个程序,它有一个按钮 Open/Close 剪贴板,然后我可以重现这个行为,我实现了相同的 'fix' 只是一条错误消息。

但是,出于好奇,我想看看 Google Chrome 是如何处理这个问题的。我使用我的程序锁定了剪贴板,并确认 Excel 正在抱怨。但是,Chrome 仍然优雅地处理了 copy/paste。我可以从 Chrome 复制到记事本,但不能从 to/from 记事本和 Excel 复制。有趣的是,我实际上可以从记事本复制到 Chrome。

我一直在浏览Chromium的源代码,但找不到copy/paste逻辑的实现。有谁知道 Chrome 是如何处理的?

我怎么可能无法在记事本中 copy/paste,但从记事本到 Chrome 都可以正常工作?

在我能在网上找到的所有实现中,解决方案似乎与我现在得到的相同:在一秒钟左右的时间内尝试几次,然后放弃。

作为参考,这是我的剪贴板储物柜背后的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ClipboardLocker
{
    public partial class MainWindow : Window
    {
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        static extern IntPtr GetOpenClipboardWindow();

        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
        static extern bool OpenClipboard(IntPtr hWndNewOwner);

        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
        static extern bool CloseClipboard();

        bool clipboardLocked = false;

        public MainWindow()
        {
            InitializeComponent();

            double screenWidth = System.Windows.SystemParameters.PrimaryScreenWidth;
            double screenHeight = System.Windows.SystemParameters.PrimaryScreenHeight;
            double windowWidth = this.Width;
            double windowHeight = this.Height;
            this.Left = (screenWidth / 2) - (windowWidth / 2);
            this.Top = (screenHeight / 2) - (windowHeight / 2);
        }

        private void LockButton_Click(object sender, RoutedEventArgs e)
        {
            if (!clipboardLocked)
            {
                IntPtr handle = GetOpenClipboardWindow();
                OpenClipboard(handle);
                clipboardLocked = true;
                LockButton.Content = "Unlock";
            }
            else
            {
                CloseClipboard();
                clipboardLocked = false;
                LockButton.Content = "Lock";
            }
        }
    }
}

[更新]

经过更多实验,Chrome 似乎能够以某种方式解锁剪贴板。在我完成 Chrome 中的 copy/paste 之后,它在其他应用程序中也可以正常工作。 Chromw 'steal' 如何从我的储物柜程序中获取剪贴板? MSDN 提到 ChangeClipboardChain。我会尝试并试验一下。

将您的数据写入 JSON 文件而不是剪贴板。因此,即使程序将关闭,您也可以访问值。

为了接管剪贴板,我首先使用 NULL 参数调用 OpenClipboard。这个 'steals' 来自 window 当前保存的剪贴板。然后我通过调用 CloseClipboard 关闭它。这现在可以工作了,因为剪贴板将像 MSDN 所说的那样 'the open clipboard is associated with the current task'。

现在剪贴板已正确关闭,可以调用 WPF 或 Windows Forms 的 Clipboard.SetText() 了。

这当然不是一个很好的解决方案,但我相信这是 Windows 在这种情况下给我们的最佳选择。

这是我的ClipboardHelper,如果其他人遇到这个问题。

static class ClipboardHelper
{
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    static extern IntPtr GetOpenClipboardWindow();

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern bool OpenClipboard(IntPtr hWndNewOwner);

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern bool CloseClipboard();

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

    public static bool SetText(string text)
    {
        IntPtr hWnd = GetOpenClipboardWindow(); // Gets the HWND of the window that currently owns the clipboard

        if (hWnd == null)   // If no window currently owns the clipboard, just go ahead and set the text.
        {
            System.Windows.Forms.Clipboard.SetText(text);
        }
        else
        {
            OpenClipboard(IntPtr.Zero);
            CloseClipboard();
            System.Windows.Forms.Clipboard.SetText(text);
        }
        return true;
    }
}