实战经验:CRichEditCtrl插入图片(不止是位图)
问题
在一次功能开发中,我希望可以在CRichEditCtrl控件中插入一些图片,这些图片可能是位图(bmp),也可能是其他格式(jpg, png等)。
以下是一种思路
1) 图片文件可以通过GDI+中的Bitmap类来加载。
2) 如果要插入图片到CRichEditCtrl,则需要涉及到OLE对象的插入。可以这样理解,每一个图片文件都是一个OLE对象,这些OLE对象通过CRichEditCtrl类暴露的IRichEditOle接口的InsertObject方法来进行插入操作(这句话有点长啊,请不要着急,慢慢理解)。
3) OLE的世界中,有一个涉及OLE对象存储介质的类:STGMEDIUM。这个类中,有一个数据成员hBitmap,其类型为HBITMAP,也即位图句柄。我们可以通过将GDI+的Bitmap对象转换为HBITMAP来将我们加载的图片文件传递给STGMEDIUM。
4) 除了以上的步骤,还有以下常规的OLE函数调用,需要引入。
CreateILockBytesOnHGlobal
StgCreateDocfileOnILockBytes
OleCreateStaticFromData
GetUserClassID
执行图片加载
以下代码演示了如何使用GDI+的Bitmap来加载图片。
Gdiplus::Bitmap * pBmp = Gdiplus::Bitmap::FromFile(pszFilePath); HBITMAP hBmp = NULL; pBmp->GetHBITMAP(0, &hBmp);
代码解析
1) 通过Gdiplus::Bitmap的FromFile方法来加载图片,图片的文件可以是位图,也可以是jpg等其他格式。
2) 加载完成后,会返回一个Gdiplus::Bitmap对象指针。
3) 通过Gdiplus::Bitmap对象的GitHBITMAP方法获取对应的位图句柄。
4) 此获取到的位图句柄,在以下插入图片的代码中会被使用到。
插入图片到CRichEdit
以下代码演示了如果将一个位图句柄插入到CRichEditCtrl中。
void CMyRichEditCtrl::InsertPicture(HBITMAP hBmp)
{
STGMEDIUM stgm;
stgm.tymed = TYMED_GDI;
stgm.hBitmap = hBmp;
stgm.pUnkForRelease = NULL;
FORMATETC fm;
fm.cfFormat = CF_BITMAP;
fm.ptd = NULL;
fm.dwAspect = DVASPECT_CONTENT;
fm.lindex = -1;
fm.tymed = TYMED_GDI;
COleDataSource oleDataSource;
oleDataSource.CacheData(CF_BITMAP, &stgm);
LPDATAOBJECT dataObject = (LPDATAOBJECT)oleDataSource.GetInterface(&IID_IDataObject);
if (OleQueryCreateFromData(dataObject) != OLE_S_STATIC)
return;
LPOLECLIENTSITE oleClientSite;
if (S_OK != GetIRichEditOle()->GetClientSite(&oleClientSite))
return;
// 内存分配
LPLOCKBYTES lockBytes = NULL;
if (S_OK == CreateILockBytesOnHGlobal(NULL, TRUE, &lockBytes) && lockBytes)
{
IStorage *storage = NULL;
if (S_OK == StgCreateDocfileOnILockBytes(lockBytes, STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_READWRITE, 0, &storage) && storage)
{
IOleObject *oleObject = NULL;
if (S_OK == OleCreateStaticFromData(dataObject, IID_IOleObject, OLERENDER_FORMAT, &fm, oleClientSite, storage, (void **)&oleObject) && oleObject)
{
CLSID clsid;
if (S_OK == oleObject->GetUserClassID(&clsid))
{
REOBJECT reobject = { sizeof(REOBJECT) };
reobject.clsid = clsid;
reobject.cp = REO_CP_SELECTION;
reobject.dvaspect = DVASPECT_CONTENT;
reobject.poleobj = oleObject;
reobject.polesite = oleClientSite;
reobject.pstg = storage;
// 插入OLE对象
GetIRichEditOle()->InsertObject(&reobject);
}
oleObject->Release();
}
storage->Release();
}
lockBytes->Release();
}
oleClientSite->Release();
}
代码解析
1) 创建一个STGMEDIUM对象,将上一个步骤中得到的位图句柄赋值给它的hBitmap成员,另外需要设定其tymed为TYMED_GDI。
2) 创建一个FORMATETC对象,初始化其各个成员,具体可以参考以上代码片段。
3) 创建一个COleDataSource,并调用其CacheData方法,将数据从STGMEDIUM对象中缓存至COleDataSource中。
4) 内存分配部分为常规调用序列,需要注意的是,在最终调用InsertObject时,需要先准备一个reobject对象,这个对象的各个接口指针需要确保赋值正确。例如poleobj,polesite和pstg。
5) 通过GetIRichEditOle获取CRichEditCtrl的IRichEditOle接口,并调用InsertObject实现OLE对象(也就是这里的图片文件)的插入。
6) 所有工作完成之后,记得调用Release释放各个接口指针,以防止出现资源泄漏。
总结
1) CRichEditCtrl,所谓的富文本编辑器,从功能上说,的确是很丰富,如果想体验它强大的功能,可以打开一个写字板(wordpad),因为写字板就是基于CRichEditCtrl控件实现的。
2) 插入图片的需求是如此普遍,以至于我忍不住在这里记录这部分代码,以方便需要使用它的人。
3) 以上代码只是对基本原理进行了介绍,并未添加完整的错误处理逻辑。
相关推荐
- 代码片段:Crypto++使用方法汇总
- Posted on 06月26日
- 第 88 期:Windows 10 技术支持即将终止,微软建议用户升级至 Windows 11
- Posted on 06月23日
- 我们需要先弄明白头文件的默认版本
- Posted on 02月25日
- 第 200 期:微软 Edge 浏览器新增实时音频翻译功能
- Posted on 09月14日



评论已关闭。