VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585       在实现IM即时通讯软件聊天框中的图片文字等内容的混合复制(复制的内容既包括文字,也包括图片)与粘贴功能时,为了方便用户操作,要支持与QQ、微信、企业微信、邮箱等常用软件之间互通的。即从我们的IM软件中混合复制的内容粘贴到QQ等软件的聊天框中时要能正确显示出来内容,从QQ等其他软件中复制的内容粘贴到我们的IM软件中能正常显示。

要实现上述对通功能,就需要了解多种格式的剪切板数据格式,粘贴内容时就是要从剪切板中获取指定剪切板格式数据内容,将剪切板格式的数据解析出来,然后显示到目标窗口中。这就要求我们在复制内容时构造多种剪切板格式数据,也要在粘贴内容时能解析多种剪切板格式数据。

1、剪切板数据格式

能否找到一种多种软件都支持的、比较通用的剪切板格式呢?确实是有的,HTML_FORMAT剪切板数据格式就是一种比较通用的,基本所有的IM软件在构建剪切板数据时都会构建HTML_FORMAT格式数据的,所以我们在复制时构造HTML_FORMAT格式数据,粘贴时解析HTML_FORMAT格式的数据即可。经过之前的测试,要将图片文字的混合内容复制到QQ中,QQ能识别出来并正常显示,必须要在复制内容时构建QQ_Unicode_RichEdit_Format剪切板格式数据,否则QQ不支持(这点没办法,QQ是主流IM即时通讯软件,我们必须向QQ“靠拢”,主动提供对QQ的对通支持)。下面我们主要讲HTML_FORMAT和QQ_Unicode_RichEdit_Format两种剪切板数据格式。

Widnows系统预先定义了一些通用的剪切板格式,比如CF_TEXT(文字)、CF_UNICODETEXT(Unicode文字)、CF_BITMAP(位图)等。对于HTML_FORMAT、QQ_Unicode_RichEdit_Format等这些系统未定义的剪切板格式,则需要调用API函数RegisterClipboardFormat向系统注册对听的格式,注册时将使用HTML_FORMAT、QQ_Unicode_RichEdit_Format名字去注册,RegisterClipboardFormat函数会返回对应剪切板格式id(DWORD整型)。不管是哪个软件注册的,只要注册时指定的名称相同,获取的剪切板数据格式id都是相等的。

如果是获取对应格式的剪切板数据,则需要调用GetClipboardData函数,传入对应的剪切板格式id。如果是要向剪切板中设置剪切板数据格式,则要调用SetClipboardData,传入对应的剪切板格式id。

2、使用Clipbrd工具查看剪切板中都有哪些剪切板数据格式

首先我们来看如何查看到IM软件在复制内容都支持那些剪切板数据格式呢?XP系统中提供了一个叫做clipbrd.exe的剪切板查看工具。比如我们在QQ中复制了如下图的图片和文字的混合内容:

复制后,可以打开clipbrd,点击clipbrd工具栏的“查看”,即可看到QQ在复制内容时都构建了那些剪切板格式的数据,如下所示:

除了常用的CF_TEXT(文本)等格式外,我们看到了上面我们讲到的HTML_FORMAT和QQ_Unicode_RichEdit_Format两种自定义的剪切板数据格式。

这个clipbrd工具在win7及以上系统已经不自带这个工具了,如果需要这个工具可以到网上下载,可以直接拿到Win7及以上系统中运行。

3、通过测试代码查看指定剪切板数据的构成与格式

在构造指定的剪切板格式之前,我们需要到剪切板中获取该种剪切板格式的数据内容,查看这些数据的构建内容与格式。以获取QQ_Unicode_RichEdit_Format剪切板格式数据为例,获取该种格式数据的测试代码如下所示:

HANDLE hClip = NULL;
char *pBuf = NULL;
WCHAR* pwchBuf = NULL;
char* pTemp = NULL;
m_dwClipFormatQQ = ::RegisterClipboardFormat( _T("QQ_Unicode_RichEdit_Format") );BOOL bRet = ::OpenClipboard( NULL );
if ( !bRet )
{return;
}hClip = GetClipboardData( m_dwClipFormatQQ );
if ( NULL == hClip )
{CloseClipboard();return;
}pBuf = (char*)GlobalLock( hClip );
if ( NULL == pBuf )
{GlobalUnlock( hClip );CloseClipboard();return;
}int nNum = MultiByteToWideChar( CP_UTF8, 0, pBuf, -1, NULL, 0 );
pwchBuf = new WCHAR[nNum];
MultiByteToWideChar( CP_UTF8, 0, pBuf, -1, pwchBuf, nNum );CString strContent;
CopyUtf8ToCStringT( strContent, pBuf );
m_editContent.SetWindowText( strContent );GlobalUnlock( hClip );

因为QQ_Unicode_RichEdit_Format剪切板格式数据,在设置到剪切板之前,是以utf-8编码的字符串,所以在调用GetClipboardData获取到字符串数据之后,需要将utf-8编码的字符串转成Unicode编码,才能查看到字符串中的文字。如果直接查看的utf-8编码字符串中有中文,utf-8编码的中文会显示乱码,所以要字符串转成Unicode编码的。

通过上述测试代码,可以查得到在执行数据复制时构造的HTML_FORMAT格式的数据

Version:0.9
StartHTML:00000112
EndHTML:00000461
StartFragment:00000126
EndFragment:00000425
<!doctype html><html><body>
<!--StartFragment --><DIV>
<IMG src="file:///C:\Users\Administrator\AppData\Roaming\Tencent\Users\710027094\QQ\WinTemp\RichOle\LVF%BQDXAUX@0MP2{(%$LLK.png" >123<IMG src="file:///C:\Users\Administrator\AppData\Roaming\Tencent\Users\710027094\QQ\WinTemp\RichOle\V8E90K]C]N%1G]8_Q(B48}8.png" >
</DIV><!--EndFragment-->
</body>
</html>

也可以得到如下的QQ_Unicode_RichEdit_Format格式数据

<QQRichEditFormat>
<Info version="1001">
</Info>
<EditElement type="1" filepath="C:\Users\Administrator\AppData\Roaming\Tencent\Users\7100****\QQ\WinTemp\RichOle\MJR{P}JK{HS{077`IF_@$Y3.png" shortcut="">
</EditElement>
<EditElement type="0"><![CDATA[123]]>
</EditElement>
<EditElement type="1" filepath="C:\Users\Administrator\AppData\Roaming\Tencent\Users\7100****\QQ\WinTemp\RichOle\L2_QM_EY}G3)R`76P9HYLPJ.png" shortcut="">
</EditElement>
</QQRichEditFormat>

4、通过代码去构造HTML_FORMAT和QQ_Unicode_RichEdit_Format格式的剪切板数据

我们可以参考上面获取到的HTML_FORMAT和QQ_Unicode_RichEdit_Format格式的剪切板数据的构成与格式,轻松地写出构造这两种剪切板格式的数据。

构造HTML_FORMAT剪切板格式数据的代码如下:

CString strHtml = _T("");
strHtml.Format( _T("Version:0.9\r\nStartHTML:%08d\r\nEndHTML:%08d\r\nStartFragment:%08d\r\nEndFragment:%08d\r\n"), HTML_BEGIN, nHtmlEnd, FRAGMENT_BEGIN, nFragmentEnd );
strHtml += _T("<truelink html><html><body>\r\n<!--StartFragment --><DIV>\r\n");
strHtml += pBuff; // 包含图片路径和文字的字符串已经事先准备好
strHtml += _T("\r\n</DIV><!--EndFragment-->\r\n</body>\r\n</html>");int nResLen = WideCharToMultiByte( CP_UTF8, 0, strHtml, -1, 0, 0, 0, 0 );
char* pHtmlChar = (char *)VirtualAlloc( 0, nResLen, MEM_COMMIT, PAGE_READWRITE );
memset( pHtmlChar, 0, nResLen );
WideCharToMultiByte( CP_UTF8, 0, strHtml, -1, pHtmlChar, nResLen, NULL, NULL );

构造QQ_Unicode_RichEdit_Format剪切板格式数据的代码如下:

wstring ConstructQQFormatData( WCHAR *pDst, WCHAR *pSrc, std::vector<REOBJECT>& objectList )
{if ( m_pRichEdit == NULL ){return L"";}if ( pSrc == NULL && pDst == NULL ){return L"";}if ( objectList.size() == 0 ){return L"";}// 得到选择的区域long lBeginPos;long lEndPos;m_pRichEdit->GetSel( lBeginPos, lEndPos );// 此处保证不修改传入的指针变量WCHAR* pTempSrc = pSrc;WCHAR* pTempDst = pDst;wstring strRes = L"<QQRichEditFormat><Info version=\"1001\" ></Info>";//CStringW strTemp;long lPos = lBeginPos;long lCount = 0;for ( int j = 0; j < (int)objectList.size(); j++ ){  lCount = objectList[j].cp - lPos;if ( lCount > 0 ){ParseReturnForQQ( &pTempDst, &pTempSrc, lCount, TRUE );//strTemp.Format( L"<EditElement type=\"0\"><![CDATA[%s]]></EditElement>", pTempDst );//strRes += strTemp;strRes += L"<EditElement type=\"0\"><![CDATA[";strRes += pTempDst;strRes += L"]]></EditElement>";}lPos = objectList[j].cp + 1;if ( objectList[j].clsid == CLSID_GifAnimator ){IGifAnimator *pGifAnimator = NULL;LRESULT hr = objectList[j].poleobj->QueryInterface( __uuidof(IGifAnimator), (void**)&pGifAnimator );if ( SUCCEEDED( hr ) ){BSTR path = NULL;pGifAnimator->GetFilePath( &path );pGifAnimator->Release();CUIString strPicPath = (LPCTSTR)(_bstr_t)path;//strTemp.Format( L"<EditElement type=\"1\" filepath=\"%s\"></EditElement>", path );//strRes += strTemp;strRes += L"<EditElement type=\"1\" filepath=\"";strRes += path;strRes += L"\"></EditElement>";if ( path != NULL ) {::SysFreeString( path );}}}else if ( objectList[j].clsid == CLSID_linkole ){Ilinkole *pLinkOle = NULL;LRESULT hr = objectList[j].poleobj->QueryInterface( __uuidof(Ilinkole), (void**)&pLinkOle );if ( SUCCEEDED(hr) ){BSTR name = NULL;pLinkOle->GetName( &name );pLinkOle->Release();//strTemp.Format( L"<EditElement type=\"0\"><![CDATA[%s]]></EditElement>", name );//strRes += strTemp;strRes += L"<EditElement type=\"0\"><![CDATA[";strRes += name;strRes += L"]]></EditElement>";if ( name != NULL ) {::SysFreeString( name );}}}pTempSrc++;}lCount = lEndPos - lPos;if ( lCount > 0 ){ParseReturnForQQ( &pTempDst, &pTempSrc, lCount, FALSE );//strTemp.Format( L"<EditElement type=\"0\"><![CDATA[%s]]></EditElement>", pTempDst );//strRes += strTemp;strRes += L"<EditElement type=\"0\"><![CDATA[";strRes += pTempDst;strRes += L"]]></EditElement>";}strRes += L"</QQRichEditFormat>";return strRes;
}

上述代码经过大量的测试和验证,其稳定性和可靠性绝对是有保证的,大家可以放心使用。

VC++ IM即时通讯软件中的剪切板数据对通的实现细节(附源码)相关推荐

  1. 【Android App】人脸识别中使用Opencv比较两张人脸相似程度实战(附源码和演示 超详细)

    需要全部代码请点赞关注收藏后评论区留言私信~~~ 一.比较两张人脸的相似程度 直方图由一排纵向的竖条或者竖线组成,横轴代表数据类型,纵轴代表数据多少. 图像直方图经常应用于特征提取.图像匹配等方面. ...

  2. Android App开发手机阅读中PDF文件渲染器的讲解及使用(附源码 简单易懂)

    需要源码和图片集请点赞关注收藏后评论区留言~~~ 一.PDF文件渲染器 Android集成了PDF的渲染操作,从很大程度上方便了开发者,这个PDF文件渲染器便是PdfRenderer.渲染器允许从存储 ...

  3. VC++分别使用WinExec、CreateProcess、ShellExecute和ShellExecuteEx来启动程序(附源码)

    VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585       有时, ...

  4. android中设置ListView的选中的Item的背景颜色(附源码)

    http://longyi-java.iteye.com/blog/976067 ListView中没有默认的选择颜色,只有选择Item后的焦点颜色,鼠标点击时Item有颜色,放开鼠标后颜色也就没有了 ...

  5. C++中的开闭原则使用C++多态功能实现附源码

    开闭原则 使用开闭原则之前一个银行柜员会将所有的工作都操作一遍,这样会大大降低办事的效率,而且银行的业务不适合扩展,没扩展一项业务就要培训所有的业务员.改为使用开闭原则之后,每个银行业务员只负责一项业 ...

  6. vscode中安装webpack_leaflet-webpack 入门开发系列一初探篇(附源码下载)

    前言 leaflet-webpack 入门开发系列环境知识点了解: node 安装包下载 webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载地址w ...

  7. ios开发读取剪切板的内容_iOS中管理剪切板的UIPasteboard粘贴板类用法详解

    一.自带剪切板操作的原生UI控件在iOS的UI系统中,有3个控件自带剪切板操作,分别是UITextField.UITextView与UIWebView.在这些控件的文字交互处进行长按手势可以在屏幕视图 ...

  8. 每次打开百度太麻烦?用程序直接打开网页 Python实现百度划词搜索功能(获取剪切板数据)

    浏览顺序 实现划词功能 运行错误 解决问题 转载声明 实现划词功能 说是划词翻译,实际上我们是通过获取用户的剪切板内容,通过一系列的操作得到的.首先呢,我们就先实现如何获取剪切板内容的程序 首先先在桌 ...

  9. python清空windows系统剪切板数据

    本文介绍python清空windows系统剪切板数据方法 一.实现原理 通过python ctypes模块,调用windows API实现 二.实现代码 from ctypes import wind ...

最新文章

  1. android多行文本框hint居中,在安卓等移动浏览器中placeholder中的文字不垂直居中问题...
  2. Python 过滤字母和数字
  3. flink写入clickhouse遇到210错误故障排查思路
  4. 前端学习(3251):样式的模块化
  5. python开发人员看什么书_python初学者看什么书
  6. [旧文新读] 深度学习在Airbnb搜索的应用实践
  7. 使用 JavaScript 将 JSON 数据动态转换为 HTML 表
  8. element-ui 执行 npm run build:theme 报错 Replace Autoprefixer browsers option to Browserslist config....
  9. 基于Myrocks和spider构建分布式数据库
  10. 架设Wikipedia的本地镜像(Linux联盟收集整理)
  11. 产品生命周期用户生命周期?
  12. 结对项目-小游戏连连看
  13. 电路元器件3——TVS:
  14. 神策数据携手老虎证券,用科技赋能美港股券商打造极致体验
  15. 【计算机视觉】图像分割与特征提取——基于Roberts、Prewitt、Sobel算子的图像分割实验
  16. esriControlsMousePointer控制鼠标指针选项
  17. dbcc dbreindex server sql_SQL Server2005索引碎片分析和解决方法
  18. 从三驾马车到三叉戟,亚马逊云科技的中国故事
  19. python读取jpg文件_python如何读jpg文件
  20. 华南师范大学计算机学院重修,华南师范大学计算机学院、软件学院本科生学年评优工作条例...

热门文章

  1. .NET程序中加入Autodesk Design Review 2013 ,打包完成后安装提示未注册XXX.dll解决办法
  2. android 11中置入第三方应用apk
  3. 4-2毕达哥拉斯三元组
  4. SpringCloud( H版 alibaba )框架开发教程(中级)
  5. 【随笔】野生在左 科班在右——数据结构学习誓师贴
  6. 好玩的黑科技app:推荐5款功能逆天的黑科技应用!
  7. 程序员领养群破解笔记
  8. 英语思维导图大全 虚拟语气(十三)
  9. 救命钱变服务费,做大病众筹的水滴筹到底靠什么赚钱?
  10. 北京买房的10点建议