【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。本示例程序程序运行后,分别点击了拷贝图像和拷贝文本本随意点击,效果如下:

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

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

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

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-04 18:18:25

【Windows编程】系列第九篇:剪贴板使用的相关文章

[高并发]Java高并发编程系列开山篇--线程实现

ava是最早开始有并发的语言之一,再过去传统多任务的模式下,人们发现很难解决一些更为复杂的问题,这个时候我们就有了并发. 引用 多线程比多任务更加有挑战.多线程是在同一个程序内部并行执行,因此会对相同的内存空间进行并发读写操作.这可能是在单线程程序中从来不会遇到的问题.其中的一些错误也未必会在单CPU机器上出现,因为两个线程从来不会得到真正的并行执行.然而,更现代的计算机伴随着多核CPU的出现,也就意味着不同的线程能被不同的CPU核得到真正意义的并行执行. 那么,要开始Java并发之路,就要开始

MongoDB基础教程系列--第九篇 MongoDB 分片

1.分片介绍 分片(sharding)是将数据拆分,将其分散存到不同机器上的过程.MongoDB 支持自动分片,可以使数据库架构对应用程序不可见.对于应用程序来说,好像始终在使用一个单机的 MongoDB 服务器一样,另一方面,MongoDB 自动处理数据在分片上的分布,也更容易添加和删除分片. 请记住:复制是让多台服务器拥有同样的数据副本,每一台服务器都是其他服务器的镜像,而每一个分片都与其他分片拥有不同的数据子集. 通常,分片可以用来: 增加可用的内存 增加可用的磁盘空间 减轻单台服务器的负

[C入门 - 游戏编程系列] 环境篇

这一系列笔记的代码都是在Ubuntu 14.04下编码并测试的,原因无他,因为我笔记本电脑只装了一个Ubuntu系统,其中唯一使用的第三方库SDL也是开源并且跨平台的.所以即使你用的是Windows或Mac,也能运行所有的代码. 1. 安装SDL库及其扩展 Ubuntu: sudo apt-get install libsdl2-dev sudo apt-get install libsdl2-gfx-dev sudo apt-get install libsdl2-image-dev sudo

[C入门 - 游戏编程系列] 序言篇

记得学习C语言的时候,看着别人能写各种各样的小游戏和小软件,甚是羡慕.而自己,虽然说语法都会,但是真正上手写个几百行的代码,就显得力不从心.曾经一度很是郁闷,看过一些书,大都处于教语法的层面,有些涉及到软件设计,但是对于几百行代码都驾驭不了的我,看了也是等于白看,完全领悟不到设计中蕴含的哲学和精髓. 所以,本来盘算着第一个月学完语法,第二个月写出自己所谓的“软件”的这个美好愿望,被无情的打破了.现实是,语法确实在一个月学完了,但是坑爹的是,除了在群里或者论坛中增加了一点谈论或者说冒充“大神”的资

深入理解ajax系列第九篇——jQuery中的ajax

前面的话 jQuery提供了一些日常开发中需要的快捷操作,例如load.ajax.get和post等,使用jQuery开发ajax将变得极其简单.这样开发人员就可以将程序开发集中在业务和用户体验上,而不需要理会那么繁琐的XMLHTTPRequest对象.jQuery对ajax操作进行了封装,在jQuery中$.ajax()属性最底层的方法,第2层是load().$.get()和$.post()方法,第3层是$.getScript()和$.getJSON()方法.下面将详细介绍jQuery中的aj

javascript运动系列第九篇——碰撞运动

× 目录 [1]碰撞检测 [2]无损碰撞 [3]有损碰撞 前面的话 碰撞可以分为碰壁和互碰两种形式,上篇介绍了碰壁运动,本文将从浅入深地介绍碰撞运动的互碰形式 碰撞检测 对于互碰形式的碰撞运动来说,首先要解决的是碰撞检测.对于矩形元素的碰撞检测前面的博文已经详细介绍过,下面主要介绍圆形元素的碰撞检测 矩形元素的碰撞检测利用九宫格分析法,而圆形元素的碰撞检测则简单很多,判断两个圆形元素的半径之和是否大于两个圆形元素的圆心点坐标之间的距离即可 由示意图可知,元素一的圆心位置为(x1,y1),半径为r

黑马程序员系列第九篇 类加载器

ASP.Net+Android+IOS开发  .Net培训.期待与您交流! 推荐阅读相关书籍<深入java虚拟机> 目录:1.java.lang.ClassLoader类介绍      2.类加载器的结构     3.加载类的过程     4.自定义类加载器 类加载器负责加载 Java 类的字节代码到 Java 虚拟机中. 类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一.它使得 Java 类可以被动态加载到 Java 虚拟机中并执行. Java 虚拟机使用 Jav

【Windows编程】系列第四篇:使用Unicode编程

上一篇我们学习了Windows编程的文本及字体输出,在以上几篇的实例中也出现了一些带有“TEXT”的Windows宏定义,有朋友留言想了解一些ANSI和Unicode编程方面的内容,本章就来了解和学习一些Windows下关于ANSI和Unicode方面的编程基础. 计算机最早在美国诞生,所以最开始都是以英语为作为交互语言,由于只有26个字母,用一个字节(范围-128 ~ 127)表示,这个范围足够表示26个因为字符和一些常用的控制字符,这个就是ASCII编码.因此最早的各种程序设计语言以及使用的

【Windows编程】系列第五篇:GDI图形绘制

上两篇我们学习了文本字符输出以及Unicode编写程序,知道如何用常见Win32输出文本字符串,这一篇我们来学习Windows编程中另一个非常重要的部分GDI图形绘图.Windows的GDI函数包含数百个API可供我们使用,本篇把最常用的GDI绘图做一个讲解.GDI可以绘制点.直线曲线.填充封闭区域.位图以及文本,其中文本部分已经在上一篇中将了,请参考[Windows编程]系列第三篇:文本字符输出. 跟前面的GDI对象一样,本篇的这些绘图函数也必须要设备上下文句柄(HDC)作为函数参数,从前文我