监视剪贴板内容变化的程序叫“剪贴板查看器”。在Windows中已经拥有了剪贴板查看器,当然也可以用API函数编写自己的剪贴板查看器程序。 8.3.1 剪贴板查看器链接列表 在Windows下可以同时运行很多剪贴板查看器,用于时刻监视剪贴板内容的变化情况。但Windows在剪贴板内容发生变化时,只对一个剪贴板查看器窗口发送消息,这就是所谓的“当前剪贴板查看器”。 应用程序想要得到Windows发送给当前剪贴板查看器的消息,就必须加入“剪贴板查看器链接列表”。当一个程序将自己注册为一个剪贴板查看器时,其窗口句柄被Windows留作当前剪贴板查看器窗口句柄,它也就被Windows设置为当前剪贴板查看器。当此程序收到一个剪贴板查看器消息时,就把这个消息发送给剪贴板链表中下一个程序的窗口过程。 8.3.2 有关剪贴板查看器的函数和消息1. 剪贴板查看器函数消息介绍 程序通过调用函数SetClipboardViewer成为剪贴板查看器链表中的一员。如果程序是作为剪贴板查看器使用的,那么在主窗口创建之初就应该调用此函数。即在处理消息WM_CREATE期间,函数返回前一个当前剪贴板查看器的窗口句柄,将其放在静态变量中,如下所示: static HWND hWndNextViewer; case WM_CREATE : hWndNextViewer = SetClipboardViewer(hWnd); 在以后介绍的实例中,大家将看到具体的应用。 一旦成为当前剪贴板查看器,只要剪贴板有任何变化,Windows都会把WM_DRAWCLIPBOARD消息发送给其窗口过程。剪贴板查看器链表中的每一个程序,都应该用SendMessage把这个消息发送给下一个剪贴板查看器。链表中的最后一个程序,就是当前剪贴板查看器的窗口所保存的hWndNextViewer是NULL。当消息传给它后,程序只是简单地返回,消息不再从这里发出。具体的处理方法是除了当前剪贴板查看器,一般都是简单地将消息发送给下一个剪贴板查看器的窗口过程,并使本窗口的客户区无效,如下所示: case WM_DRAWCLIPBOARD : if( hWndNextViewer ) SendMessage ( hWndNextViewer, message, wParam, lParam); InvalidateRect( hWnd, NULL, TRUE); Break; 在处理WM_PAINT期间,可以调用OpenClipboard,GetClipboardData和CloseClipboard读取剪贴板的内容。当一个程序要从剪贴板查看器链表中删除自己时,应当调用ChangeClipboardChain函数。函数原型如下: BOOL ChangeClipboardChain(HWND hWndRemove, HWND hWndNewNext ); 其中,第一个参数hWndRemove是从剪贴板查看器链中删除的窗口句柄;第二个参数hWndNewNext是剪贴板查看器链中hWndRemove后面紧跟的窗口句柄。当程序调用ChangeClipboardChain时,Windows将消息 WM_CHANGECBCHAIN发送到当前剪贴板查看器。在此消息中,参数wParam是函数ChangeClipboardChain的第一个参数,参数lParam是函数ChangeClipboardChain的第二个参数。 当程序收到消息WM_CHANGECBCHAIN,必须检查wParam与保留的hWndNextViewer静态变量是否相同。如果相同,则将hWndNextViewer设置为lParam。此项工作保证了将来WM_DRAWCLIPBOARD消息不会被发送到一个已经不存在的窗口。如果不同,并且hWndNextViewer不为NULL,表明非当前剪贴板查看器,则将消息发送给下一个剪贴板查看器,如下所示: case WM_CHANGECBCHAIN : if( (HWND) wParam ==hWndClipboardViewer ) hWndClipboardViewer = (HWND) lParam; else if (hWndClipboardViewer) SendMessage ( hWndClipboardViewer, message, wParam, lParam); return 0; 当程序要结束时,如果它仍然在剪贴板查看器链中,则必须将其删除。为了保证这一点,可以在处理消息WM_DESTORY时通过调用函数ChangeClipboardChain来实现,如下所示: case WM_DESTORY : ChangeClipboardChain( hWnd, hWndNextViewer ); PostQuitMessage( 0); break; Windows还提供获得第一个剪贴板查看器窗口句柄的函数: HWND GetClipboardViewer(VOID); 一般来说不会用到这个函数。如果没有当前剪贴板查看器,那么此函数的返回值为NULL。 2. 剪贴板查看器的实例分析 下面来看一个剪贴板查看器的具体实例,这个实例提供了显示CF_BITMAP和CF_TEXT的剪贴板查看器。虽然它只提供了Windows剪贴板查看器的部分功能,但从中可以了解剪贴板查看器是如何工作的。 首先,在创建窗口时就设置本窗口为剪贴板查看器链表中的一员,其中函数SetOwner是用在窗口标题表明客户区的内容来自何处。其中,用到了函数GetClipboardOwner,如下所示: case WM_CREATE: hNextViewer = SetClipboardViewer( hWnd ); SetOwner( hWnd ); break; 其次,看消息是如何在剪贴板查看器链表中传递的,如下所示: case WM_DRAWCLIPBOARD: if (hNextViewer) SendMessage( hNextViewer, WM_DRAWCLIPBOARD, wParam, lParam ); InvalidateRect( hWnd, NULL, TRUE ); SetOwner( hWnd ); break; 再看应用程序是如何将自己从剪贴板查看器链表中删除的,如下所示: case WM_CHANGECBCHAIN: if ( (HWND)wParam == hNextViewer ) hNextViewer = (HWND)lParam; else if ( hNextViewer ) SendMessage( hNextViewer, WM_CHANGECBCHAIN, wParam, lParam ); break; 最后是介绍在消息WM_PAINT中实现了什么。真正的具体实现是在这里完成的,在前面已经介绍了有关函数的具体操作,读者看起来应该轻松一些。 case WM_PAINT : { static HANDLE hMem; // 剪贴板文本的句柄 static HBITMAP hBitmap; // 剪贴板位图的句柄 RECT rect; // 客户区 PAINTSTRUCT ps; // 绘图的结构 BeginPaint( hWnd, &ps ); GetClientRect( hWnd, &rect ); OpenClipboard( hWnd ); if (hMem = GetClipboardData( CF_TEXT )) // 如果剪贴板中的内容为文本格式,则执行 { LPSTR lpMem = GlobalLock( hMem ); DrawText( ps.hdc, lpMem, -1, &rect, DT_LEFT ); GlobalUnlock( hMem ); } // 如果非文本格式,测试是否为位图格式 else if (hBitmap = GetClipboardData( CF_BITMAP )) { BITMAP bm; HDC hMemDC = CreateCompatibleDC( ps.hdc ); SelectObject( hMemDC, hBitmap ); GetObject( hBitmap, sizeof (BITMAP), (LPSTR) &bm ); BitBlt( ps.hdc, 0, 0, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY ); DeleteDC( hMemDC ); } CloseClipboard( ); EndPaint( hWnd, &ps ); } break; 通过本章的学习,了解了如何运用API提供的函数来编写剪贴板的程序。在实际的编程中会经常用到剪贴板的内容,希望通过本章的学习,读者在编写程序时能如虎添翼。
|