ClipSpy +
介绍 我原本计划在此应用程序调用ClipSpy而是中途项目我想我最好谷歌它,看看是否有任何与这个名字,你瞧这里有一个很好的文章在迈克尔·邓恩题为ClipSpy写的代码项目。在回顾了Michael的演讲之后,我决定继续我的项目;他写于2001年,剪贴板API自那以后发生了变化。我打算用不同的方式来展示数据,并深入一点。这个实现是用c#编写的,我想了解剪贴板及其内部工作原理。因此,ClipSpy+是如何出现的,希望这篇文章将向您介绍剪贴板的所有荣耀! 在接下来的部分中,我们将探索剪贴板的内部工作原理,当我们有了基本的知识后,我将解释如何使用ClipSpy+应用程序。所以让我们把这个问题分解成可管理的小块: 101年更新剪贴板 剪贴板链接和活动通知的注册/取消注册基本API 剪贴板201 DataFormatsAdvanced API 使用ClipSpy+使用ClipInjector+引用注入数据——这是我在这个过程中获得的一些链接 和往常一样,我希望您能从阅读这篇文章中获得和我写这篇文章一样多的收获! 更新 有一些主要的问题需要解决,以使应用程序更加稳定,并获得我们在第一个版本中无法获得的数据。我需要一种方法来测试新版本,以确保我可以向剪贴板传递不同的格式,并让它接受任何类型的数据,因此我创建了ClipInjector+来注入不同的格式。它的效果是如此之好,以至于我决定它将是一个有用的补充文章,以及一个很好的教学工具。我添加到注入器中的自定义数据格式选项将向您展示使用自定义格式可以做什么,以及数据传递的方式。我在注入器中使用了带有格式控件状态的结构,并将RichTextBox中的文本作为剪贴板中的字符串。我可以像string[]或MemoryStream一样轻松地传递它,但是使用StringBuilder很容易创建一个字符串。 在主屏幕上增加的一个按钮是右上角的开始图像,这将打开注入器。在数据查看器中,我在左上角添加了一个按钮,如果遇到wave文件,可以播放它。 剪贴板101 剪贴板类在剪切、复制和粘贴操作以及拖放文件时使用全局内存存储和检索数据。它通过将与对象相关的数据存储在片段中以各种格式表示被操作的数据的不同方面来实现这一点。在下面的部分中,我们将进一步讨论不同的数据格式。 剪贴板链接和活动通知的注册/取消注册 Windows为任何对从剪贴板截取数据感兴趣的人提供了一个钩子,允许我们将自己添加到侦听器链或链表中。这里我们唯一需要做的是将传递给我们的数据转发给链中的下一个侦听器。不要打破链条,否则你将如履薄冰。即MSDN文献中不可预测的事情会发生。我还没有足够的勇气去尝试,明明知道! 要将自己注册为侦听器,我们将不得不诉诸于一些互操作。
[DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SetClipboardViewer(IntPtr hWnd); [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern bool ChangeClipboardChain( IntPtr hWndRemove, //handle to window to remove IntPtr hWndNewNext //handle to next window );
SetClipboardViewer调用用于注册为侦听器,使用方法如下:
private IntPtr ClipboardViewerNext; /// <spanclass="code-SummaryComment"><summary></span> /// Add this control to the Clipboard chain to receive notification events| /// <spanclass="code-SummaryComment"></summary></span> private void RegisterClipboardViewer() { ClipboardViewerNext = SetClipboardViewer(this.Handle); }
ClipboardViewNext是一个指向链中的下一个监听器的指针,当我们从链中删除我们自己时,我们必须保留它,如下所示:
/// <spanclass="code-SummaryComment"><summary></span> /// Remove this form from the Clipboard Viewer list /// <spanclass="code-SummaryComment"></summary></span> private void UnregisterClipboardViewer() { ChangeClipboardChain(this.Handle, ClipboardViewerNext); }
现在说到问题的核心!我们已经注册为听众了,现在怎么办?我们必须重写WndProc方法:
/// <spanclass="code-SummaryComment"><summary></span> /// Process window messages /// <spanclass="code-SummaryComment"><remarks></span> /// This code was not entirely written by me but has been modified from /// compiled from examples /// found while researching the subject! /// <spanclass="code-SummaryComment"></remarks></span> /// <spanclass="code-SummaryComment"></summary></span> /// <spanclass="code-SummaryComment"><paramname="m"></param></span> protected override void WndProc(ref Message m) { switch ((Msgs)m.Msg) { case Msgs.WM_DRAWCLIPBOARD: //0x308 //Retrieve the data object and Process it IDataObject data = Clipboard.GetDataObject(); ProcessClip(data); // // Each window that receives the WM_DRAWCLIPBOARD message // must call the SendMessage function to pass the message // on to the next window in the clipboard viewer chain. // SendMessage(ClipboardViewerNext, m.Msg, m.WParam, m.LParam); break; // // The WM_CHANGECBCHAIN message is sent to the first window // in the clipboard viewer chain when a window is being // removed from the chain. // case Msgs.WM_CHANGECBCHAIN: //0x30D // When a clipboard viewer window receives the WM_CHANGECBCHAIN message, // it should call the SendMessage function to pass the message to the // next window in the chain, unless the next window is the window // being removed. In this case, the clipboard viewer should save // the handle specified by the lParam parameter // as the next window in the chain. // // wParam is the Handle to the window being removed from // the clipboard viewer chain //Param is the Handle to the next window in the chain //following the window being removed. if (m.WParam == ClipboardViewerNext) { // // If wParam is the next clipboard viewer then it // is being removed so update pointer to the next // window in the clipboard chain // ClipboardViewerNext = m.LParam; } else { SendMessage(ClipboardViewerNext, m.Msg, m.WParam, m.LParam); } break; default: // // Just pass the message on. // base.WndProc(ref m); break; }
代码应该解释自己。 基本API Clipboard类定义了检查、获取和设置特定类型的数据的方法,但我发现这些方法在处理什么类型的数据方面是有限制的。如果你遵守规则,只使用它能理解的数据,你就没问题了。以下是可用的方法: 用于音频- ContainsAudio/GetAudioStream/SetAudio用于一般数据- ContainsData/GetData/SetData用于DropLists - ContainsFileDropList/GetFileDropList/SetFileDropList用于图像- ContainsImage/GetImage/SetImage用于文本- ContainsText/GetText/SetText 我不会详细介绍这些内容,因为它们在帮助文件和MSDN中有很好的文档说明。 如果您不关心处理任何外来的东西,这些方法就可以正常工作,但是我需要查看抛出给我的任何东西,并且我希望在较低的级别上显示数据,因此我使用了与IDataObject对象关联的方法。这些将在下一节中详细讨论。 剪贴板201 IDataObject接口提供了以下方法来处理包含在其中的数据: 使用的to确定给定格式的数据对象是否可用或可以转换为该格式GetFormats -返回该对象包含或可能转换为GetData的格式列表-获取数据对象SetData -设置数据对象 GetFormats方法返回与该对象关联的格式列表。其中大多数都是无趣的,似乎没有一个规范可用来确定它们的用途,但相关的列出如下: 文本-简单文本富文本格式-富文本HTML格式- HTML文件名-描述在哪里找到对象位图的完整路径和文件名-位图数据设备独立位图-通用位图数据 注意:当我继续我的研究,我会更新这个列表,如果我发现更多,但现在这些是相关的格式,我检查。 作为一个例子,如果我们复制一个RTF选择涉及以下格式: 系统。字符串,包含一个。net中的文本字符串格式Unicode文本- Unicode文本文本只是普通文本富文本格式——这是富文本隐藏文本标题格式,而不是用于我们需要的任何东西,例如我不知道语言环境,而不是用于任何我们需要的东西——我假设与文化? 如您所见,信息以多种格式提供,以适应可能使用该信息的应用程序。 当将数据呈现为原始形式时,我发现通过查看GetFormats方法返回的格式列表,数据以下列方式之一存储: 系统。字符串的系统。String [] MemoryStream系统。位图(只是一个流) 下面给出的代码是我用于从DataObject提取原始数据的代码。 注意:我已经尝试从几个应用程序中复制了很多不同类型的数据,到目前为止我只找到了这些。如果我发现更多的格式,我将更新。
public void ProcessRawData(IDataObject data, bool IsUnknowClip) { string[] strs = new string[20]; dataFormats = data.GetFormats(true); try { int index = 0; MemoryStream ms = null; foreach (string s in dataFormats) { switch (data.GetData(s, true).GetType().ToString()) { case "System.String[]": strs = (string[])data.GetData(s, true); rawDataBuffers[index++] = strs[0]; break; case "System.IO.MemoryStream": ms = (MemoryStream)data.GetData(s, true); rawByteBuffers[index++] = ms.ToArray(); break; case "System.String": rawDataBuffers[index++] = (string)data.GetData(s, true); break; } } } //The catchall, there was an error processing the data catch { if (IsUnknowClip)| title = "ERROR - Processing data"; } }
了解这一点的最佳方法是使用ClipSpy+并查看您所学到的内容。这让我们真正使用了dang,所以让我们现在就做! 注入数据 注入数据与检索数据相反。如果我们知道如何检索数据,并且希望您已经读到这里,我们可以可视化如何将数据传递到剪贴板。我们可以看到有四种选项可用来构造数据:string、string[]、MemoryStream或位图。 注意:您可能传递的任何数据都必须是可序列化的。下面的代码是在传递自定义格式数据时使用的类。
[Serializable] public class CustomFormatStruct : Object { public bool textActive = true; public bool rtfActive = true; public bool audioActive = true; public bool imageActive = true; public string data = string.Empty; public string name = string.Empty; public CustomFormatStruct(bool ta, bool ra, bool aa, bool ia, string d, string n) { textActive = ta; rtfActive = ra; audioActive = aa; imageActive = ia; data = d; name = n; } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("This is an example of the kinds of data that can be passed using the Clipboard\n\n"); sb.Append("<CustomFormatStruct>\n"); sb.Append("\t Text Data Sent: " + textActive.ToString() + "\n"); sb.Append("\t Rtf data sent: " + rtfActive.ToString() + "\n"); sb.Append("\tAudio data sent: " + audioActive.ToString() + "\n"); sb.Append("\tImage data sent: " + imageActive.ToString() + "\n"); sb.Append("</CustomFormatStruct>\n\n"); sb.Append("<" + name + ">\n"); sb.Append("\t" + data + "\n"); sb.Append("</" + name + ">"); return sb.ToString(); }
如果您查看下面的代码,您将看到不同的数据是如何根据数据类型以不同的数据格式传递的,也就是说,如果我们传递文本,我们使用文本格式等等。一开始看起来有点吓人,但一旦你开始使用它,它确实是一个很容易掌握的概念。
/// <spanclass="code-SummaryComment"><summary></span> /// Add data to the Clipboard /// <spanclass="code-SummaryComment"></summary></span> /// <spanclass="code-SummaryComment"><paramname="sender"></param></span> /// <spanclass="code-SummaryComment"><paramname="e"></param></span> private void button1_Click(object sender, EventArgs e) { if (richTextBox1.Text == string.Empty) richTextBox1.Text = "Tried to fool me didn't ya? Enter some text and try again!"; DataObject data = new DataObject(); //If we are going to send TExt set it here if (optionTextActive) data.SetData(DataFormats.Text, richTextBox1.Text); //Struct for our custom data CustomFormatStruct cfs = new CustomFormatStruct( optionTextActive, optionRtfActive, optionAudioActive, optionImageActive, richTextBox1.Text, textBox1.Text); if (optionCustomActive) { data.SetData("ClipInjectorPlus", "This format was generated byt ClipInjector+"); data.SetData(textBox1.Text, cfs.ToString()); } //Rtf data here if (optionRtfActive) data.SetData(DataFormats.Rtf, richTextBox1.Rtf); //Image data here if (optionImageActive) data.SetData(DataFormats.Bitmap, image); //Audio data here if (optionAudioActive) data.SetData(DataFormats.WaveAudio, audioData); //Do the deed! Clipboard.Clear(); Clipboard.SetDataObject(data); }
使用ClipSpy + 你可能知道,也可能不知道,我一直在自学GDI+和一般的图形,所以UI将说明一些我一直在学习的方法。我本可以做得更多,但这样下一篇文章就不会有任何惊喜了。 因此,在拍了拍自己的背,休息了一下,重新调整我的手臂之后,我现在准备向您展示如何使用这个神奇的工具。 图1和图2分别显示了完全/展开模式下的ClipSpy+和迷你或折叠布局。 图1所示。剪贴间谍+布局-全模式 A切换声音开/关B启用/禁用ClipSpy+从拦截数据C过滤器条,切换类别(不活动时变暗) 注意:如果你点击工具条左边的绿色过滤器按钮,它会把所有类别都变成活动的。 D当前剪辑对象-简单描述和图标指示类型E显示视图切换(正常/原始数据)F历史树。剪辑被插入到适当类别的开头。G表单控件(从左到右:帮助,最小化到托盘,最小化到迷你模式,退出)H视图窗格(普通或原始) 在原始模式下,组合框包含各种格式。选择一种格式会在查看器中显示相应的数据。 图2。ClipSpy +迷你模式 默认情况下,应用程序启动时本身已经注册,并且声音是打开的。 使用ClipInjector + 使用注入器是相当直接的。可用的格式在上面的窗格中,并确定是否将设置数据,以及将为各种可用的数据格式设置哪些数据。设置好要使用的选项后,单击格式区域左下方的箭头按钮,这将把剪辑设置为剪贴板!一旦数据被发送,它将被ClipSpy+应用程序拾取,在那里可以查看数据。 好了,这次就到这里吧,快乐的编码! 参考文献 CodeProject: ClipSpy—Michael Dunn撰写的一篇优秀文章MSDN: Clipboard—包含到其他MSDN文章的链接 修订历史 版本1.0发布于12/29.07 SaturdayKnown问题: 一段时间后,声音会消失,不再提醒用户活动。这似乎不是这个应用程序所独有的!我偶尔会遇到无法识别的数据类型。当我找到它们时,我会将它们添加到这个应用程序并进行更新,但不应该有更多。在raw视图中,如果在viewer窗格下面的size字段中有消息读取“Error: 0 bytes”,这意味着我不能处理那种格式的数据! 本文转载于:http://www.diyabc.com/frontweb/news164.html