WindowsAPI查缺补漏-剪贴板

常用函数

OpenClipboard

打开剪贴板:

1
2
3
BOOL OpenClipboard(
_In_opt_ HWND hWndNewOwner //与剪贴板相关联得窗口句柄
);

在关闭剪贴板前其他应用程序无法打开剪贴板。

EmptyClipboard

清空剪贴板中数据,并把OpenClipboard的hWndNewOwner指定的窗口设为剪贴板的新所有者。若hWndNewOwner为NULL则导致后续SetClipboardData失败。

1
BOOL EmptyClipboard(VOID);

SetClipboardData

把指定格式的数据写入剪贴板:

1
2
3
4
HANDLE SetClipboardData(
_In_ UINT uFormat, //要写入剪贴板中数据的格式
_In_opt_ HANDLE hMem //数据的句柄
); //成功返回剪贴板数据句柄 失败NULL

其中uFormat常用的有:

枚举值 含义
CF_TEXT ANSI文本格式
CF_UNICODETEXT Unicode文本格式
CF_BITMAP 设备相关位图
CF_DIB 设备无关位图
CF_SYLK 符号链接格式
CF_WAVE 标准波形格式的音频数据
CF_OWNERDISPLAY 所有者显示格式

对于CF_OWNERDISPLAY,hMem必须为NULL,剪贴板所有者必须显示和更新剪贴板查看器窗口,接收WM_ASKCBFORMATNAME、WM_HSCROLLCLIPBOARD、WM_PAINTCLIPBOARD、WM_SIZECLIPBOARD、WM_VSCROLLCLIPBOARD消息。

当hMem为NULL时,则表示在GetClipboardData等时通过消息确定格式,即延迟提交技术。延迟提交即例如将文件、图片等大型数据放入剪贴板中时,只放入一个标识,直到请求数据或自身进程终止时才提交真正的数据。

调用SetClipboardData后不能将hMem释放、锁定或挪作他用。

GetClipboardData

从剪贴板中获取指定格式数据:

1
2
3
HANDLE GetClipboardData(
_In_ UINT uFormat
); //成功返回指定格式的剪贴板数据句柄 失败NULL

CloseClipboard

关闭剪贴板:

1
BOOL CloseClipboard(VOID);

RegisterClipboardFormat

注册一个新的剪贴板数据格式:

1
2
3
UINT RegisterClipboardFormat(
_In_ LPCTSTR lpszFormat //剪贴板格式名称 不区分大小写
); //成功返回值表示已注册的剪贴板数据格式

已存在则返回已存在的格式标识值,已注册剪贴板数据格式在0xC000~0xFFFF之间,注册后其他应用程序都可使用。

GetClipboardFormatName

后去已注册剪贴板数据格式名称:

1
2
3
4
5
INT GetClipboardFormatName(
_In_ UINT format, //已注册剪贴板数据格式标识值
_Out_ LPTSTR lpszFormatName, //接收剪贴板数据格式名称缓冲区
_In_ INT cchMaxCount //缓冲区长度 单位字符
); //成功返回复制到缓冲区字符数 失败0

CountClipboardFormats

获取已注册剪贴板数据格式名称:

1
INT CountClipboardFormats(VOID); //成功非0 失败0

EnumClipboardFormats

枚举剪贴板中当前可用的剪贴板数据格式:

1
2
3
UINT EnumClipboardFormats(
_In_ UINT format
);

枚举时需要循环调用该函数,第一次format为0,返回第一个可用剪贴板数据格式,后续调用时format为上次调用返回值,知道返回0,如:

1
2
3
4
5
6
UINT uFormat = 0;
uFormat = EnumClipboardFormats(0);
while (uFormat) {
//...
uFormat = EnumClipboardFormats(uFormat);
};

IsClipboardFormatAvailable

确定剪贴板中是否包含该格式的数据:

1
2
3
BOOL IsClipboardFormatAvailable(
_In_ UINT format //标准或注册剪贴板格式
); //包含返回非0 否则0

例子

写入不同格式数据项:

1
2
3
4
5
6
7
8
9
10
11
12
TCHAR szText[] = TEXT("xxx");
LPTSTR lpText = NULL;
HBITMAP hBitmap = NULL;
lpText = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (_tcslen(szText) + 1) * sizeof(TCHAR));
if (lpText)
StringCchCopy(lpText, _tcslen(szText) + 1, szText);
hBitmap = LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDS_BITMAP));
OpenClipboard(hwndDlg);
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT, lpText);
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();

数据获取:

1
2
3
4
5
LPTSTR lpStr;
OpenClipboard(hwndDlg);
lpStr = (LPTSTR)GetClipboardData(CF_UNICODETEXT);
hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
CloseClipboard();

剪贴板相关消息

WM_RENDERFORMAT

剪贴板所有者采用延迟提交后,有进程对剪贴板请求数据时,剪贴板所有者窗口过程将收到该消息,wParam标识所需剪贴板数据格式,处理完该消息后返回0。

例如延迟提交:

1
2
3
4
OpenClipboard(hwnd);
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT, NULL);
CloseClipboard();

剪贴板所有者处理:

1
2
3
4
5
6
TCHAR szText[] = TEXT("xxx");
LPVOID lpMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (_tcslen(szText) + 1) * sizeof(TCHAR));
if (lpMemory) {
StringCchCopy((LPTSTR)lpMemory, _tcslen(szText) + 1, szText);
SetClipboardData(wParam, lpMemory);
};

WN_DESTROYCLIPBOARD

其他进程打开了剪贴板并用EmptyClipboard清空剪贴板并接管剪贴板所有权,剪贴板所有者收到WM_DESTROYCLIPBOARD消息,通知该进程对剪贴板所有权丧失,窗口过程处理完该消息后应返回0。

WM_RENDERALLFORMATS

当另一个进程为剪贴板所有者,该所有者运用延迟提交且进程将要终止时,系统向其他进程发送该消息,希望其他进程接管运用延迟技术的剪贴板。一般需要依次OpenClipboardEmptyClipboardSetClipboardDataCloseClipboard。wParam和lParam都没用上,处理完后应返回0。

剪贴板进行进程间通信

写入端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>
#include "resource.h"
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// 定义CF_TCHAR,不管程序是Unicode还是ANSI版本,剪贴板文本数据格式都会被正确设置
#ifdef UNICODE
#define CF_TCHAR CF_UNICODETEXT
#else
#define CF_TCHAR CF_TEXT
#endif
// 自定义剪贴板数据格式
typedef struct _CUSTOM_DATA{
// 假设存储的是一个人的姓名、年龄
TCHAR szName[128];
UINT uAge;
}CUSTOM_DATA, * PCUSTOM_DATA;
// 全局变量
HINSTANCE g_hInstance; // 实例句柄
HWND g_hwnd; // 窗口句柄
HWND g_hwndList; // 列表框窗口句柄
UINT g_uFormat; // 注册剪贴板数据格式
// 函数声明
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID OnInit(HWND hwndDlg); // WM_INITDIALOG
VOID OnBtnText(); // 写入文本按钮按下
VOID OnBtnBitmap(); // 写入位图按钮按下
VOID OnBtnCustom(); // 写入自定义数据按钮按下
VOID OnBtnDelay(); // 延迟提交按钮按下
VOID OnBtnMultiple(); // 写入多项数据按钮按下
VOID OnRenderFormat(UINT uFormat); // WM_RENDERFORMAT
VOID OnDestroyClipbord(); // WM_DESTROYCLIPBOARD
VOID OnRenderAllFormats(); // WM_RENDERALLFORMATS
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
g_hInstance = hInstance;
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
return 0;
};
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_INITDIALOG: {
OnInit(hwndDlg);
return TRUE;
};
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_BTN_TEXT: {
OnBtnText();
break;
};
case IDC_BTN_BITMAP: {
OnBtnBitmap();
break;
};
case IDC_BTN_CUSTOM: {
OnBtnCustom();
break;
};
case IDC_BTN_DELAY: {
OnBtnDelay();
break;
};
case IDC_BTN_MULTIPLE: {
OnBtnMultiple();
break;
};
case IDCANCEL: {
EndDialog(hwndDlg, IDCANCEL);
break;
};
};
return TRUE;
};
case WM_RENDERFORMAT: {
OnRenderFormat(wParam);
return FALSE;
};
case WM_DESTROYCLIPBOARD: {
OnDestroyClipbord();
return FALSE;
};
case WM_RENDERALLFORMATS: {
OnRenderAllFormats();
return FALSE;
};
};
return FALSE;
};
/****************************************************************/
VOID OnInit(HWND hwndDlg) { // WM_INITDIALOG
g_hwnd = hwndDlg;
g_hwndList = GetDlgItem(hwndDlg, IDC_LIST_MSG);
// 注册一个自定义剪贴板格式
g_uFormat = RegisterClipboardFormat(TEXT("RegisterFormat"));
};
VOID OnBtnText() { // 写入文本按钮按下
TCHAR szText[128] = { 0 };
LPTSTR lpStr;
// 打开剪贴板
OpenClipboard(g_hwnd);
// 清空剪贴板
EmptyClipboard();
// 获取文本数据编辑框的文本
GetDlgItemText(g_hwnd, IDC_EDIT_TEXT, szText, _countof(szText));
// 分配内存
lpStr = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (_tcslen(szText) + 1) * sizeof(TCHAR));
if (lpStr) {
// 复制文本数据到刚刚分配的内存中
StringCchCopy(lpStr, _tcslen(szText) + 1, szText);
// 将内存中的数据放置到剪贴板
//SetClipboardData(CF_TEXT, lpStr);
//SetClipboardData(CF_UNICODETEXT, lpStr);
SetClipboardData(CF_TCHAR, lpStr);
};
// 关闭剪贴板
CloseClipboard();
};
VOID OnBtnBitmap() { // 写入位图按钮按下
// 打开剪贴板
OpenClipboard(g_hwnd);
// 清空剪贴板
EmptyClipboard();
// 设置剪贴板位图数据
HDC hdcDesk, hdcMem;
HBITMAP hBmp;
int nWidth = GetSystemMetrics(SM_CXSCREEN);
int nHeight = GetSystemMetrics(SM_CYSCREEN);
hdcDesk = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
hdcMem = CreateCompatibleDC(hdcDesk);
hBmp = CreateCompatibleBitmap(hdcDesk, nWidth, nHeight);
SelectObject(hdcMem, hBmp);
BitBlt(hdcMem, 0, 0, nWidth, nHeight, hdcDesk, 0, 0, SRCCOPY);
SetClipboardData(CF_BITMAP, hBmp);
DeleteObject(hBmp);
DeleteDC(hdcMem);
DeleteDC(hdcDesk);
// 关闭剪贴板
CloseClipboard();
};
VOID OnBtnCustom() { // 写入自定义数据按钮按下
LPVOID lpMem;
// 自定义数据
CUSTOM_DATA customData = { TEXT("老王"), 40 };
// 打开剪贴板
OpenClipboard(g_hwnd);
// 清空剪贴板
EmptyClipboard();
// 分配内存
lpMem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CUSTOM_DATA));
if (lpMem) {
// 复制自定义数据到刚刚分配的内存中
memcpy_s(lpMem, sizeof(CUSTOM_DATA), &customData, sizeof(CUSTOM_DATA));
// 设置剪贴板自定义数据
SetClipboardData(g_uFormat, lpMem);
};
// 关闭剪贴板
CloseClipboard();
};
VOID OnBtnDelay() { // 延迟提交按钮按下
// 打开剪贴板
OpenClipboard(g_hwnd);
// 清空剪贴板
EmptyClipboard();
// 设置剪贴板延迟提交位图数据
SetClipboardData(CF_BITMAP, NULL);
// 关闭剪贴板
CloseClipboard();
};
VOID OnBtnMultiple() { // 写入多项数据按钮按下
// 打开剪贴板
OpenClipboard(g_hwnd);
// 清空剪贴板
EmptyClipboard();
// 文本数据
TCHAR szText[128] = { 0 };
LPTSTR lpStr;
GetDlgItemText(g_hwnd, IDC_EDIT_TEXT, szText, _countof(szText));
lpStr = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (_tcslen(szText) + 1) * sizeof(TCHAR));
if (lpStr) {
StringCchCopy(lpStr, _tcslen(szText) + 1, szText);
SetClipboardData(CF_TCHAR, lpStr);
};
// 位图数据
HDC hdcDesk, hdcMem;
HBITMAP hBmp;
int nWidth = GetSystemMetrics(SM_CXSCREEN);
int nHeight = GetSystemMetrics(SM_CYSCREEN);
hdcDesk = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
hdcMem = CreateCompatibleDC(hdcDesk);
hBmp = CreateCompatibleBitmap(hdcDesk, nWidth, nHeight);
SelectObject(hdcMem, hBmp);
BitBlt(hdcMem, 0, 0, nWidth, nHeight, hdcDesk, 0, 0, SRCCOPY);
SetClipboardData(CF_BITMAP, hBmp);
DeleteObject(hBmp);
DeleteDC(hdcMem);
DeleteDC(hdcDesk);
// 自定义数据
LPVOID lpMem;
CUSTOM_DATA customData = { TEXT("老王"), 40 };
lpMem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CUSTOM_DATA));
if (lpMem) {
memcpy_s(lpMem, sizeof(CUSTOM_DATA), &customData, sizeof(CUSTOM_DATA));
SetClipboardData(g_uFormat, lpMem);
};
// 关闭剪贴板
CloseClipboard();
};
VOID OnRenderFormat(UINT uFormat) { // WM_RENDERFORMAT
SendMessage(g_hwndList, LB_ADDSTRING, 0, (LPARAM)TEXT("WM_RENDERFORMAT"));
// 设置剪贴板位图数据
HDC hdcDesk, hdcMem;
HBITMAP hBmp;
int nWidth = GetSystemMetrics(SM_CXSCREEN);
int nHeight = GetSystemMetrics(SM_CYSCREEN);
hdcDesk = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
hdcMem = CreateCompatibleDC(hdcDesk);
hBmp = CreateCompatibleBitmap(hdcDesk, nWidth, nHeight);
SelectObject(hdcMem, hBmp);
BitBlt(hdcMem, 0, 0, nWidth, nHeight, hdcDesk, 0, 0, SRCCOPY);
SetClipboardData(uFormat, hBmp);
DeleteObject(hBmp);
DeleteDC(hdcMem);
DeleteDC(hdcDesk);
};
VOID OnDestroyClipbord() { // WM_DESTROYCLIPBOARD
SendMessage(g_hwndList, LB_ADDSTRING, 0, (LPARAM)TEXT("WM_DESTROYCLIPBOARD"));
};
VOID OnRenderAllFormats() { // WM_RENDERALLFORMATS
SendMessage(g_hwndList, LB_ADDSTRING, 0, (LPARAM)TEXT("WM_RENDERALLFORMATS"));
OnBtnBitmap();
};

读取端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>
#include "resource.h"
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// 定义CF_TCHAR,不管程序是Unicode还是ANSI版本,剪贴板文本格式数据都能被正确获取
#ifdef UNICODE
#define CF_TCHAR CF_UNICODETEXT
#else
#define CF_TCHAR CF_TEXT
#endif
// 自定义剪贴板数据格式
typedef struct _CUSTOM_DATA{
// 假设存储的是一个人的姓名、年龄
TCHAR szName[128];
UINT uAge;
}CUSTOM_DATA, * PCUSTOM_DATA;
// 全局变量
HINSTANCE g_hInstance; // 实例句柄
HWND g_hwnd; // 窗口句柄
UINT g_uFormat; // 注册剪贴板数据格式
// 函数声明
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
VOID OnInit(HWND hwndDlg);
VOID OnBtnText(); // 读取文本数据按钮按下
VOID OnBtnBitmap(); // 读取位图按钮按下
VOID OnBtnCustom(); // 读取自定义数据按钮按下
VOID OnBtnDelay(); // 读取延迟提交按钮按下
VOID OnBtnMultiple(); // 读取多项数据按钮按下
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
g_hInstance = hInstance;
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
return 0;
};
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_INITDIALOG: {
OnInit(hwndDlg);
return TRUE;
};
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_BTN_TEXT: {
OnBtnText();
break;
};
case IDC_BTN_BITMAP: {
OnBtnBitmap();
break;
};
case IDC_BTN_CUSTOM: {
OnBtnCustom();
break;
};
case IDC_BTN_DELAY: {
OnBtnDelay();
break;
};
case IDC_BTN_MULTIPLE: {
OnBtnMultiple();
break;
};
case IDCANCEL: {
EndDialog(hwndDlg, IDCANCEL);
break;
};
};
return TRUE;
};
};
return FALSE;
};
/****************************************************************/
VOID OnInit(HWND hwndDlg) {
g_hwnd = hwndDlg;
// 注册一个自定义剪贴板格式,和写入端使用相同的数据格式名称,因此返回相同的格式标识值
g_uFormat = RegisterClipboardFormat(TEXT("RegisterFormat"));
// 显示图片的静态控件设置SS_BITMAP | SS_REALSIZECONTROL样式
LONG lStyle = GetWindowLongPtr(GetDlgItem(g_hwnd, IDC_STATIC_BITMAP), GWL_STYLE);
SetWindowLongPtr(GetDlgItem(g_hwnd, IDC_STATIC_BITMAP), GWL_STYLE, lStyle | SS_BITMAP | SS_REALSIZECONTROL);
};
VOID OnBtnText() { // 读取文本数据按钮按下
LPTSTR lpStr;
// 剪贴板是否包含CF_TCHAR格式的文本
if (IsClipboardFormatAvailable(CF_TCHAR)) {
// 打开剪贴板
OpenClipboard(g_hwnd);
// 获取剪贴板中文本格式的数据,并复制到szText缓冲区
lpStr = (LPTSTR)GetClipboardData(CF_TCHAR);
// 显示到编辑框中
SetDlgItemText(g_hwnd, IDC_EDIT_TEXT, lpStr);
// 关闭剪贴板
CloseClipboard();
}
else
MessageBox(g_hwnd, TEXT("剪贴板没有文本格式的数据!"), TEXT("Error"), MB_OK);
};
VOID OnBtnBitmap() { // 读取位图按钮按下
// 剪贴板是否包含CF_BITMAP格式的数据
if (IsClipboardFormatAvailable(CF_BITMAP)) {
// 打开剪贴板
OpenClipboard(g_hwnd);
// 获取剪贴板中位图格式的数据
HBITMAP hBmp = (HBITMAP)GetClipboardData(CF_BITMAP);
// 显示图片
SendDlgItemMessage(g_hwnd, IDC_STATIC_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp);
// 关闭剪贴板
CloseClipboard();
}
else
MessageBox(g_hwnd, TEXT("剪贴板没有位图格式的数据!"), TEXT("Error"), MB_OK);
};
VOID OnBtnCustom() { // 读取自定义数据按钮按下
TCHAR szBuf[128] = { 0 };
PCUSTOM_DATA pCustomData;
// 剪贴板是否包含g_uFormat格式的数据
if (IsClipboardFormatAvailable(g_uFormat)) {
// 打开剪贴板
OpenClipboard(g_hwnd);
// 获取剪贴板中g_uFormat格式的数据
pCustomData = (PCUSTOM_DATA)GetClipboardData(g_uFormat);
wsprintf(szBuf, TEXT("%s, %d"), pCustomData->szName, pCustomData->uAge);
// 显示到编辑框中
SetDlgItemText(g_hwnd, IDC_EDIT_CUSTOM, szBuf);
// 关闭剪贴板
CloseClipboard();
}
else
MessageBox(g_hwnd, TEXT("剪贴板没有自定义格式的数据!"), TEXT("Error"), MB_OK);
};
VOID OnBtnDelay() { // 读取延迟提交按钮按下
// 本程序读取延迟提交实际上读取的也是位图,所以直接调用OnBtnBitmap
OnBtnBitmap();
};
VOID OnBtnMultiple() { // 读取多项数据按钮按下
// 清空文本数据编辑框、自定义数据编辑框和图像静态控件的内容
SetDlgItemText(g_hwnd, IDC_EDIT_TEXT, TEXT(""));
SetDlgItemText(g_hwnd, IDC_EDIT_CUSTOM, TEXT(""));
SendDlgItemMessage(g_hwnd, IDC_STATIC_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)NULL);
// 打开剪贴板
OpenClipboard(g_hwnd);
// 枚举剪贴板上当前可用的数据格式
UINT uFormat = EnumClipboardFormats(0);
while (uFormat) {
// 这里只处理这3种格式
if (uFormat == CF_TCHAR) {
LPTSTR lpStr;
// 获取剪贴板中文本格式的数据,并复制到szText缓冲区
lpStr = (LPTSTR)GetClipboardData(CF_TCHAR);
// 显示到编辑框中
SetDlgItemText(g_hwnd, IDC_EDIT_TEXT, lpStr);
}
else if (uFormat == CF_BITMAP) {
// 获取剪贴板中位图格式的数据
HBITMAP hBmp = (HBITMAP)GetClipboardData(CF_BITMAP);
// 显示图片
SendDlgItemMessage(g_hwnd, IDC_STATIC_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp);
}
else if (uFormat == g_uFormat) {
TCHAR szBuf[128] = { 0 };
PCUSTOM_DATA pCustomData;
// 获取剪贴板中g_uFormat格式的数据
pCustomData = (PCUSTOM_DATA)GetClipboardData(g_uFormat);
wsprintf(szBuf, TEXT("%s, %d"), pCustomData->szName, pCustomData->uAge);
// 显示到编辑框中
SetDlgItemText(g_hwnd, IDC_EDIT_CUSTOM, szBuf);
};
uFormat = EnumClipboardFormats(uFormat);
};
// 关闭剪贴板
CloseClipboard();
};

剪贴板内容变化监视

SetClipboardViewer

将指定窗口添加到剪贴板查看器链中。剪贴板查看器即可以监视剪贴板内容变化的进程。

1
2
3
HWND SetClipboardViewer(
HWND hWndNewViewer
); //返回下一个剪贴板查看器链中下一个窗口 错误或链中没有窗口NULL

该函数应在WM_CREATE或WM_INITDIALOG消息中调用:

1
2
3
4
case WM_INITDIALOG: {
g_hwndNextViewer = SetClipboardViewer(hwndDlg);
break;
};

每当剪贴板内容改变时,剪贴板查看器链会收到WM_DRAWCLIPBOARD消息,链中每个剪贴板查看器都有义务将该消息传递给下一个剪贴板查看器:

1
2
3
4
case WM_DRAWCLIPBOARD: {
if (g_hwndNextViewer)
SendMessage(g_hwndNextViewer, uMsg, wParam, lParam);
};

ChangeClipboardChain

从剪贴板查看器链中删除自身:

1
2
3
4
BOOL ChangeClipboardChain(
_In_ HWND hWndRemove, //要从链中移除的窗口句柄
_In_ HWND hWndNewNext //下一个窗口句柄
);

如响应WM_DESTROY等消息时:

1
2
3
4
5
6
7
8
9
10
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDCANCEL: {
ChangeClipboardChain(hwndDlg, g_hwndNextViewer);
EndDialog(hwndDlg, IDCANCEL);
break;
};
};
return TRUE;
};

当程序调用时当前剪贴板查看器收到WM_CHANGECBCHAIN消息,wParam为要从链中退出的窗口句柄,lParam是将要退出的窗口的下一个剪贴板查看器窗口句柄。每个剪贴板查看器应为了维护剪贴板查看器链,应判断要退出的程序wParam是不是自己的下一个,如果是则自己的下一个剪贴板查看器应改为lParam:

1
2
3
4
5
6
7
case WM_CHANGECBCHAINE: {
if ((HWND)wParam == g_hwndNextViewer)
g_hwndNextViewer = (HWND)lParam;
else if (g_hwndNextViewer)
SendMessage(g_hwndNextViewer, uMsg, wParam, lParam);
break;
};

例子

这里用到了RichEdit20富文本空间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <Windows.h>
#include <Richedit.h>
#include "resource.h"
#ifdef UNICODE
#define CF_TCHAR CF_UNICODETEXT
#else
#define CF_TCHAR CF_TEXT
#endif
// 全局变量
HWND g_hwndNextViewer; // 剪贴板查看器链中下一个剪贴板查看器的窗口句柄
// 函数声明
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 加载Riched20.dll动态链接库
LoadLibrary(TEXT("Riched20.dll"));
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
return 0;
};
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static HWND hwndEdit;
LONG lStyle;
LPTSTR lpStr;
HBITMAP hBmp;
UINT uFormat;
TCHAR szSeparator[] = TEXT("\n--------------------------------------------------------\n");
switch (uMsg) {
case WM_INITDIALOG: {
// 显示图片的静态控件设置SS_BITMAP | SS_REALSIZECONTROL风格
lStyle = GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_STATIC_BITMAP), GWL_STYLE);
SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_STATIC_BITMAP), GWL_STYLE, lStyle | SS_BITMAP | SS_REALSIZECONTROL);
// 将当前程序窗口添加到剪贴板查看器的链中
g_hwndNextViewer = SetClipboardViewer(hwndDlg);
hwndEdit = GetDlgItem(hwndDlg, IDC_RICHEDIT);
return TRUE;
};
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDCANCEL: {
// 从剪贴板查看器链中删除自身
ChangeClipboardChain(hwndDlg, g_hwndNextViewer);
EndDialog(hwndDlg, IDCANCEL);
break;
};
};
return TRUE;
};
case WM_DRAWCLIPBOARD: {
// 剪贴板的内容发生变化
// 如果剪贴板查看器链中存在下一个窗口
if (g_hwndNextViewer)
SendMessage(g_hwndNextViewer, uMsg, wParam, lParam);
// 更新显示
OpenClipboard(hwndDlg);
uFormat = EnumClipboardFormats(0);
while (uFormat) {
// 这里只处理这2种格式
if (uFormat == CF_TCHAR) {
lpStr = (LPTSTR)GetClipboardData(CF_TCHAR);
SendMessage(hwndEdit, EM_SETSEL, -1, -1);
SendMessage(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)lpStr);
SendMessage(hwndEdit, EM_SETSEL, -1, -1);
SendMessage(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)szSeparator);
}
else if (uFormat == CF_BITMAP) {
hBmp = (HBITMAP)GetClipboardData(CF_BITMAP);
SendDlgItemMessage(hwndDlg, IDC_STATIC_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp);
};
uFormat = EnumClipboardFormats(uFormat);
};
CloseClipboard();
return TRUE;
};
case WM_CHANGECBCHAIN: {
// 处理WM_CHANGECBCHAIN消息,维护好剪贴板查看器链
if ((HWND)wParam == g_hwndNextViewer)
g_hwndNextViewer = (HWND)lParam;
else if (g_hwndNextViewer)
SendMessage(g_hwndNextViewer, uMsg, wParam, lParam);
return TRUE;
};
};
return FALSE;
};

剪贴板数据格式监听器

因为有老六剪贴板查看器不履行义务,导致整个剪贴板查看器链崩溃,这里提出了新的解决方案。

AddClipboardFormatListener

注册程序成为剪贴板数据格式监听器,当剪贴板内容变化时窗口收到WM_CLIPBOARDUPDATE消息,wParam和lParam都为0:

1
2
3
BOOL AddClipboardFormatListener(
HWND hwnd
);

RemoveClipboardFormatListener

移除剪贴板数据格式监听器:

1
2
3
BOOL RemoveClipboardFormatListener(
HWND hwnd
);

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <Windows.h>
#include <Richedit.h>
#include "resource.h"
#ifdef UNICODE
#define CF_TCHAR CF_UNICODETEXT
#else
#define CF_TCHAR CF_TEXT
#endif
// 函数声明
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 加载Riched20.dll动态链接库
LoadLibrary(TEXT("Riched20.dll"));
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
return 0;
};
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static HWND hwndEdit;
LONG lStyle;
LPTSTR lpStr;
HBITMAP hBmp;
UINT uFormat;
TCHAR szSeparator[] = TEXT("\n--------------------------------------------------------\n");
switch (uMsg) {
case WM_INITDIALOG: {
// 显示图片的静态控件设置SS_BITMAP | SS_REALSIZECONTROL风格
lStyle = GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_STATIC_BITMAP), GWL_STYLE);
SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_STATIC_BITMAP), GWL_STYLE, lStyle | SS_BITMAP | SS_REALSIZECONTROL);
// 注册成为剪贴板格式监听器
if (!AddClipboardFormatListener(hwndDlg))
MessageBox(hwndDlg, TEXT("注册成为剪贴板格式监听器失败"), TEXT("Error"), MB_OK);
hwndEdit = GetDlgItem(hwndDlg, IDC_RICHEDIT);
return TRUE;
};
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDCANCEL: {
// 移除剪贴板格式监听器
RemoveClipboardFormatListener(hwndDlg);
EndDialog(hwndDlg, IDCANCEL);
break;
};
};
return TRUE;
};
case WM_CLIPBOARDUPDATE: {
// 更新显示
OpenClipboard(hwndDlg);
uFormat = EnumClipboardFormats(0);
while (uFormat) {
// 这里只处理这2种格式
if (uFormat == CF_TCHAR) {
lpStr = (LPTSTR)GetClipboardData(CF_TCHAR);
SendMessage(hwndEdit, EM_SETSEL, -1, -1);
SendMessage(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)lpStr);
SendMessage(hwndEdit, EM_SETSEL, -1, -1);
SendMessage(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)szSeparator);
}
else if (uFormat == CF_BITMAP) {
hBmp = (HBITMAP)GetClipboardData(CF_BITMAP);
SendDlgItemMessage(hwndDlg, IDC_STATIC_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp);
};
uFormat = EnumClipboardFormats(uFormat);
};
CloseClipboard();
return TRUE;
};
};
return FALSE;
};