【Windows编程】系列第九篇:剪贴板使用

摘要:
在上一章中,我们了解了常见的通用对话框。本章介绍剪贴板的使用,剪贴板通常用于复制和粘贴功能。剪贴板是Windows最早的功能。因为它非常实用,我们几乎每天都使用它。许多文档处理软件都具有复制、剪切和粘贴功能,这些功能都是用Windows剪贴板实现的。当然,我们也可以在程序中实现自己的剪贴板功能。在本章中,我们将实现自己的剪贴板。这仍然是老规矩。让我们首先介绍剪贴板的常见功能,然后使用示例演示基本用法。

上一篇我们学习了常见的通用对话框,本篇来了解剪贴板的使用,它常用于复制粘贴功能。

剪贴板是Windows最早就加入的功能,由于该功能非常实用,我们几乎每天都会使用到。通过剪贴板,我们就可以将数据从一个应用程序传递到另一个应用程序,是一种简单的进程间通信。

许多文档处理软件都有复制、剪切、粘贴功能,这些都是用Windows剪贴板实现的,当然我们也可以在我们的程序中实现自己的剪贴板功能,本篇我们就来实现自己的剪贴板。使用剪贴板时,都是先把源数据先传到剪贴板上,再在需要的时候从剪贴板传输到目的处,所以看起来我们就是直接从源直接搬到目的处。

Windows的控件比如EditBox,已经在控件内部实现了复制、剪切和粘贴功能,所以我们能直接使用。但我们很多控件是没有这个功能的,比如静态文本控件,自己创建的窗口等,我们就没有办法直接拷贝粘贴,这些都是需要我们自己实现的。还是老规矩,下面我们先介绍剪贴板常用函数,然后用实例来演示基本用法。

  • 剪贴板常用函数

不管是复制还是粘贴,使用剪贴板首先要打开它,打开剪贴板API函数如下:

BOOL OpenClipboard(HWND hWndNewOwner);

唯一参数hWndNewOwner是和剪贴板关联的窗口句柄,如果该参数为NULL,则关联到当前任务。

在从源设置数据到剪贴板之前,必须先清空剪贴板,同时得到剪贴板的占有权。API函数如下:

Bool EmptyClipboard(void)

该函数没有参数。

向剪贴板设置数据,也就是拷贝操作:

HANDLE SetClipboardData(UINT uFormat, HANDLE hMem);

参数uFormat表示要设置传输到剪贴板上的数据格式,预定义的格式有十几种,我们这里列出几个最常用的,其他请参考MSDN:

CF_TEXT:表示要设置的格式为以NULL结尾的ANSI字符串,及标C的字符串。这是最简单的剪贴簿数据格式。

CF_UNICODETEXT:格式为包含Unicode字符集的字符串。

CF_BITMAP:格式为设备相关的位图。

参数hMem:设置数据的句柄,一般为GlobalAlloc函数返回的句柄。

设置完数据或者从剪贴板获取数据之后,需要关闭剪贴板,否则其他应用程序无法再打开剪贴板。API函数为:

Bool CloseClipboard(void);

该函数没有参数。

从剪贴板获取数据,也就是粘贴操作,API函数为:

HANDLE GetClipboardData(UINT uFormat);

参数uFormat就是想要获取的数据格式。

查询剪贴板中是否有指定格式的数据:

BOOL IsClipboardFormatAvailable(UINT format);

参数format就是想要查询的数据格式,该函数不需要打开剪贴板。

  • 剪贴板实例

以上几个就是最主要的剪贴板相关函数:

下面我们用这些函数来完成基本的文本复制粘贴和图像复制粘贴功能,为了演示,我们设定了一幅图和一行文本作为数据源,同时创建两个按钮“copy image”和“copy text”分别用于复制图像和文本,当点击时复制对于的数据。

为了程序简洁,我用了鼠标左键来抬起作为粘贴触发,粘贴的位置就是抬起鼠标时的鼠标位置,您可以在不同的地方多次点击后抬起鼠标来重复粘贴,具体代码如下:

#include <windows.h>
#include <tchar.h>

#define IDC_LABEL     1000
#define IDC_COPY_IMG  1001
#define IDC_COPY_TXT  1002

static TCHAR szAppName[] = TEXT("Clipboard Demo");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
     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,            // window class name
                          szAppName,           // window caption
                          WS_OVERLAPPEDWINDOW, // window style
                          CW_USEDEFAULT,       // initial x position
                          CW_USEDEFAULT,       // initial y position
                          400,              // initial x size
                          300,              // initial y size
                          NULL,             // parent window handle
                          NULL,             // window menu handle
                          hInstance,        // program instance handle
                          NULL);            // creation parameters
     
     ShowWindow(hWnd, iCmdShow);
     UpdateWindow(hWnd);
     
     while (GetMessage(&msg, NULL, 0, 0))
     {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
     }

     return msg.wParam;
}

static int DrawBmp(HDC hDC, int xDst, int yDst, int width, int height, int BytesPerPixel, unsigned char *pPixels)
{
	int ret = -1;
	HDC hdcMem;
	BITMAPINFO bmi;
	BYTE *pBits = NULL;
	
	memset(&bmi, 0x00, sizeof(BITMAPINFO));
	bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	bmi.bmiHeader.biWidth = width;
	bmi.bmiHeader.biHeight = height;
	bmi.bmiHeader.biPlanes = 1;
	bmi.bmiHeader.biBitCount = BytesPerPixel*8;
	bmi.bmiHeader.biCompression = BI_RGB;
	
	hdcMem = CreateCompatibleDC(hDC);
	if (hdcMem)
	{
		HBITMAP hBitmap = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void **)&pBits, NULL, 0);
		if (hBitmap)
		{
			HGDIOBJ hOldBmp = SelectObject(hdcMem, hBitmap);
			memcpy(pBits, pPixels, width * height * BytesPerPixel);
			BitBlt(hDC, xDst, yDst, width, height, hdcMem, 0, 0, SRCCOPY);
			SelectObject(hdcMem, hOldBmp);
			DeleteObject(hBitmap);
			ret = 0;
		}
		DeleteDC(hdcMem);
	}
	
	return ret;
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hDC;
	static HBITMAP hBmp;
	static BITMAP  bm;

	switch (message)
	{
	case WM_CREATE:
		CreateWindow(TEXT("STATIC"), TEXT("你好,World!"), WS_CHILD|WS_VISIBLE, 10, 160, 100, 20, hWnd, (HMENU)IDC_LABEL, NULL, NULL);
		CreateWindow(TEXT("BUTTON"), TEXT("copy image"), WS_CHILD|WS_VISIBLE, 10, 190, 100, 20, hWnd, (HMENU)IDC_COPY_IMG, NULL, NULL);
		CreateWindow(TEXT("BUTTON"), TEXT("copy text"), WS_CHILD|WS_VISIBLE, 10, 220, 100, 20, hWnd, (HMENU)IDC_COPY_TXT, NULL, NULL);
		hBmp = (HBITMAP)LoadImage(NULL, TEXT("start.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		GetObject(hBmp, sizeof(BITMAP), &bm);
		return 0;

	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			hDC = BeginPaint(hWnd, &ps);
			HDC hMemDC = CreateCompatibleDC(hDC);
			HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBmp);
			BitBlt(hDC, 10, 10, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY);
			DeleteDC(hMemDC);
			EndPaint(hWnd, &ps);
		}
		return 0;

	case WM_COMMAND:
		{
			int id = LOWORD(wParam);
			switch (id)
			{
			case IDC_COPY_IMG:
				{
					BOOL ret;
					BYTE *pData = NULL;
					BITMAPINFO bmpInfo;

					bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
					bmpInfo.bmiHeader.biWidth = bm.bmWidth;
					bmpInfo.bmiHeader.biHeight = bm.bmHeight;
					bmpInfo.bmiHeader.biPlanes = 1;
					bmpInfo.bmiHeader.biBitCount = 32;
					bmpInfo.bmiHeader.biCompression = BI_RGB;

					HDC hClientDC = GetDC(hWnd);
					HDC hMemDC = CreateCompatibleDC(hClientDC);
					HBITMAP hBitmap = CreateDIBSection(hMemDC, &bmpInfo, DIB_RGB_COLORS, (void **)&pData, NULL, 0);
					SelectObject(hMemDC, hBitmap);
					ret = BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hClientDC, 10, 10, SRCCOPY);
					DeleteDC(hMemDC);

					LONG len = bm.bmWidth * bm.bmHeight * 4;
					HGLOBAL hClipData = GlobalAlloc(GHND, len);
					BYTE *pClipData = (BYTE *)GlobalLock(hClipData);
					memcpy(pClipData, pData, len);
					ret = GlobalUnlock(hClipData);

					ret = OpenClipboard(hWnd);
					ret = EmptyClipboard();
					SetClipboardData(CF_BITMAP, hClipData);
					ret = CloseClipboard();

					DeleteObject(hBitmap);
					ReleaseDC(hWnd, hClientDC);

					MessageBox(hWnd, TEXT("image has been copy into clipboard"), TEXT("info"), MB_OK);
				}
				break;

			case IDC_COPY_TXT:
				{
					BOOL ret;
					TCHAR buf[256];

					GetWindowText(GetDlgItem(hWnd, IDC_LABEL), buf, _countof(buf));
					int len = _tcslen(buf) + 1;

					HGLOBAL hClipData = GlobalAlloc(GHND, len * sizeof(TCHAR));
					TCHAR *pClipData = (TCHAR *)GlobalLock(hClipData);
					memcpy(pClipData, buf, len * sizeof(TCHAR));
					pClipData[len-1] = (TCHAR)0;
					ret = GlobalUnlock(hClipData);

					ret = OpenClipboard(hWnd);
					ret = EmptyClipboard();
					SetClipboardData(CF_TEXT, hClipData);
					ret = CloseClipboard();

					MessageBox(hWnd, TEXT("text has been copy into clipboard"), TEXT("info"), MB_OK);
				}
				break;

			default:
				break;
			}
		}
		return 0;

	case WM_LBUTTONUP:
		{
			BOOL ret;
			WORD xPos = LOWORD(lParam); 
			WORD yPos = HIWORD(lParam);

			ret = IsClipboardFormatAvailable(CF_BITMAP);
			if (ret)
			{
				ret = OpenClipboard(hWnd);
				HGLOBAL hglb = GetClipboardData(CF_BITMAP);
				//len = GlobalSize(hglb);
				BYTE *pClipData = (BYTE *)GlobalLock(hglb);
				HDC hClientDC = GetDC(hWnd);
				DrawBmp(hClientDC, xPos, yPos, bm.bmWidth, bm.bmHeight, 4, pClipData);
				GlobalUnlock(hglb);
				CloseClipboard();
				ReleaseDC(hWnd, hClientDC);
			}

			ret = IsClipboardFormatAvailable(CF_TEXT);
			if (ret)
			{
				ret = OpenClipboard(hWnd);
				HGLOBAL hglb = GetClipboardData(CF_TEXT);
				TCHAR *pClipData = (TCHAR *)GlobalLock(hglb);
				HDC hClientDC = GetDC(hWnd);
				int len = _tcslen(pClipData);
				//len = GlobalSize(hglb);
				TextOut(hClientDC, xPos, yPos, pClipData, len);
				GlobalUnlock(hglb);
				CloseClipboard();
				ReleaseDC(hWnd, hClientDC);
			}
		}
		return 0;

	case WM_DESTROY:
		DeleteObject(hBmp);
		PostQuitMessage(0);
		return 0 ;
	}

	return DefWindowProc (hWnd, message, wParam, lParam);
}

示例中在设置剪贴板时,需要用GlobalAlloc函数分配全局内存,设置和获取数据前需要GlobalLock函数锁定内存一般拷贝数据,使用后再用GlobalUnlock函数解锁内存,这几个函数在使用剪贴板基本成了讨论,照葫芦画瓢就行了,要了解详细参数请查看MSDN。本示例程序程序运行后,分别点击了拷贝图像和拷贝文本本随意点击,效果如下:

clipboard

总的来说剪贴板的基本应用还是比较简单,相关的其他或更多的信息请查看MSDN。对本文有什么疑议请给我留言。

更多经验交流可以加入Windows编程讨论QQ群454398517

关注微信公众平台:程序员互动联盟(coder_online),你可以第一时间获取原创技术文章,和(java/C/C++/Android/Windows/Linux)技术大牛做朋友,在线交流编程经验,获取编程基础知识,解决编程问题。程序员互动联盟,开发人员自己的家。

【Windows编程】系列第八篇:创建通用对话框

转载请注明出处http://www.coderonline.net/?p=1815,谢谢合作!

免责声明:文章转载自《【Windows编程】系列第九篇:剪贴板使用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇php的CodeIgniter学习笔记(一)C# 与 C++ 数据类型比较及结构体转换下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

PB各对象常用事件

1.window中的事件    事件名                  触发的时机 01.Activate            在窗口激活之前触发 02.Clicked             当用户用鼠标单击窗口的空白区域(没有控件的区域)时触发 03.Close               当关闭窗口时触发 04.CloseQuery       ...

centos 重启网络服务的方法

linux 重启网络服务的方法 - Linux - 5x54学习笔记 - Linux自学手册、Linux常见问题解决、原创作品整理 linux 重启网络服务的方法 如果是redhat或CentOS,重启网络可以输入 复制内容到剪贴板代码: service network restart 或者 复制内容到剪贴板代码: /etc/rc.d/init.d/ne...

迅雷/快车/旋风地址转换器

一个用来转换迅雷/快车/旋风下载地址的小工具 应网友要求开放源代码,转载请说明出处,谢谢 点击下载 源代码下载 现在这几个下载工具的斗争越来越厉害,为了争夺更多的用户,他们将普通的链接加密成只有他们的产品才能识别的格式 结果就让我们这些用户倒了霉,我本身只用迅雷的(连BT都用她),可是为了下载某些资源,不得不安装相应的下载工具,不胜其烦 后来研究了下,发现...

vim复制粘贴到系统剪贴板

一般来讲,如果你没有在.vimrc中配置过相关的信息的话,可以考虑下面的方法。系统环境 Ubuntu 14.04 LTS。 安装与使用 首先需要安装一个vim-gtk命令$sudo apt-get install vim-gtk 关掉现有vim,重新打开后就可以使剪切板互通了。vim中复制一行到系统剪切板的命令"+yy, (共计4个字符,按序快速按就行了...

如何将文件拷贝到剪贴板

怎样判断当前剪贴板中的内容为文件,如何将指定文件(不是文件内容)拷贝到剪贴板中。请问各位大侠:怎样判断当前剪贴板中的内容为文件,如何将指定文件(不是文件内容)拷贝到剪贴板中。int GetClipboardFormatName(  UINT format,            // clipboard format to retrieve  LPTSTR...

六步升级你的Debian Linux内核到当前最新稳定版本

本文经验基于如下环境:原Debian版本为:Debian GNU/Linux 3.1原内核版本为:2.6.8-2-686下载的新内核版本为:2.6.15.6下载文件的存放路径为:/tmp参考了文档:/usr/share/doc/kernel-package/README.gz关于本文档:/**********************************...