65.9K
CodeProject 正在变化。 阅读更多。
Home

剪贴板编解码器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (6投票s)

2012年9月20日

CPOL

7分钟阅读

viewsIcon

27281

downloadIcon

882

用于混淆剪贴板上的字符串和小图像的工具。可以查看剪贴板以了解当前内容的DataFormats。ASCII85编解码器的C#实现。

Clipboard Codec showing Peek results

Clipboard Codec showing Encode results

引言

能够混淆剪贴板是一件好事。您可以复制、编码并粘贴您希望保护一定隐私的文本片段或图像。此工具可以让您做到这一点。之后,在电子邮件的另一端或您的秘密实验室中,您可以复制、解码并粘贴片段以恢复其原始形式。

了解剪贴板上的内容类型也很有用。您会发现,除了文本和图像之外,还有许多剪贴板对象格式。此工具允许您查看当前剪贴板内容兼容的System.Windows.Forms.DataFormats。上面剪贴板编码解码的第一个截图显示了一个来自CodeProject网页的小图像的兼容DataFormats。

背景

实际上,还有第二个理由来扰乱剪贴板的内容。如果我们选择正确的编码方法,我们不仅可以让普通窃听者无法使用数据,还可以将二进制代码转换为一种只使用ASCII字符的形式,因此可以经过只允许ASCII数据的通道,例如纯文本电子邮件或HTML表单数据。当然,我们需要为每种编码提供解码方法,以便最终能够“解扰”内容。

Base64编码就是为此而发明的。请参阅RFC 1421(已废弃)、2045(MIME,第6.8节)、3548或4648,网址为http://www.ietf.org/rfc.html,了解详情。

但等等,还有更多。许多间谍一看字符串就能说:“啊哈,这是Base64编码的东西!”该怎么办?  那么,试试另一个基数,比如Base85,它不是2的幂,因此不仅仅是重新组合位并将结果用作ASCII表的偏移量。Base85,后来一个非常相似的编解码器称为ASCII85,是一种不太为人所知的方案,它将编码后的字符串扩大,每4个字节仅增加5个字节,而不是Base64的每3个字节增加4个字节。为什么?因为它有一个更大的基数。需要记住的是,使用这两种方法中的一种或两种进行编码都会增加编码字符串的大小。

一个更古老的编解码器是Rot13或RotN,其中“N”是1到25之间的某个数字。在此方案中,任何字符都将被替换为字母表中靠后N个位置的字符。对于N = 13,'A'变为'N','B'变为'O',依此类推。可以对其他非字母ASCII字符进行替换技巧,但此工具的RotN仅替换大写和小写字母字符。这种较旧编码的好处是它不会增加编码字符串的原始大小。

此工具始终至少使用Base64编码和解码。可以添加ASCII85和/或RotN。将它们全部组合起来,无疑会为片段窃听者提供无法逾越的防御。(这就是我们说的“按原样”软件,不提供明示或暗示的保证,等等。)

关于这里使用的方法:剪贴板上的编码字符串实际上被维护为Unicode(System.String)。  也就是说,Windows本身会很方便地在您将5L6L44GILuODhuOCueODiA==粘贴到命令提示符时将其转换为ASCII。但当粘贴此内容的Base64解码版本例え.テスト时,Windows确实无能为力,因此解码后的内容可能需要粘贴到支持Unicode的容器或应用程序中,如MS Word或此HTML页面。但这也是数据最有可能被复制来的地方。

顺便说一句,您可以将例え.テスト粘贴到记事本中,它会正确显示,但当您保存此类.txt文件时,您将被迫选择“确定”将非ANSI字符保存为“?”,从而得到例え.テスト的??.???,或者选择另一种编码以Unicode格式保存。

最后一件事。反复单击“编码”是滥用此工具的绝佳方式。按相同的次数单击“解码”应该会恢复您开始的对象。只是我确信在某个时刻您会达到极限。我还没有尝试将剪贴板编码解码推向那个程度。

一些代码细节

单击“查看”按钮可以检查剪贴板内容而不修改它。这是单击“查看”的事件处理程序。请注意,它做了四件事。

private void buttonPeek_Click(object sender, RoutedEventArgs e)
{
    bool got = false;
    textBox1.Text = (flipper = !flipper) ? "" : " ";
	
    ShowCompatibleFormats();
	
    string s = ClipboardPeekImage(ref got);
    if (got)
    {
        textBox1.Text += s;
        return;
    }
    textBox1.Text += ClipboardPeekString(ref got);
}

首先,它将textBox1中的文本初始化为字符串为空或单个空格。这是交替进行的,因此重复单击“查看”会显示更新。这只是为了提供积极的反馈,表明处理程序正在被执行。

接下来调用ShowCompatibleFormats将兼容的数据格式加载到comboBox1(窗口底部的控件)中进行显示。

第三,字符串s被设置为通过调用ClipboardPeekImage来尝试查看剪贴板内容作为图像。  如果剪贴板包含图像数据,则将传引用布尔值got设置为true,并将textBox1.Text附加一个简短的图像描述。

最后,如果图像未“获取”,则调用ClipboardPeekString以检索剪贴板上文本的副本。 textBox1.Text将附加剪贴板上的文本或指示剪贴板上没有内容、没有文本对象或调用Clipboard.GetText(TextDataFormat.UnicodeText)时发生的异常的描述字符串。

buttonEncode_Click处理用户请求编码剪贴板上的数据。如果不存在图像,它也首先尝试处理剪贴板图像数据,然后处理文本数据。此处理程序还调用ClipboardPeekImage并测试got。这是此处理程序中的图像编码代码。

string s = ClipboardPeekImage(ref got);
if (got)
{
    var bs = Clipboard.GetImage();
    BitmapEncoder encoder;
    if (radioButton1.IsChecked == true)
    {
        encoder = new JpegBitmapEncoder();
        ((JpegBitmapEncoder)encoder).QualityLevel = 100;
    }
    else
        encoder = new PngBitmapEncoder();
    using (MemoryStream ms = new MemoryStream())
    {
        encoder.Frames.Add(BitmapFrame.Create(bs));
        encoder.Save(ms);
        s = Convert.ToBase64String(ms.ToArray());
        s = "!" + s; // leave non-Base64 marker for image

        if ((checkBox2.IsChecked == true) || (checkBox3.IsChecked == true))
            s = EncodeASCII85String(s);
    }
}
...
if (got)
    Clipboard.SetText(s);
textBox1.Text += Clipboard.GetText();  // empty string if no text

在这种情况下,图像被收集为BitmapSource,并编码为Jpeg图像或Png图像,然后转换为Base64字符串。需要注意的是,字符串前面加上了一个“!”字符,它不在Base64编码集中,用以标记它是一个Base64**图像**字符串,而不是Base64**文本**字符串。这一点在稍后解码剪贴板内容时很重要。

Jpg或Png的选择由UI上的RadioButton控制。Jpg是默认选项,因为它生成的字符串更小,但有时在维护内容方面不够忠实。在下面的两张图片中,

Jpg Giraffe Png Giraffe

右侧的PNG图像更接近复制到剪贴板的原始图像。

然后测试CheckBox控件,并通过ASCII85和/或RotN编码(s = EncodeASCII85String(s))来更新编码字符串,如果选择了任一编码。

一旦创建了最终编码,它将通过Clipboard.SetText(s)设置回剪贴板,并在textBox1中显示。

在解码端,剪贴板内容按编码步骤的相反顺序解码。第一步是在RotN CheckBox被选中时解码RotN编码。RotN解码是通过查找替换字符N个位置(或等效地,如果我们将字母表的末尾环绕到开头,则为26 - N个位置)来完成的(请参阅*MainWindow.xaml.cs*中的*Private Constants*)。这就是为什么EncodeDecodeRotNString可以同时处理编码和解码。

private string EncodeDecodeRotNString(string s, int N)
{
    StringBuilder sb = new StringBuilder(s);

    int at = 0, idx;
    foreach (char c in s)
    {
        if ((idx = UpperRot.IndexOf(c)) != -1)
            sb[at] = LowerRot[idx + N];
        else if ((idx = LowerRot.IndexOf(c)) != -1)
            sb[at] = UpperRot[idx + N];
        at++;
    }
    return sb.ToString();
}

对于编码,参数N是来自textBox3的值,即带有右侧ScrollBar的小TextBox。对于解码,N参数是26 - textBox3的值。请注意,我们在编码或解码时交换大小写,以进一步混淆。

关注点

我不会列出与此项目相关的参考资料,而是指出两个链接。我使用的最大帮助在于http://www.codinghorror.com/blog/2005/10/c-implementation-of-ascii85.html。  我感谢Jeff Atwood,他在2005年实现了这里使用的ASCII85版本,并做了一些修改。请参阅源文件Ascii85.cs

我之前在Tip 440706中提到过我心爱的Adobe Photoshop 7.0。我又一次被背叛了。但是,我找到了从Photoshop复制大图像时在外部剪贴板访问中显示不正确的原因。有一个注册表项需要克服一个大小限制,如http://forums.adobe.com/message/1637186中所述。在HKEY_CURRENT_USER\Software\Adobe\Photoshop\7.0(您的版本可能不同)中添加一个名为MaxClipSize的DWORD值0可以解决复制“全选”到剪贴板的大小限制问题。

UI更新

我真不敢相信我2年半前还是个新手。  该应用程序确实存在一些可用性问题。  希望上面的链接中的更新能够解决其中大部分问题。  例如,我添加了一个双击处理程序,以便可以轻松选中TextBox的全部内容以进行复制。  我还添加了滚动条,以处理(编码剪贴板图像)需要多行的那些情况。  我仍然不知道Base64编码如何会导致换行,但对于小图片来说,复制粘贴和编码/解码似乎都有效。  希望这能解决您可能遇到的任何问题。

历史

提交给CodeProject:2012年9月19日。

更新于:2015年4月。

© . All rights reserved.