MFC富文本编辑框richedit插入图片ole对象问题

MFC富文本编辑框richedit插入图片ole对象问题

学习了三四天的一点成果,如果有不对的地方还请指出。

目前发现有两种方法来向richedit中插入ole对象:

1.直接粘贴,然后使用回调函数来控制即将插入的ole对象。
2.自己构造一个ole对象然后调用richedit的方法来插入。

MFC应该对richedit插入ole对象做了封装,只留了一个接口设置回调函数,然后在回调函数中对即将插入的ole对象进行处理。这种方法的广度还是很大的,可以直接获得ole对象中的所有数据,然后想怎么改就怎么改。但是问题是,这方面的文档实在是有点少,找来找去只找到了一个能用的例子,而且是简单的返回,没有做更多的操作。回调函数中的很多方法和结构体完全不知道怎么使用,于是尝试第二种方法。

大致思路是:先截图,然后从剪切板中读取位图数据,转换成CImage类型的图片,然后对图片进行缩放,构造ole对象,将其中的hBitmap替换成缩放图的hBitmap,最后插入richedit。我的做法只是能实现功能而已,很多地方并不规范,实现功能简单。如果有做过截图和ole的大佬,也希望能够指点一下。

大致源码如下:

CChatRichEditCtrl.h

class CChatRichEditCtrl : public CRichEditCtrl
{
CDisplayPicture* m_pDisplayPic = nullptr;//图片显示器类
bool m_bPastePic = false;//粘贴的是位图则屏蔽系统的ctrl+v
DWORD m_oleCnt = 0;
map<DWORD, CString> m_olePathMap;//粘贴到聊天框中的ole与原图的映射
public:
CChatRichEditCtrl() {/* AfxInitRichEdit(); AfxInitRichEdit2(); AfxOleInit(); /}
~CChatRichEditCtrl(){}
DECLARE_MESSAGE_MAP()
afx_msg void OnPaste();
afx_msg void OnPasteUpdateUI(CCmdUI
pCmdUI);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
virtual BOOL PreTranslateMessage(MSG* pMsg);
protected:

static DWORD CALLBACK readFunction(DWORD dwCookie,
LPBYTE lpBuf, // the buffer to fill
LONG nCount, // number of bytes to read
LONG* nRead); // number of bytes actually read

interface IExRichEditOleCallback; // forward declaration (see below in this header file)

IExRichEditOleCallback* m_pIRichEditOleCallback;
BOOL m_bCallbackSet;

interface IExRichEditOleCallback : public IRichEditOleCallback
{
public:
IExRichEditOleCallback();
virtual ~IExRichEditOleCallback();
int m_iNumStorages;
IStorage* pStorage;
DWORD m_dwRef;

virtual HRESULT STDMETHODCALLTYPE GetNewStorage(LPSTORAGE* lplpstg);
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
virtual HRESULT STDMETHODCALLTYPE GetInPlaceContext(LPOLEINPLACEFRAME FAR* lplpFrame,
LPOLEINPLACEUIWINDOW FAR* lplpDoc, LPOLEINPLACEFRAMEINFO lpFrameInfo);
virtual HRESULT STDMETHODCALLTYPE ShowContainerUI(BOOL fShow);
virtual HRESULT STDMETHODCALLTYPE QueryInsertObject(LPCLSID lpclsid, LPSTORAGE lpstg, LONG cp);
virtual HRESULT STDMETHODCALLTYPE DeleteObject(LPOLEOBJECT lpoleobj);
virtual HRESULT STDMETHODCALLTYPE QueryAcceptData(LPDATAOBJECT lpdataobj, CLIPFORMAT FAR* lpcfFormat,
DWORD reco, BOOL fReally, HGLOBAL hMetaPict);
virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
virtual HRESULT STDMETHODCALLTYPE GetClipboardData(CHARRANGE FAR* lpchrg, DWORD reco, LPDATAOBJECT FAR* lplpdataobj);
virtual HRESULT STDMETHODCALLTYPE GetDragDropEffect(BOOL fDrag, DWORD grfKeyState, LPDWORD pdwEffect);
virtual HRESULT STDMETHODCALLTYPE GetContextMenu(WORD seltyp, LPOLEOBJECT lpoleobj, CHARRANGE FAR* lpchrg,
HMENU FAR* lphmenu);
};
IRichEditOle* m_pRichEditOle;
virtual void PreSubclassWindow();
public:
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
};

CChatRichEditCtrl.cpp

#define ID_RICH_PASTE 104

BEGIN_MESSAGE_MAP(CChatRichEditCtrl, CRichEditCtrl)
ON_COMMAND(ID_RICH_PASTE, OnPaste)
ON_UPDATE_COMMAND_UI(ID_RICH_PASTE, OnPasteUpdateUI)
ON_WM_CREATE()
ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()

int CChatRichEditCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CRichEditCtrl::OnCreate(lpCreateStruct) == -1)
return -1;

ASSERT(m_pIRichEditOleCallback != NULL);
if (!m_bCallbackSet)
	SetOLECallback(m_pIRichEditOleCallback);

    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    m_pDisplayPic = new CDisplayPicture;
    m_pDisplayPic->Create(IDD_DIALOG_DISPLAYPIC, this/*AfxGetMainWnd()*/);
    m_pDisplayPic->ShowWindow(SW_HIDE);
return 0;

}

BOOL CChatRichEditCtrl::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类

if (pMsg->message == WM_KEYDOWN)
{
if(pMsg->wParam == 0x56 && GetAsyncKeyState(VK_CONTROL))
//0x56是’v’键,但是不知道为什么,直接写pMsg->wParam == 'v’不好使
{
m_bPastePic = false;
OnPaste();
if (m_bPastePic)
return TRUE;//屏蔽ctrl+v
}
}

return CRichEditCtrl::PreTranslateMessage(pMsg);
}

void CChatRichEditCtrl::OnPaste()
{
//对于图片,粘贴前需要进行处理
if (IsClipboardFormatAvailable(CF_DIB))
{
GLOBALHANDLE hGMem;
LPBITMAPINFO lpBI;
void* pDIBBits;
if (OpenClipboard())
{
hGMem = GetClipboardData(CF_DIB);
lpBI = (LPBITMAPINFO)GlobalLock(hGMem);
// point to DIB bits after BITMAPINFO object
pDIBBits = (void*)(lpBI + 1);
int width = 100 * lpBI->bmiHeader.biWidth / lpBI->bmiHeader.biHeight;
//缩略图高固定100,按比例计算缩略图宽,最大200
if (width > 200)
width = 200;
CRect abbpic(0,0, width,100);
CImage image, image1, image2;
if (m_pTeamWorkOpr)
{
CString path = L"C:\Users\Administrator\Desktop\新建文件夹";

            image1.Load(path + L"system\\screenshot.bmp");//样图,提供格式,否则下面的GetBPP()函数错误,无法生成图片
            image.Create(abbpic.Width(), abbpic.Height(), image1.GetBPP());
            image2.Create(lpBI->bmiHeader.biWidth, lpBI->bmiHeader.biHeight, image1.GetBPP());
            ::StretchDIBits(image2.GetDC(), 0, 0,
                lpBI->bmiHeader.biWidth,
                lpBI->bmiHeader.biHeight,
                0, 0, lpBI->bmiHeader.biWidth,
                lpBI->bmiHeader.biHeight,
                pDIBBits, lpBI, DIB_RGB_COLORS, SRCCOPY);
            //保存原图
            image2.Save(path + L"system\\oriscreenshot.png");
            string pathStr = StringWideToAnsi((path + L"system\\oriscreenshot.png").GetBuffer());
            //生成md5作为文件名另存文件,用于之后ole对象和图片的关联
            //生成md5用的第三方库,可以生成GUID代替md5做文件名
            string md5 = CalcFileMd5(pathStr);
            CString md5Name;
            md5Name = StringAnsiToWide(md5).c_str();
            CString md5Path;
            md5Path = path + L"system\\OriScreenShot\\" + md5Name + L".png";
            image2.Save(md5Path);
            HBITMAP bitmap2 = image2.Detach();
            image2.ReleaseDC();
            ::StretchDIBits(image.GetDC(),
                abbpic.left, abbpic.top,
                abbpic.Width(), abbpic.Height(),
                0, 0, lpBI->bmiHeader.biWidth,
                lpBI->bmiHeader.biHeight,
                pDIBBits, lpBI, DIB_RGB_COLORS, SRCCOPY);
            //保存缩略图
            GlobalUnlock(hGMem);
            image.Save(path + L"system\\abbscreenshot.png");
            HBITMAP bitmap = image.Detach();
            image.ReleaseDC();
            CloseClipboard();

            STGMEDIUM stgm;
            stgm.tymed = TYMED_GDI;
            stgm.hBitmap = bitmap;//在这里将图片hBitmap换成缩略图的
            stgm.pUnkForRelease = NULL;

            FORMATETC fm;
            fm.cfFormat = CF_BITMAP;
            fm.ptd = NULL;
            fm.dwAspect = DVASPECT_CONTENT;
            fm.lindex = -1;
            fm.tymed = TYMED_GDI;

            IStorage* pStorage;
            LPLOCKBYTES lpLockBytes = NULL;
            SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
            if (sc != S_OK) AfxThrowOleException(sc);
            ASSERT(lpLockBytes != NULL);
            sc = ::StgCreateDocfileOnILockBytes(lpLockBytes, STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_READWRITE, 0, &pStorage);
            if (sc != S_OK)
            {
                VERIFY(lpLockBytes->Release() == 0);
                lpLockBytes = NULL;
                AfxThrowOleException(sc);

            }
            ASSERT(pStorage != NULL);

            COleDataSource* pDataSource = new COleDataSource;
            pDataSource->CacheData(CF_BITMAP, &stgm);
            LPDATAOBJECT lpDataObject = (LPDATAOBJECT)pDataSource->GetInterface(&IID_IDataObject);

            LPOLECLIENTSITE lpClientSite;
            GetIRichEditOle()->GetClientSite(&lpClientSite);
            //创建OLE对象
            IOleObject* pOleObject;
            sc = OleCreateStaticFromData(lpDataObject, IID_IOleObject, OLERENDER_FORMAT,
                &fm, lpClientSite, pStorage, (void**)&pOleObject);
            if (sc != S_OK) AfxThrowOleException(sc);


            //插入OLE对象
            REOBJECT reobject;
            ZeroMemory(&reobject, sizeof(REOBJECT));
            reobject.cbStruct = sizeof(REOBJECT);

            CLSID clsid;
            sc = pOleObject->GetUserClassID(&clsid);
            if (sc != S_OK)
                AfxThrowOleException(sc);

            reobject.clsid = clsid;
            reobject.cp = REO_CP_SELECTION;
            reobject.dvaspect = DVASPECT_CONTENT;
            reobject.poleobj = pOleObject;
            reobject.polesite = lpClientSite;
            reobject.pstg = pStorage;
            reobject.dwUser = m_oleCnt++;//每次插入ole对象的索引,删除不做处理

            //REOBJECT obj;
            //obj.cbStruct = sizeof(obj);
            // 	if (GetPic(&obj))
            // 	{
            // 	long lStart, lEnd;
            // 	pTextIn1->GetSel(lStart, lEnd);
            // 	pTextIn1->SetSel(obj.cp, obj.cp + 1);
            // 	pTextIn1->Clear();
            // 
            // 	pTextIn1->GetIRichEditOle()->InsertObject(&reobject);
            // 
            // 	pTextIn1->SetSel(lStart, lEnd);
            // 
            // 	}
            //	else
            //	{
            GetIRichEditOle()->InsertObject(&reobject);

            //	}

            delete pDataSource;
            SendMessage(EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
            SetFocus();
            m_olePathMap.insert(make_pair(reobject.dwUser, md5Path));//关联ole和原图
            m_bPastePic = true;
        }
    }
}

}

void CChatRichEditCtrl::OnPasteUpdateUI(CCmdUI* pCmdUI)
{
pCmdUI->Enable(CanPaste());
}

DWORD CALLBACK CChatRichEditCtrl::readFunction(DWORD dwCookie,
LPBYTE lpBuf, // the buffer to fill
LONG nCount, // number of bytes to read
LONG* nRead) // number of bytes actually read
{
CFile* fp = (CFile*)dwCookie;
*nRead = fp->Read(lpBuf, nCount);
return 0;
}

CChatRichEditCtrl::IExRichEditOleCallback::IExRichEditOleCallback()
{
pStorage = NULL;
m_iNumStorages = 0;
m_dwRef = 0;

// set up OLE storage

HRESULT hResult = ::StgCreateDocfile(NULL,
	STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE /*| STGM_DELETEONRELEASE */ | STGM_CREATE,
	0, &pStorage);

if (pStorage == NULL ||
	hResult != S_OK)
{
	AfxThrowOleException(hResult);
}

}

CChatRichEditCtrl::IExRichEditOleCallback::~IExRichEditOleCallback()
{
}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::GetNewStorage(LPSTORAGE* lplpstg)
{
m_iNumStorages++;
WCHAR tName[50];
swprintf_s(tName, L"REOLEStorage%d", m_iNumStorages);

HRESULT hResult = pStorage->CreateStorage(tName,
	STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,
	0, 0, lplpstg);

if (hResult != S_OK)
{
	::AfxThrowOleException(hResult);
}

return hResult;

}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::QueryInterface(REFIID iid, void** ppvObject)
{

HRESULT hr = S_OK;
*ppvObject = NULL;

if (iid == IID_IUnknown ||
	iid == IID_IRichEditOleCallback)
{
	*ppvObject = this;
	AddRef();
	hr = NOERROR;
}
else
{
	hr = E_NOINTERFACE;
}

return hr;

}

ULONG STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::AddRef()
{
return ++m_dwRef;
}

ULONG STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::Release()
{
if (–m_dwRef == 0)
{
delete this;
return 0;
}

return m_dwRef;

}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::GetInPlaceContext(LPOLEINPLACEFRAME FAR* lplpFrame,
LPOLEINPLACEUIWINDOW FAR* lplpDoc, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::ShowContainerUI(BOOL fShow)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::QueryInsertObject(LPCLSID lpclsid, LPSTORAGE lpstg, LONG cp)
{
//return E_NOTIMPL;
return S_OK;
}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::DeleteObject(LPOLEOBJECT lpoleobj)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::QueryAcceptData(LPDATAOBJECT lpdataobj, CLIPFORMAT FAR* lpcfFormat,
DWORD reco, BOOL fReally, HGLOBAL hMetaPict)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::ContextSensitiveHelp(BOOL fEnterMode)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::GetClipboardData(CHARRANGE FAR* lpchrg, DWORD reco, LPDATAOBJECT FAR* lplpdataobj)
{
return E_NOTIMPL;//这里如果返回S_OK,则剪切板数据被拦截
}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::GetDragDropEffect(BOOL fDrag, DWORD grfKeyState, LPDWORD pdwEffect)
{

return S_OK;

}

HRESULT STDMETHODCALLTYPE
CChatRichEditCtrl::IExRichEditOleCallback::GetContextMenu(WORD seltyp, LPOLEOBJECT lpoleobj, CHARRANGE FAR* lpchrg,
HMENU FAR* lphmenu)
{
return S_OK;
}

void CChatRichEditCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
LONG cnt = GetIRichEditOle()->GetObjectCount();
for (LONG i=0;i<cnt;i++)//判断双击中哪个ole对象
{
REOBJECT reobject;
ZeroMemory(&reobject, sizeof(REOBJECT));
reobject.cbStruct = sizeof(REOBJECT);
GetIRichEditOle()->GetObjectW(i, &reobject, REO_GETOBJ_NO_INTERFACES);
if (reobject.dwFlags &= REO_SELECTED)
//这里我只知道如果选中ole对象则会有REO_SELECTED标志,但我感觉选中ole对象和对ole对象的操作不是这么做的
{
if (m_pDisplayPic)
{
if (m_olePathMap.count(reobject.dwUser))
{
CString path = m_olePathMap[reobject.dwUser];
m_pDisplayPic->DisplayPic(path);
}
}
}
}

CRichEditCtrl::OnLButtonDblClk(nFlags, point);

}

CDisplayPicture.h

class CDisplayPicture : public CDialogEx
{
DECLARE_DYNAMIC(CDisplayPicture)

public:
CDisplayPicture(CWnd* pParent = nullptr); // 标准构造函数
virtual ~CDisplayPicture();

// 对话框数据
enum { IDD = IDD_DIALOG_DISPLAYPIC };

void DisplayPic(CString path);
bool m_bInit = false;
bool m_bDealCut = false;
int m_delX;
int m_delY;
CRect m_initStaticPicRect;
int m_initDlgWidth;
int m_initDlgHeight;

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持

DECLARE_MESSAGE_MAP()

public:
CStatic m_picDisplayPic;
virtual BOOL OnInitDialog();
afx_msg void OnSize(UINT nType, int cx, int cy);
};

CDisplayPiture.cpp

IMPLEMENT_DYNAMIC(CDisplayPicture, CDialogEx)

CDisplayPicture::CDisplayPicture(CWnd* pParent / =nullptr/)
CDialogEx(IDD_DIALOG_DISPLAYPIC, pParent)
{

}

CDisplayPicture::~CDisplayPicture()
{
}

void CDisplayPicture::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATIC_DISPLAYPIC, m_picDisplayPic);
}

BEGIN_MESSAGE_MAP(CDisplayPicture, CDialogEx)
ON_WM_SIZE()
END_MESSAGE_MAP()

// CDisplayPicture 消息处理程序

void CDisplayPicture::DisplayPic(CString path)
{
CRect rect;
m_picDisplayPic.GetClientRect(&rect);
CImage image;
image.Load(path);
HBITMAP hbitmap = image.Detach();
if (image.GetWidth()>rect.Width()||image.GetHeight()>rect.Height())
{
CImage image1;
image1.Create(rect.Width(), rect.Height(), image.GetBPP());
SetStretchBltMode(image1.GetDC(), COLORONCOLOR);
StretchBlt(image1.GetDC(), 0, 0, rect.Width(), rect.Height(), image.GetDC(), 0, 0, image.GetWidth(), image.GetHeight(), SRCCOPY);
hbitmap = image1.Detach();
image1.ReleaseDC();
image1.Destroy();
}
m_picDisplayPic.SetBitmap(hbitmap);
image.ReleaseDC();
image.Destroy();
ShowWindow(SW_SHOW);
}

BOOL CDisplayPicture::OnInitDialog()
{
CDialogEx::OnInitDialog();

// TODO:  在此添加额外的初始化
m_bInit = true;
CRect rect, crect;
m_picDisplayPic.GetClientRect(rect);
GetClientRect(crect);
m_initStaticPicRect = rect;
m_initDlgHeight = crect.Height();
m_initDlgWidth = crect.Width();
SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0);

return TRUE;  // return TRUE unless you set the focus to a control
			  // 异常: OCX 属性页应返回 FALSE

}

void CDisplayPicture::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);

// TODO: 在此处添加消息处理程序代码
if (m_bInit)
{
	CRect currect;
	GetClientRect(currect);
	int curheight = currect.Height();
	int curwidth = currect.Width();
	CRect rect;
	rect.top = m_initStaticPicRect.top;
	rect.bottom = m_initStaticPicRect.bottom * curheight / m_initDlgHeight;
	rect.left = m_initStaticPicRect.left * curwidth / m_initDlgWidth;
	rect.right = m_initStaticPicRect.right * curwidth / m_initDlgWidth;
	m_picDisplayPic.MoveWindow(rect);
	RedrawWindow();
}

}

希望能对第一次做richedit的人有帮助

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值