C#技巧:用C#监视剪贴板活动
C#技巧:用C#监视剪贴板活动
原作者:Tom Archer
翻译:DotNetEarthWorm
Welcome to this week's installment of .NET Tips & Techniques! Each week, award-winning Architect and Lead Programmer Tom Archer demonstrates how to perform a practical .NET programming task using either C# or Managed C++ Extensions.
欢迎来到本周的.NET技巧和技术版块!每一周,获奖的程序员Tom Archer都会向我们演示如何用C#或扩展C++来实现 .NET的编程任务。
My MFC book Visual C++ .NET Bible includes a chapter on working with the Windows Clipboard from Visual C++. Quite a number of readers have asked about how to perform similar tasks in C#. One of those tasks—which the "Clipboard ring" in Microsoft Office has made popular—is specifying that your application is notified if the Clipboard changes. This week's installment of .NET Tips & Techniques provides both an overview and a step-by-step example for accomplishing this with .NET and C#. That way, if you're already familiar with how the Clipboard works (or you're in a hurry) and just want to jump directly to the code, you can.
我的关于MFC的书《Visual C++ .NET 圣经》包括了关于如何用VC++与Windows剪贴板一起工作的一章。相当多的读者问如何用C#来实现相同的任务。其中的一个任务是指定如果剪贴板内容发生变化时,你的应用程序被通知。(剪贴板环在Microsoft Office中相当受欢迎)本周的.NET 技术与技巧模块既提供了一个总览同时也提供了一个用C#和.NET完成的一步一步的例子。如果你已经熟悉了剪贴板如何工作(或者你很着急)并且你想直接跳到代码,你可以:
Overview
Windows maintains a list, or chain, of windows that have requested to be notified when data on the Clipboard has been modified. Each time the Clipboard data is modified, the first window in this chain receives a message—WM_DRAWCLIPBOARD. The window then can query the Clipboard as to the type of data it contains (such as RTF, ASCII text, and so on) and the actual data.
概要
Windows维护了一个窗口的List或者Chain,在剪贴板上的数据被修改时,它用来请求被告知。每一次剪贴板的数据被修改,Chain中第一个window都会接到一个WM_DRAWCLIPBOARD消息。然后该window查询剪贴板包含的数据类型(比如RTF,ASCII文本等等)和实际的数据。
Because there is no managed (.NET) API for adding a window to this notification chain, you have to use the Win32 SetClipboardViewer function. While this is a fairly simple process, you should be aware of some general guidelines when using this function:
因为没有托管的API(.NET环境)用来把一个window添加到通知链,你不得不利用Win32 API中的 SetClipboardViewer 函数。然而这是一个相当简单的过程,你应该意识到当使用这个函数的基本方针:
- When calling the SetClipboardViewer function, you need to pass the handle of the window that will receive the WM_DRAWCLIPBOARD message. The SetClipboardViewer function returns the current first window in the chain. Your application should store this value—typically in a class member—because each window that receives the WM_DRAWCLIPBOARD message has to send that same message to the next window in the chain (via the SendMessage function).
- 当调用SetClipboardViewer 函数时,你需要传递一个窗口的句柄,这个窗口将会收到这个WM_DRAWCLIPBOARD消息。SetClipboardViewer 函数返回当前在链中第一个窗口。你的应用程序应该存储这个值(典型的是用类成员的形式),因为每个接受到WM_DRAWCLIPBOARD消息的窗口都必须发送相同的消息到链中下一个窗口(通过 SendMessage 函数)。
- Handle the WM_DRAWCLIPBOARD message. This can be done by providing a Form class overload of the WndProc method. You'll see an example of doing this shortly.
- 处理WM_DRAWCLIPBOARD 消息。这个通过 Form 类重写WndProc方法来实现。稍后,你将会看到一个这样的例子。
- Handle WM_CHANGECBCHAIN message. Because each window that handles the WM_DRAWCLIPBOARD message is responsible for sending that message to the next window in the chain, it must also know when the chain changes. The Clipboard sends the WM_CHANGECBCHAIN message when a window has removed itself from the chain.
- 处理 WM_CHANGECBCHAIN 消息。因为每个处理WM_DRAWCLIPBOARD
消息的窗口都负责将该消息发送到链中的下一个窗口,它也必须知道什么时候链会改变。 - Remove the window from the chain when finished. This task is accomplished via the Win32 ChangeClipboardChain function, and it can be done any time that Clipboard monitoring is no longer needed.
- 当完成的时候,从链中移除这个窗口。这个通过Win32 API的ChangeClipboardChain函数来实现,并且它能在剪贴板监视不再需要的时候这样做。
Step-by-step Instructions
一步一步的指令
- As mentioned in the Overview, you'll need to call several Win32 functions—SetClipboardViewer, ChangeClipboardChain, and SendMessage—in your application. In order to do that from a .NET application, you first have to import those functions by using the DllImport attribute (which resides in the System.Runtime.InteropServices namespace). The following example imports these functions within the demo application's Form class:
- 正如概要中提到的,你需要在你的应用程序中调用几个Win32的函数(SetClipboardViewer, ChangeClipboardChain,和 SendMessage)。为了能从一个.NET应用程序中调用Win32函数,你首先必须通过使用DllImport属性来导入这些函数(它位于System.Runtime.InteropServices命名空间中)。下面的例子在示例应用程序的Form类中导入了这些函数:
using System.Runtime.InteropServices;
...
public class Form1 : System.Windows.Forms.Form
{
[DllImport("User32.dll")]
protected static extern int
SetClipboardViewer(int hWndNewViewer);
[DllImport("User32.dll", CharSet=CharSet.Auto)]
public static extern bool
ChangeClipboardChain(IntPtr hWndRemove,
IntPtr hWndNewNext);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hwnd, int wMsg,
IntPtr wParam,
IntPtr lParam);
...
- Define a class member to hold the current first window in the Clipboard notification chain:
- 定义一个类成员,用来保存当前在剪贴板通知链中第一个窗口:
public class Form1 : System.Windows.Forms.Form
{
...
IntPtr nextClipboardViewer;
- Call the SetClipboardViewer function. In the demo, I call this function in the form's constructor:
- 调用SetClipboardViewer函数。在这个示例中,我在Form的构造方法中调用。
public Form1()
{
InitializeComponent();
nextClipboardViewer = (IntPtr)SetClipboardViewer((int)
this.Handle);
..
- Within the Form class, override the WndProc method. As you can see here, I handle only two messages: WM_DRAWCLIPBOARD and WM_CHANGECBCHAIN. Note that I define two constants for these messages (where both values can be found in the Platform SDK's winuser.h file.)
在Form类中,重写WndProc方法。正如你看到的,我仅仅处理了两个消息:WM_DRAWCLIPBOARD 和 WM_CHANGECBCHAIN。注意:我为这两个消息定义了两个常量(这两个值都可以在Platforme SDK的winuser.h文件中找到)
In the WM_DRAWCLIPBOARD message-handling code, I call a helper function to display the text that is currently on the Clipboard and pass the same message on to the next window in the chain. (You can see the code to display RTF and ASCII text in the article's demo code at the end of this column.)
在WM_DRAWCLIPBOARD消息处理代码中,我调用了一个帮助类方法来显示当前在剪贴板上的文本并将同样的消息传递给链中的下一个窗口。(你会在文章示例代码的最后一列看到显示RTF和 ASCII文本的代码)
In the WM_CHANGECBCHAIN message-handling code, I check to see whether the window being removed from the Clipboard chain (passed in the Message.WParam member) is the next window in the chain. If it is, I then set the form's next window member variable (nextClipboardViewer) to the next window in the chain (passed in the Message.LParam member):
在WM_CHANGECBCHAIN消息的处理代码中,我查看这个正在从剪贴板链中移除(在传过来的Message.Wparam成员中)的窗口是否是链中的下一个窗口。如果是的话,我将设置nextClipboardViewer变量为链中的下一个窗口(在传过来的Message.Lparam成员中)
protected override void
WndProc(ref System.Windows.Forms.Message m)
{
// defined in winuser.h
const int WM_DRAWCLIPBOARD = 0x308;
const int WM_CHANGECBCHAIN = 0x030D;
switch(m.Msg)
{
case WM_DRAWCLIPBOARD:
DisplayClipboardData();
SendMessage(nextClipboardViewer, m.Msg, m.WParam,
m.LParam);
break;
case WM_CHANGECBCHAIN:
if (m.WParam == nextClipboardViewer)
nextClipboardViewer = m.LParam;
else
SendMessage(nextClipboardViewer, m.Msg, m.WParam,
m.LParam);
break;
default:
base.WndProc(ref m);
break;
}
}
- Finally, I remove the window from the Clipboard chain when the window class's Dispose method is called by the .NET runtime:
- 最后,在窗口类的Dispose方法被.NET运行时调用时,将窗口从剪贴板链中删除。
protected override void Dispose( bool disposing )
{
ChangeClipboardChain(this.Handle, nextClipboardViewer);
...
A Chain Is Only As Strong...
After following these few steps, your application will be notified of any changes to the text on the Clipboard. Like a lot of tasks in Windows development, it's not very difficult once you know the right APIs to call. The key issue with working with the Clipboard is to make sure that you follow a few simple rules so that other applications in the Clipboard chain continue to perform correctly.
在执行了这些步骤之后,你的应用程序将会在剪贴板的文本有任何变化的时候收到通知。像大多数Windows开发的任务一样,一旦你知道了正确的API调用,就变得一点儿也不难。处理剪贴板的关键问题在于确保你遵循一些简单的规则以便其他的在剪贴板链中的应用程序能够正确运行。
Download the Code
To download the accompanying source code for this tip, click here.
About the Author
关于作者
The founder of the Archer Consulting Group (ACG), Tom Archer has been the project lead on three award-winning applications and is a best-selling author of 10 programming books as well as countless magazine and online articles.
Archer Consulting Group(ACG)公司的创始人,Tom Archer是3个获奖应用程序的项目领导,并且还是10本编程书籍的畅销书作者,还有数不清的杂志和在线文章。