进程的通信 - 剪切板

news/2024/6/29 11:48:28 标签: microsoft, mfc, c++, 通信

剪切板是系统维护管理的一块内存区域,本机的所有进程都可以访问。当一个进程复制数据时,先将数据放在该内存区,当另一个进程粘贴时,则是从该内存区块取出数据

剪切板操作:

其实在剪切板中也就那几个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();
	}
}

运行:


http://www.niftyadmin.cn/n/1662848.html

相关文章

Virtual Server 2005 下加快在 SCSI 磁盘上安装操作系统的速度

虚拟服务器现在包含名为 SCSI Shunt Driver.vfd 的虚拟软盘文件,该文件包含由虚拟机仿真的适用于 Windows Server 2003、Windows 2000 Server 和 Windows XP Professional 的各个 SCSI 驱动程序。当您在附加到仿真 SCSI 适配器的虚拟硬盘上安装其中的一个操作系统时…

【Vue.js学习笔记】13:属性传值(父传子),事件传值(子传父)

属性传值(父传子) 简述 属性传值即父组件向子组件传值。有些属性可能有多个子组件要使用,这种时候总不能每个组件里都写一遍,这时候就可以把这样的属性放到父组件里,然后通过属性传值的方式传递给要使用它的子组件里去使用。 这个例子是在…

权限管理数据表设计说明

权限管理数据表设计说明 B/S系统中的权限比C/S中的更显的重要,C/S系统因为具有特殊的客户端,所以访问用户的权限检测可以通过客户端实现或通过客户端服务器检测实现,而B/S中,浏览器是每一台计算机都已具备的,如果不建立…

【Vue.js学习笔记】14:组件生命周期钩子函数,vue-router路由

组件生命周期钩子函数 即组件生命周期过程中调用的函数。组件的生命周期可以参考官方文档中的图&#xff0c;这里在前面的例子上修改Header组件&#xff0c;在生命周期的各个方法中弹出对话框演示一下。 Header.vue <template><header v-on:click"updateTitle…

进程的通信 - 邮槽

邮槽 邮槽是Windows系统提供的一种单向进程间的通信机制。对于相对简短的地坪率信息发送&#xff0c;使用邮槽通常比命名管道或者Unix域套接字更简单 使用邮槽通信的进程分为服务端和客户端。邮槽由服务端创建&#xff0c;在创建时需要指定邮槽名&#xff0c;创建后服务端得…

写给MongoDB开发者的50条建议Tip24

本系列文章翻译自《50 Tips and Tricks for MongoDB Developers》&#xff0c;暂时没有找到中文版&#xff0c;反正自己最近也在深入学习mongodb&#xff0c;所以正好拿来翻译一下。一方面加强自己学习的效果&#xff0c;另一方面让大 家也一起来体验一下需要我们这些mongodb使…

Python 字典和集合的的实现:散列表

字典和集合的效率高&#xff0c;和他背后的散列表是绕不开的。 散列表其实是一个稀疏数组&#xff08;总是有空白元素的数组称为稀疏数组&#xff09;。散列表的单元叫做表元bucket。在dict的散列表中&#xff0c;每个键值对都占用一个表元&#xff0c;每个表元都有两个部分&a…

进程的通信 - 无(匿)名管道

匿名管道概述 匿名管道是一个没有名称的管道&#xff0c;本质上是一块共享的内存区域。匿名管道可以实现在本机上的父子进程之间的通信。这里需要注意两点&#xff0c;第一就是在本地机器上&#xff0c;这是因为匿名管道不支持跨网络之间的两个进程之间的通信&#xff0c;第二就…