原文地址:http://www.developer.com/net/csharp/article.php/3359891,翻译的不好的地方望指出。
概述
当剪贴板上的数据被修改时,Windows 保持一张要求被通知的窗口链表。每次剪贴板上的数据被修改,在链表中的第一个窗口接到 WM_DRAWCLIPBOARD 消息。这个窗口然后可以查询剪贴板关于它包含的数据类型(如 RTF,ASCII 文本等)与实际的数据。因为没有加入一个窗口到通知链表的托管(.NET)API,你必须使用 SetClipboardViewer Win32 函数。虽然这是一个简单的过程,当使用这个函数时你应该知道一些一般性指导原则 :
- 当调用 SetClipboardViewer 函数时,你需要通过这个窗口的句柄接收 WM_DRAWCLIPBOARD 消息。
该 SetClipboardViewer 函数返回在链表中的目前第一个窗口。你的应用程序应该存储这个值——典型的一个类的成员——因为每个窗口得到 WM_DRAWCLIPBOARD 消息必须发送相同的消息给在链表中的下个窗口(通过 SendMessage 函数)。 - 处理 WM_DRAWCLIPBOARD 消息。可以提供一个 Form 类超载 WndProc 类成员函数做到。不久你会看到一个例子。
- 处理 WM_CHANGECBCHAIN 消息。因为每个窗口处理 WM_DRAWCLIPBOARD 消息时负责发送这一消息给在链表中的下个窗口,还必须知道什么时候该链表变化。当一个窗口从链表中删除自己时剪贴板发送 WM_CHANGECBCHAIN 消息。
- 从链表中删除窗口。这个任务通过 ChangeClipboardChain Win32 函数完成,剪贴板监视不再需要时它随时可以完成。
一步一步的教导
- 概述里提到的,在你的程序里你需要调用几个 Win32函数——SetClipboardViewer, ChangeClipboardChain和SendMessage。为了在 .NET 程序里做到这点,你首先要用 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);
... - 在剪贴板通知链表中定义一个类成员去保持目前第一个窗口:
public class Form1 : System.Windows.Forms.Form
{
...
IntPtr nextClipboardViewer; - 调用 SetClipboardViewer 函数。在演示中,我在form 构造函数中调用这个函数:
public Form1()
{
InitializeComponent();
nextClipboardViewer = (IntPtr)SetClipboardViewer(( int )
this .Handle);
..
- 在 Form 类里,超载 WndProc 类成员函数。正如你可以看到这里,我只处理2个消息:
WM_DRAWCLIPBOARD 和 WM_CHANGECBCHAIN。注意,我为了这些消息定义了2个常量
(2个值可以在Platform SDK's winuser.h 文件里找到。)
在 WM_DRAWCLIPBOARD 消息处理代码里,我查看从剪贴板链表中删除的窗口(通过
Message.WParam 成员)是否是链表中的下个窗口。假如是的话,然后我设置 form 的下个
窗口成员变量(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 ;
}
} - 最后,当在 .NET 运行环境中调用窗口类 Dispose 类成员函数时我从剪贴板链表中删除窗口:
protected override void Dispose( bool disposing )
{
ChangeClipboardChain( this .Handle, nextClipboardViewer);
...
A Chain Is Only As Strong...
经过这些步骤,剪贴板中文本的任何改变你的应用程序都将被告知。相似的在 Windows 下开发的很多任务,
一旦你知道调用正确的 APIs 就不是很困难了。关键的问题是用剪贴板工作是要确保你遵循一些简单的规则
使剪贴板链表中其他的应用程序继续正确地执行。
下载代码
下载陪同源代码,click here