【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,谢谢合作!