如何在RichEditCtrl中插入ActiveX控件

前两天在优快云上看到一篇文章介绍如何在RichEditCtrl控件中添加一个动态的Gif,比较有意思。去年用老版的QQ时倒是模仿写了一下,当时只解决插入bmp和词法解析,后来就没有继续下去。

    优快云上的文章,采用的是腾讯QQ带的组件库ImageOle.dll提供的GifAnimator类提供的ActiveX对象来实现这个功能。该对象暴露一个IGifAnimator接口,具体的定义如下:

interface IGifAnimator : IDispatch {

    HRESULT LoadFromFile([in] BSTR FileName);

    HRESULT TriggerFrameChange([out, retval] VARIANT_BOOL* pbChanged);

    HRESULT GetFilePath([out, retval] BSTR* pFilePath);

    HRESULT ShowText([in] BSTR Text);

};

优快云中给出了一个例子,采用Import方式倒入com类,并且给出了如何插入到RichEdit中。不过,我不太喜欢这种方式,自己建了一个。

下面是通过从类型库创建类的方式创建的一个类,比较简洁。

#include <initguid.h>

DEFINE_GUID(CLSID_GIFAnimator,

0x06ADA938,0x0FB0,0x4BC0,0xB1,0x9B,0x 0A ,0x38,0xAB,0x17,0xF1,0x82);

DEFINE_GUID(IID_IGIFAnimator,

0x 0C 1CF2DF,0x 05A 3,0x4FEF,0x 8C ,0xD4,0xF5,0xCF,0xC4,0x35,0x 5A ,0x16);

 

class CGifAnimator : public COleDispatchDriver

{

public:

    CGifAnimator(){} // 调用 COleDispatchDriver 默认构造函数

    CGifAnimator(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}

    CGifAnimator(const CGifAnimator& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

    // 操作

public:

    // IGifAnimator 方法

public:

    void LoadFromFile(LPCTSTR FileName){

        static BYTE parms[] = VTS_BSTR ;

        InvokeHelper(0x1, DISPATCH_METHOD, VT_EMPTY, NULL, parms, FileName);

    }

    BOOL TriggerFrameChange(){

        BOOL result;

        InvokeHelper(0x2, DISPATCH_METHOD, VT_BOOL, (void*)&result, NULL);

        return result;

    }

    CString GetFilePath(){

        CString result;

        InvokeHelper(0x3, DISPATCH_METHOD, VT_BSTR, (void*)&result, NULL);

        return result;

    }

    void ShowText(LPCTSTR Text){

        static BYTE parms[] = VTS_BSTR ;

        InvokeHelper(0x4, DISPATCH_METHOD, VT_EMPTY, NULL, parms, Text);

    }

};

    通过编码,对上面的四个函数进行了简单的测试,每个函数的功能如下:

1  void LoadFromFile(LPCTSTR FileName)

装载要显示的文件到控件,文件为图像类型,不局限于GIF格式。失败时抛出异常。

2     BOOL TriggerFrameChange()

发出消息,动态的GIF动起来。在对话框中有问题,可以通过向父窗口发送WM_PAINT消息来激活。

3     CString GetFilePath()

返回装入的文件的名称。

4     void ShowText(LPCTSTR Text)

没有测出其作用,只是调用以后,动态的GIF不动了。

    下面给出的代码,将该控件插入到RichEdit控件。

BOOL CImageRichEditCtrl::InsertGifAnimator(LPCTSTR lpszGifName)

{

    CGifAnimator    animator;

    IStorage*       lpStorage       = NULL;

    IOleObject*     lpOleObject     = NULL;

    LPLOCKBYTES     lpLockBytes     = NULL;

    IOleClientSite* lpOleClientSite = NULL;

    IRichEditOle*   lpRichEditOle   = NULL;

    CLSID           clsid;

    REOBJECT        reobject;

 

    lpRichEditOle   = GetIRichEditOle();

    if( lpRichEditOle == NULL ) return FALSE;

 

    BOOL bRet = animator.CreateDispatch(CLSID_GIFAnimator);

    if( bRet == FALSE ){

        lpRichEditOle->Release();

        return FALSE;

    }

 

    try{

        animator.LoadFromFile(lpszGifName);     //Load失败时,抛出异常

        HRESULT hr = animator.m_lpDispatch->QueryInterface(&lpOleObject);

        if( hr != S_OK )

            AfxThrowOleException(hr);

        hr = lpOleObject->GetUserClassID(&clsid);

        if ( hr != S_OK)

            AfxThrowOleException(hr);

       

        hr = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);

        if (hr != S_OK)

            AfxThrowOleException(hr);

        ASSERT(lpLockBytes != NULL);

       

        hr = ::StgCreateDocfileOnILockBytes(lpLockBytes,

            STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &lpStorage);

        if (hr != S_OK)

        {

            VERIFY(lpLockBytes->Release() == 0);

            lpLockBytes = NULL;

            AfxThrowOleException(hr);

        }

 

        lpRichEditOle->GetClientSite(&lpOleClientSite);

       

        ZeroMemory(&reobject, sizeof(REOBJECT));       

        reobject.cbStruct   = sizeof(REOBJECT);

        reobject.clsid      = clsid;

        reobject.cp         = REO_CP_SELECTION;

        reobject.dvaspect   = DVASPECT_CONTENT;

        reobject.dwFlags    = REO_BELOWBASELINE;

        reobject.poleobj    = lpOleObject;

        reobject.polesite   = lpOleClientSite;

        reobject.pstg       = lpStorage;

 

        hr = lpRichEditOle->InsertObject( &reobject );

        if (hr != S_OK)

            AfxThrowOleException(hr);

        OleSetContainedObject(lpOleObject,TRUE);

 

//      bRet = animator.TriggerFrameChange();  

//      作用是更新显示,让ActiveX控件动起来,不过在对话框中的控件无效。

 

//如果本函数在OnInitDialog中调用,本消息不能被处理,可能不会动态显示

        SendMessage(WM_PAINT,0,0); 

    }

 

    catch( COleException* e )

    {

        TRACE(_T("CImageRichEditCtrl::InsertGifAnimator() OleException

                code:%d"),e->m_sc);

        e->Delete();

        bRet = FALSE;

    }

 

    // release the interface

    lpRichEditOle->Release();

    if( lpOleObject     != NULL )   lpOleObject->Release();

    if( lpOleClientSite != NULL )       lpOleClientSite->Release();

    if( lpStorage       != NULL )           lpStorage->Release();

 

    return bRet;

}

这样,通过调用InsertGifAnimator就可以将一个图像文件插入到RichEdit控件中,并且如果是动态的GIF则可以动起来,对于聊天来说,效果比较好。

    那么这个控件是如何实现的呢?通过的分析,实现控件需要的技术要点如下:

1  必须是ActiveX控件,这可以用ATL模板来生成,Easy

2  用于可以插入很多控件实例,为了尽可能的少占用系统资源,必须为WndLess控件。

3  为了动态显示GIFTIFF文件,必须采用时钟,(线程占用资源太大)。

4  为了显示所有的图像文件,这里采用Gdiplus来简化图像的操作。

5  为了支持Undo操作,必须在处理Undo后的动态显示。

    而上述操作中,相对较难的是时钟处理和Undo恢复操作。

    经过几天的努力,我也写了一个支持在RichEdit控件中显示动画的控件DynamicGif,下面是测试结果(在插入了6128×128的动态GIF文件后),和QQ带的ImageOle.dll相比较。

 

CPU

内存(KB)

句柄数

线程数

GDI

未插入控件时

0

6104

94

2

59

DynamicGif

28

8332

118

3

69

QQ

44

8372

118

3

70

测试结果:资源占用比较

   

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值