剪切板是系统维护管理的一块内存区域,本机的所有进程都可以访问。当一个进程复制数据时,先将数据放在该内存区,当另一个进程粘贴时,则是从该内存区块取出数据
剪切板操作:
其实在剪切板中也就那几个API在使用,下面会介绍几个常用的API,然后会给出一个demo示例
剪切板打开—OpenClipboard
BOOL OpenClipboard(
[in, optional] HWND hWndNewOwner
);
OpenClipboard函数用来打开剪切板(放数据前的笔要操作)。参数hWndNewOwner指向一个窗口句柄,即代表是这个窗口打开的剪切板,如果这个参数设置为NULL,则以当前任务来打开剪切板 。如果有另一个窗口打开了剪切板,则函数失败,返回零。
清空剪切板—EmptyClipboard
BOOL EmptyClipboard();
这个函数将清空剪贴板,并释放剪贴板中数据的句柄,然后将剪贴板的所有权分配给当前打开剪贴板的窗口。
因为剪贴板是所有进程都可以访问的,所以应用程序在使用这个剪贴板时,有可能已经有其他的应用程序把数据放置到了剪贴板上,因此该进程打开剪贴板之后,就需要调用 EmptyClipboard 函数来清空剪贴板,释放剪贴板中存放的数据的句柄,并将剪贴板的所有权分配给当前的进程,这样做之后当前打开这个剪贴板的程序就拥有了剪贴板的所有权,因此这个程序就可以往剪贴板上放置数据了。
数据发送到剪切板—SetClipboardData
HANDLE SetClipboardData(
[in] UINT uFormat,
[in, optional] HANDLE hMem
);
- 参数uFormat用来指定发送剪切板上的数据的格式
常见的有CF_BTMAP(bitmap类型),CF_TEXT(text类型),CT_DIB等
- 参数hMen表示一个指定格式的数据句柄。
如果hMen参数标识的是内存对象,则必须带有GME_MOVEABLE属性的函数分配对象。下面的GlobalAlloc函数可以创建这种标识的内存对象句柄。
应用程序在调用了 SetClipboardData 函数之后,系统就拥有了 hMem 参数所标识的数据对象,该应用程序可以读取这个数据对象,但是在应用程序调用 CloseClipboard 函数之前,它都是不能释放该对象的句柄的,或者锁定这个句柄,如果 hMem 标识一个内存对象,那么这个对象必须是利用 GMEM_MOVEABLE 标识调用 GlobalAlloc 函数为其分配内存的。
数据句柄—GlobalAlloc
DECLSPEC_ALLOCATOR HGLOBAL GlobalAlloc(
[in] UINT uFlags,
[in] SIZE_T dwBytes
);
GlobalAlloc函数从堆上分配指定数目的字节
这里有读者可能会问:为什么我们在自己的应用程序中不使用 GlobalAlloc 函数来分配内存,而是要使用 malloc 或者 new 来实现?
其实,这个也只用稍微想想就知道了,使用 malloc 或者 new 分配的内存是在进程的私有地址空间上分配的,这块私有地址空间归这个进程所拥有,在之后对这块内存的读写会快很多,而全局内存不属于这个进程,你下次要去访问全局内存的时候,还得通过映射转换,这样肯定是运行效率低。
- 第一个参数表示内存分配的属性,例如上面要求的GME_MOVEABLE表示分配可移动内存 。
- 第二个参数表示分配的字节数。
函数执行成功,返回新分配内存对象的句柄,否则返回NULL。
锁定全局内存对象—GlobalLock
LPVOID GlobalLock(
[in] HGLOBAL hMem
);
锁定全局内存对象并返回指向该对象内存块的第一个字节的指针。我们可以通过这个指针对这块内存数据存取,这也保证了其他进程不会对这块内存的数据修改。
- 参数hMem表示全局内存对象的句柄。此句柄由 GlobalAlloc 或GlobalReAlloc函数返回。
每个内存对象的内部数据结构包括最初为零的锁计数。对于可移动内存对象(GME_MOVEABLE),全局锁定将计数递增 1,全局解锁函数将计数递减 1。锁定内存对象的内存块将保持锁定状态,直到其锁定计数减少到零,此时可以移动或丢弃它。
全局内存对象解锁—GlobalUnLock
BOOL GlobalUnlock(
[in] HGLOBAL hMem
);
GlobalUnlock函数递减与分配了GMEM_MOVEABLE的内存对象关联的锁定计数。
- 参数hMem表示全局内存对象的句柄。此句柄由 GlobalAlloc 或GlobalReAlloc函数返回。
若函数执行完后内存对象仍处于锁定状态,则函数返回非零值,如果减少计数后解锁内存对象,则函数返回零(GetLasrError返回NO_ERROR),如果函数失败,则返回零(GetLastError返回NO_ERROR以外的值)
剪切板中的数据格式判断—IsClipboardFormatAvaliable
BOOL IsClipboardFormatAvailable(
[in] UINT format
);
该函数将确定剪贴板是否包含指定格式的数据。如果剪贴板格式可用,则返回值为非零值。否则返回零。
- 参数format指明需要判断的格式
剪贴板中数据接收—GetClipboardData
HANDLE GetClipboardData(
[in] UINT uFormat
);
GetClipboardData以指定格式从剪贴板检索数据。剪贴板之前必须已打开。函数成功,返回指定格式剪切板对象的句柄,失败返回NULL。
Demo示例
创建一个MFC项目
当接收按钮按下后,会打开剪切板,将上方编辑框里的内容放到剪切板内
当接收按钮按下后,则会将剪切板中的内容粘贴到上方编辑框中。
接收按钮程序:
void CClipDlg::OnBnClickedSendBtn()
{
//打开剪切板
if (OpenClipboard()) {
//清空剪切板
EmptyClipboard();
char* szSendBuf;//要发送的数据
//获取编辑框的内容
CStringW strSendW;
GetDlgItemText(IDC_SEND_EDIT, strSendW);
CStringA strSend = (CStringA)strSendW;
//分配一个内存对象,内存对象的句柄就是hClip
HANDLE hClip = GlobalAlloc(GMEM_MOVEABLE, strSend.GetLength() + 1);
//句柄加锁
szSendBuf = (char*)GlobalLock(hClip);
//将指定字符串复制到目标字符串,若目标字符串大小小于指定字符串,则会溢出
//第一个参数为目标字符串,第二个参数为指定字符串
strcpy(szSendBuf, strSend);
//TRACE("seSendBuf =%s", szSendBuf);
//解锁
GlobalUnlock(hClip);
//将数据放在剪切板
SetClipboardData(CF_TEXT, hClip);
//关闭剪切板
CloseClipboard();
}
}
发送按钮程序:
void CClipDlg::OnBnClickedButton1()
{
if (OpenClipboard()) {
//先确认剪切板是否可用
if (IsClipboardFormatAvailable(CF_TEXT)) {
HANDLE hClip;
char* pBuf;
//向剪切板要数据
hClip = GetClipboardData(CF_TEXT);
pBuf = (char*)GlobalLock(hClip);
USES_CONVERSION;
LPCWSTR strBuf = A2W(pBuf);
GlobalUnlock(hClip);
//显示
SetDlgItemText(IDC_RECV_EDIT, strBuf);
}
CloseClipboard();
}
}
运行: