摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P462

如果某个程序能获知剪贴板内容的改变,这个程序就被称为“剪贴板查看器”。Windows 带有一个剪贴板查看器,但是你也可以编写自己的剪贴板查看器程序。剪贴板查看器通过发送给查看器窗口过程的消息得知剪贴板内容的更改。

12.3.1  剪贴板查看器链

在 Windows 下可以同时运行任意数目的剪贴板查看器应用程序,并且剪贴板的改变都能通知到它们。但是从 Windows 的角度来看,只有一个剪贴板查看器,我称之为“当前剪贴板查看器”。Windows 只维护一个标识当前剪贴板查看器的窗口句柄,并且只给那个窗口发送消息,通知它剪贴板内容改变了。

剪贴板查看器应用程序应该加入“剪贴板查看器链”,这样所有运行的剪贴板查看器程序就都能收到 Windows 发给当前剪贴板查看器的消息。当某程序把自己注册为剪贴板查看器时,这个程序就成为当前剪贴板查看器。Windows 把上一个当前剪贴板查看器的窗口句柄交给这个程序,然后这个程序保存此句柄。当它收到剪贴板查看器消息时,它会把消息发送给剪贴板查看器链中下一个程序的窗口过程。

12.3.2  剪贴板查看器函数和消息

通过调用 SetClipboardViewer 函数,程序就能成为剪贴板查看器链的一部分。如果此程序的主要目的就是作为一个剪贴板查看器,那么它可以在处理 WM_CREATE 消息时调用此函数。这个函数返回上一个当前剪贴板查看器的窗口句柄。程序应该用静态变量来保存这个句柄:

static HWND hwndNextViewer;
[其他程序行]
case WM_CREATE:[其他程序行]hwndNextViewer = SetClipboardViewer (hwnd);

如果你的程序在 Windows 会话期间是头一个成为剪贴板查看器的程序,那么 hwndNextViewer 将为 NULL。

一旦剪贴板内容改变了,Windows 就会向当前剪贴板查看器(即最新注册成为为剪贴板查看器的窗口)发送 WM_DRAWCLIPBOARD 消息。剪贴板查看器链的最后一个程序(即第一个注册成为剪贴板查看器的窗口)将会有一个值为 NULL 的 hwndNextViewer。如果 hwndNextViewer 值为 NULL,程序就直接返回,不需要把消息发送给另一个程序。(不要把 WM_DRAWCLIPBOARD 和 WM_PAINTCLIPBOARD 消息混为一谈。WM_PAINTCLIPBOARD 是由剪贴板查看器发给使用 CF_OWNERDISPLAY 剪贴板格式的程序的。WM_DRAWCLIPBOARD 则是由 Windows 发给当前剪贴板查看器的。)

处理 WM_DRAWCLIPBOARD 消息的最简单的方法是把消息发给下一个剪贴板查看器(除非 hwndNextViewer 为 NULL),并且使窗口的客户区无效:

case WM_DRAWCLIPBOARD:if (hwndNextViewer)SendMessage (hwndNextViewer, message, wParam, lParam);InvalidateRect (hwnd, NULL, TRUE);return 0;

处理 WM_PAINT 消息期间,你可以通过普通的 OpenClipboard、GetClipboardData 和 CloseClipboard 调用读取剪贴板内容。

当某程序想从剪贴板查看器链中退出时,必须调用 ChangeClipboardChain。这个函数需要退出的程序的窗口句柄和它下一个剪贴板查看器的窗口句柄:

ChangeClipboardChain(hwnd, hwndNextViewer);

当程序调用 ChangeClipboardChain 时,Windows 会向当前剪贴板查看器发送一个 WM_CHANGECBCHAIN 消息。该消息的 wParam 参数是要从链里退出的窗口的句柄(即 ChangeClipboardChain 的第一个参数),lParam 是将要退出的窗口的下一个剪贴板查看器的窗口句柄(即 ChangeClipboardChain 的第二个参数)。

当你的程序收到了 WM_CHANGECBCHAIN 消息,则必须检查 wParam 是否等于你保存的 hwndNextViewer 值。如果相等,你的程序必须把 hwndNextViewer 设为 lParam。这个操作确保将来你得到的任何 WM_CHANGECBCHAIN 消息不会被发送给从链里退出的那个窗口。如果 wParam 不等于 hwndNextViewer,并且 hwndNextViewer 不为 NULL,就把消息发送给下一个剪贴板查看器:

case WM_CHANGECBCHAIN:if ((HWND) wParam == hwndNextViewer)hwndNextViewer = (HWND)lParam);else if (hwndNextViewer)SendMessage (hwndNextViewer, message, wParam, lParam);return 0;

并不一定要加上 else if 语句,它只是检查 hwndNextViewer 是否为非 NULL。hwndNextViewer 值为 NULL 意味着执行这段代码的程序是链里的最后一个剪贴板查看器,在这种情况下,消息不应该再传递。

如果你的程序在要终止时仍然在剪贴板查看器链里,则必须把它从链里删除。可以在处理 WM_DESTROY 消息时调用 ChangeClipboardChain 来完成删除:

case WM_DESTROY:ChangeClipboardChain (hwnd, hwndNextViewer);PostQuitMessage (0);return 0;

Windows 也有一个让程序获得第一个剪贴板查看器的窗口句柄的函数:

hwndViewer = GetClipboardViewer();

这个函数通常没有什么用。如果当前剪贴板查看器不存在,则返回值为 NULL。

这里有一个例子说明剪贴板查看器链是怎样工作的。当 Windows 刚开始启动时,当前剪贴板查看器为 NULL:

当前剪贴板查看器:                                                      NULL

拥有窗口句柄 hwnd1 的程序调用了 SetClipboardViewer。该函数会返回 NULL,这个值成为此程序里的 hwndNextViewer 值:

当前剪贴板查看器:                                                      hwnd1

hwnd1 的下一个查看器:                                             NULL

拥有窗口句柄 hwnd2 的第二个程序现在调用 SetClipboardViewer,并且取得 hwnd1:

        当前剪贴板查看器:                                                      hwnd2

hwnd2 的下一个查看器:                                            hwnd1

hwnd1 的下一个查看器:                                            NULL

第三个程序(hwnd3)和第四个程序(hwnd4)也调用了 SetClipboardViewer,并且取得 hwnd2 和 hwnd3:

当前剪贴板查看器:                                                      hwnd4

hwnd4 的下一个查看器:                                            hwnd3

hwnd3 的下一个查看器:                                            hwnd2

hwnd2 的下一个查看器:                                            hwnd1

hwnd1 的下一个查看器:                                            NULL

当剪贴板内容发生变化时,Windows 向 hwnd4 发送 WM_DRAWCLIPBOARD 消息,hwnd4 把消息发给 hwnd3,hwnd3 再发给 hwnd2,hwnd2 发给 hwnd1,hwnd1 返回。

现在 hwnd2 决定从链中退出,它调用以下函数:

ChangeClipboardChain (hwnd2, hwnd1);

Windows 会向 hwnd4 发送 WM_CHANGECBCHAIN 消息,相应的 wParam 等于 hwnd2,lParam 等于 hwnd1。由于 hwnd4 的下一个查看器时 hwnd3,因此 hwnd4 把这个消息发给 hwnd3。现在 hwnd3 注意到 wParam 等于它的下一个查看器(hwnd2),所以它把它的下一个查看器设置成等于 lParam(hwnd1)并且返回。这个使命就完成了。剪贴板查看器现在看起来如下:

当前剪贴板查看器:                                                      hwnd4

hwnd4 的下一个查看器:                                            hwnd3

hwnd3 的下一个查看器:                                            hwnd1

hwnd1 的下一个查看器:                                            NULL

12.3.3  一个简单的剪贴板查看器

剪贴板查看器不一定要和 Windows 提供的查看器一样复杂。比如,剪贴板查看器可以只显示一种剪贴板格式。图 12-2 所示的 CLIPVIEW 程序就是一个只显示 CF_TEXT 格式的剪贴板查看器。

/*---------------------------------------------CLIPVIEW.C -- Simple Clipboard Viewer(c) Charles Petzold, 1998
----------------------------------------------*/#include <windows.h>LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{static TCHAR szAppName[] = TEXT(".ClipView.");HWND       hwnd;MSG            msg;WNDCLASS    wndclass;wndclass.style = CS_HREDRAW | CS_VREDRAW;wndclass.lpfnWndProc = WndProc;wndclass.cbClsExtra = 0;wndclass.cbWndExtra = 0;wndclass.hInstance = hInstance;wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName = NULL;wndclass.lpszClassName = szAppName;if (!RegisterClass(&wndclass)){MessageBox(NULL, TEXT("This program requires Windows NT!"),szAppName, MB_ICONERROR);return 0;}hwnd = CreateWindow(szAppName, TEXT("Simple Clipboard Viewer (Text Only)"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL);ShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;
}LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static HWND  hwndNextViewer;HGLOBAL         hGlobal;HDC             hdc;PTSTR       pGlobal;PAINTSTRUCT  ps;RECT        rect;switch (message){case WM_CREATE:hwndNextViewer = SetClipboardViewer(hwnd);return 0;case WM_CHANGECBCHAIN:if ((HWND)wParam == hwndNextViewer)hwndNextViewer = (HWND)lParam;else if (hwndNextViewer)SendMessage(hwndNextViewer, message, wParam, lParam);return 0;case WM_DRAWCLIPBOARD:if (hwndNextViewer)SendMessage(hwndNextViewer, message, wParam, lParam);InvalidateRect(hwnd, NULL, TRUE);return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);GetClientRect(hwnd, &rect);OpenClipboard(hwnd);#ifdef UNICODEhGlobal = GetClilpboardData(CF_UNICODETEXT);
#elsehGlobal = GetClipboardData(CF_TEXT);
#endifif (hGlobal != NULL){pGlobal = (PTSTR)GlobalLock(hGlobal);DrawText(hdc, pGlobal, -1, &rect, DT_EXPANDTABS);GlobalUnlock(hGlobal);}CloseClipboard();EndPaint(hwnd, &ps);return 0;case WM_DESTROY:ChangeClipboardChain(hwnd, hwndNextViewer);PostQuitMessage(0);return 0;}return DefWindowProc(hwnd, message, wParam, lParam);
}

按照前面讨论的,CLIPVIEW 处理 WM_CREATE、WM_CHANGECBCHAIN、WM_DRAWCLIPBOARD 和 WM_DESTROY 消息。WM_PAINT 消息打开剪贴板并用 CF_TEXT 格式调用 GetClipboardData。如果该函数返回一个全局内存句柄,CLIPVIEW 就锁定它并用 DrawText 在客户区显示文本。

能处理标准格式之外的其他格式的剪贴板查看器(比如 Windows 提供的剪贴板查看器)还需要作一些额外的工作,比如显示当前剪贴板里所有格式的名称。你可以调用 EnumClipboardFormats 得到格式名,还可以调用 GetClipboardFormatName 来取得非标准格式的名称。使用 CF_OWNERDISPLAY 格式的剪贴板查看器必须向剪贴板所有者发送以下四个消息才能显示数据:

WM_PAINTCLIPBOARD                                             WM_VSCROLLCLIPBOARD

WM_SIZECLIPBOARD                                                WM_HSCROLLCLIPBOARD

如果想实现这种剪贴板查看器,你得通过 GetClipboardOwner 取得剪贴板所有者的窗口句柄。而且,在需要更新剪贴板查看器的客户区时,还需要向剪贴板所有者的窗口发送上述这些信息。

12.3 实现一个剪贴板查看器相关推荐

  1. 剪贴板查看器:CopyClip 2 for Mac

    CopyClip 2 for Mac是一款运行在macOS平台上的剪贴板查看器,CopyClip 2 for Mac版可以帮助用户将存储您以前复制或剪切的所有内容,还能找到你所需要的内容,如果您正好也 ...

  2. C#制作一个图片查看器,具有滚轮放大缩小,鼠标拖动,图像像素化,显示颜色RGB信息功能

    目录 前言 一.界面设计 二.关键技术 1.把图片拖入到窗体并显示 2.实现图像缩放的功能 3.实现图像的移动效果 4.实时显示当前鼠标处的RGB值 5. 右击功能的实现 6.效果展示 总结 前言 使 ...

  3. python画生日_使用PYTHON制作一个生日查看器

    python中的字典说白了就是一个键值对,birthdays = {'张三': '12月1日', '李四并': '11月23日', '普通': '6月2日'} 通过键取得值的内容,例如birthday ...

  4. Android 日志自动分析,Android Log Viewer:一个日志查看器工具,可简化实时对Android日志的分析...

    作为与Cordova一起工作的移动应用程序开发人员, 我知道调试应用程序的本机部分会很困难, 例如, 当你为应用程序创建本机插件时(在这种情况下, Android Studio无效).在试图找出我的应 ...

  5. Google浏览器开发者工具:CSSViewer(一个Css查看器)

    CSSViewer的简介 CSSViewer是一款可以帮助用户快速查看当前的网页元素的CSS属性的谷歌浏览器插件,在Chrome中安装了CSSViewer插件以后,用户就可以在设计网页的时候,快速地模 ...

  6. java swing awt绘制一个图片查看器 图片显示 图片控件

    感谢 java图片查看器 的代码 java似乎没有一个名字叫图片控件的 控件,使用swing 的Label显示图片 他的代码如下: package swing.draw; import java.aw ...

  7. 习题:编写一个二进制位查看器,要求程序运行时从命令行输入一个整数,将其在内存中的二进制位的形式输出出来。

    个人答案: (1)java import java.util.Scanner;public class 二进制 {public static void main(String[] args) {//输 ...

  8. 推荐一个AutoCad查看器——Free DWG Viewer试用

    今天要看一个AutoCAD的文件,于是Google了一下AutoCad的Viewer. 于是找了几个软件, Free Autocad Viewer Acme CADsee 感觉这两个软件都非常不错,但 ...

  9. 制作一个简单的照片查看器(支持缩放手势)

    本文主要实现的功能是制作一个照片查看器,点击照片能进入大图模式,支持左右滑动,支持手势缩放, 本文只演示1张图和3张图的效果,其他的效果分为为: 2张图的时候按照1行2列展示, 3张图的时候按照1行3 ...

  10. 剪切板 html 查看器,剪贴板历史查看器

    剪贴板查看器是一款可以帮助您查看剪贴板历史信息的工具.大家都知道电脑自带的剪贴板只会记录最后一次复制的内容吧,而这款软件就不一样了,它可以在启动后为您记录所有的复制内容,以便您查看或者再次使用. 剪贴 ...

最新文章

  1. 前一千页CVE 对应影响产品信息 JSON文件格式转成HTML
  2. java读取配置文件的几种方法
  3. 数据处理——One-Hot Encoding
  4. 周四话分析:数据驱动,如何塑造下一个“教育领头羊”?
  5. Ubuntu系统安装(win7双系统)
  6. ruby watir 登陆邮箱
  7. python编程序列类型_python序列类型种类详解
  8. java注解教程 pdf_Java注解详解
  9. mysql 5.6 ddl 锁表_MySQL数据库之MySQL5.6 Online DDL 是否锁表、rebuild表、inplace的说明...
  10. eclipse查找指定行数
  11. Win11连接WiFi后显示无Internet访问权限怎么办?
  12. iphone 相机拍摄比例_在iPhone上拍摄:Apple如何解决Deepfakes和其他媒体操纵问题
  13. JAVA基础(3/17)-基本语法_运算符
  14. 景深与光圈、拍摄距离和镜头焦距的关系
  15. 【转帖】刘备三顾茅庐,请Elasticsearch出山
  16. panda3d python教程_图解 Panda3D引擎开发入门
  17. Oracle 10g 版本10.2.0.1.0升级到Oracle 10g 10.2.0.3.0过程
  18. Linux下Mysql数据库
  19. WEB攻防-JavaWeb项目
  20. R语言ggplot2可视化:使用patchwork包将两个ggplot2可视化结果图像垂直堆叠排列进行组合构图(vertically stack the plots)

热门文章

  1. Ubuntu常见错误合集——持续更新
  2. 安装 Unity Hub 发现 进不了 Unity3D 了
  3. Mujoco雅克比-逆运动-传感器
  4. 学习强国---Android逆向及JS逆向
  5. USB免驱接触式FM4442卡ISO7816读写器S3-BMU-A0函数使用规则
  6. 【数据结构与算法】详解什么是哈希表,并用代码手动实现一个哈希表
  7. java调用jni_Java调用JNI
  8. 车载电子电器防水防尘等级介绍
  9. 用友华表Cell组件/插件注册
  10. tl494cn逆变器电路图_基于TL494CN的车载逆变器电路设计