【问题标题】:Using Windows clipboard使用 Windows 剪贴板
【发布时间】:2017-02-11 10:11:39
【问题描述】:

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

然后我编写了另一个程序,该程序有一个打开/关闭剪贴板的按钮,然后我可以重现此行为,并且我实现了相同的“修复”,但只有一条错误消息。

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

我一直在浏览 Chromium 的源代码,但找不到复制/粘贴逻辑的实现。有谁知道 Chrome 是如何处理这个问题的?

我怎么可能无法在记事本中复制/粘贴,但从记事本到 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 中完成复制/粘贴后,它也可以在其他应用程序中使用。 Chromw 如何从我的储物柜程序中“窃取”剪贴板? MSDN 提到了ChangeClipboardChain。我会尝试并尝试一下。

【问题讨论】:

  • 您是否使用 WinForms 剪贴板类 (msdn.microsoft.com/en-us/library/…) 进行了测试并查看它们的行为是否有所不同?不知道会不会,只是一个想法。
  • @Yushatak 是的,WinForms 剪贴板方法使用“尝试几次”循环,而 WPF 剪贴板方法没有,只是立即抛出异常。据我所知,没有其他区别。
  • 看起来 CloseClipboard 没有参数,所以也许你在处理该异常的地方尝试调用 CloseClipboard 并重试?
  • @Yushatak 这是我的第一个希望,但据我所知,它没有做任何事情。另外,请参阅我的更新。

标签: c# wpf google-chrome clipboard


【解决方案1】:

要接管剪贴板,我首先使用 NULL 参数调用 OpenClipboard。这会从当前保存的任何窗口中“窃取”剪贴板。然后我通过调用CloseClipboard 关闭它。这现在可以工作了,因为剪贴板将是 MSDN 所说的“打开的剪贴板与当前任务相关联”。

现在剪贴板已正确关闭,调用 WPF 或 Windows 窗体的 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;
    }
}

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我以为他没有将其用作数据存储,而是专门需要剪贴板支持?
    • 是的,用例是在我的应用程序和 excel 之间复制/粘贴。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 2013-05-04
    • 1970-01-01
    • 2018-03-09
    相关资源
    最近更新 更多