65.9K
CodeProject 正在变化。 阅读更多。
Home

MSN Messenger风格的动画表情符号

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.38/5 (33投票s)

2005年4月9日

2分钟阅读

viewsIcon

251159

downloadIcon

9579

如何创建一个 RichEdit 控件来显示类似 MSN Messenger 的动画表情符号。

引言

我们大多数人都知道,在 MSN Messenger 中聊天时,我们可以在聊天窗口中插入动画表情符号。这很酷。在中国,还有另一个著名的即时通讯工具叫做 QQ (前身是 OICQ),它可以显示 GIF 作为表情符号。在我阅读了一些关于 RichEdit 和 COM 的代码,并在 QQ 和 MSN Messenger 上进行了许多测试之后,我得到了代码,并将其发布在我在 CSDN 上的博客上。:)

背景

首先,MSN Messenger 如何显示动画表情符号?在 MSN Messenger 中,它使用 PNG 文件作为表情符号,每个 PNG 图像显示整个动画的帧列表。在 QQ 中,它使用 GIF 作为表情符号,因此用户可以通过更改图像来定制表情符号,事实上,在 QQ 中,我们可以向朋友发送任何图像,并将其设置为表情符号。如果我们只需要显示静态表情符号,您可以参考在您的 RichEdit 控件中插入任何 HBITMAP (位图)

CRichEditCtrl 可以显示动态 GIF 吗?当然不行。但是CRichEditCtrl可以显示一个 COM 对象,那么 COM 对象可以做什么?几乎任何事情。所以我们将一个 COM 对象插入到CRichEditCtrl实例中,然后我们能看到的就是 COM 对象。这是我们想看到的吗?不。我们想看到的是动画表情符号!所以我们在 COM 对象中显示 GIF。然后我们能看到的将是表情符号。

使用代码

首先,我们需要一个 COM 对象来显示 GIF 并将其插入到富文本编辑器中。可以使用 ATL 开发此 COM 对象。如果您不知道如何显示 GIF,您可以获取 Gif89a 源代码或CPictureEx源代码。如果您不关心应用程序的大小,GDI+ 可以是您的选择。如果你想问我使用哪个来显示 GIF,我的答案可能是“都不是”。因为我使用了 QQ 中的一个 DLL,这个 DLL 是一个 COM 对象,名为ImageOle.dll。它被插入并可以显示 GIF(在这个模块中,它使用 GDI+ 显示 GIF)。

按照以下步骤获取您自己的表情符号 RichEdit

首先,在 Microsoft Visual Studio 6.0 工具中打开您的 OLE/COM 查看器。使用 View TypeLib...打开ImageOle.dll(您最好使用regsvr32.exe注册它),然后您就可以获得以下文本

[
  uuid(0C1CF2DF-05A3-4FEF-8CD4-F5CFC4355A16),
  helpstring("IGifAnimator Interface"),
  dual,
  nonextensible
]
dispinterface IGifAnimator {
    properties:
    methods:
        [id(0x00000001), helpstring("method LoadFromFile")]
        void LoadFromFile([in] BSTR FileName);
        [id(0x00000002), helpstring("method TriggerFrameChange")]
        VARIANT_BOOL TriggerFrameChange();
        [id(0x00000003), helpstring("method GetFilePath")]
        BSTR GetFilePath();
        [id(0x00000004), helpstring("method ShowText")]
        void ShowText([in] BSTR Text);
};

此对象实现一个名为IGifAnimator的接口,我们可以使用它来显示 GIF。要查看效果,您可以运行 ActiveX 控件测试容器来测试它。首先调用LoadFromFile,然后调用TriggerFrameChange

//use this line to import the dll and genetate tlh and tli file.
#import "D:\\Program files\\tencent\\qq\\ImageOle.dll" named_guids

ImageOle.tlh

// Created by Microsoft (R) C/C++ Compiler Version 12.00.8168.0 (9de7951a).
//
// d:\myproject\msger\debug\ImageOle.tlh
//
// C++ source equivalent of Win32 type library
// D:\\Program files\\tencent\\qq\\ImageOle.dll
// compiler-generated file created 10/25/04 at 22:00:58 - DO NOT EDIT!
#pragma once
#pragma pack(push, 8)
#include <comdef.h>

namespace ImageOleLib {

//
// Forward references and typedefs
//

struct /* coclass */ GifAnimator;
struct __declspec(uuid("0c1cf2df-05a3-4fef-8cd4-f5cfc4355a16"))
/* dual interface */ IGifAnimator;

//
// Smart pointer typedef declarations
//

_COM_SMARTPTR_TYPEDEF(IGifAnimator, __uuidof(IGifAnimator));

//
// Type library items
//

struct __declspec(uuid("06ada938-0fb0-4bc0-b19b-0a38ab17f182"))
GifAnimator;
    // [ default ] interface IGifAnimator

struct __declspec(uuid("0c1cf2df-05a3-4fef-8cd4-f5cfc4355a16"))
IGifAnimator : IDispatch
{
    //
    // Wrapper methods for error-handling
    //

    HRESULT LoadFromFile (
        _bstr_t FileName );
    VARIANT_BOOL TriggerFrameChange ( );
    _bstr_t GetFilePath ( );
    HRESULT ShowText (
        _bstr_t Text );

    //
    // Raw methods provided by interface
    //

    virtual HRESULT __stdcall raw_LoadFromFile (
        BSTR FileName ) = 0;
    virtual HRESULT __stdcall raw_TriggerFrameChange (
        VARIANT_BOOL * pbChanged ) = 0;
    virtual HRESULT __stdcall raw_GetFilePath (
        BSTR * pFilePath ) = 0;
    virtual HRESULT __stdcall raw_ShowText (
        BSTR Text ) = 0;
};

//
// Named GUID constants initializations
//

extern "C" const GUID __declspec(selectany) LIBID_ImageOleLib =
    {0x710993a2,0x4f87,0x41d7,{0xb6,0xfe,0xf5,0xa2,0x03,0x68,0x46,0x5f}};
extern "C" const GUID __declspec(selectany) CLSID_GifAnimator =
    {0x06ada938,0x0fb0,0x4bc0,{0xb1,0x9b,0x0a,0x38,0xab,0x17,0xf1,0x82}};
extern "C" const GUID __declspec(selectany) IID_IGifAnimator =
    {0x0c1cf2df,0x05a3,0x4fef,{0x8c,0xd4,0xf5,0xcf,0xc4,0x35,0x5a,0x16}};

//
// Wrapper method implementations
//
#include "d:\myproject\msger\debug\ImageOle.tli"

} // namespace ImageOleLib
#pragma pack(pop)

ImageOle.tli

// Created by Microsoft (R) C/C++ Compiler Version 12.00.8168.0 (79a657ba).
//
// ImageOle.tli
//
// Wrapper implementations for Win32 type library
// D:\\Program Files\\Tencent\\qq\\ImageOle.dll
// compiler-generated file created 10/11/04 at 18:24:40 - DO NOT EDIT!

#pragma once

//
// interface IGifAnimator wrapper method implementations
//

inline HRESULT IGifAnimator::LoadFromFile ( _bstr_t FileName ) {
    HRESULT _hr = raw_LoadFromFile(FileName);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _hr;
}

inline VARIANT_BOOL IGifAnimator::TriggerFrameChange ( ) {
    VARIANT_BOOL _result;
    HRESULT _hr = raw_TriggerFrameChange(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _result;
}

inline _bstr_t IGifAnimator::GetFilePath ( ) {
    BSTR _result;
    HRESULT _hr = raw_GetFilePath(&_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _bstr_t(_result, false);
}

inline HRESULT IGifAnimator::ShowText ( _bstr_t Text ) {
    HRESULT _hr = raw_ShowText(Text);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _hr;
}

我们如何使用它?以下是代码

    LPLOCKBYTES lpLockBytes = NULL;
    SCODE sc;
    HRESULT hr;
    //print to RichEdit' s IClientSite
    LPOLECLIENTSITE m_lpClientSite;
    //A smart point to IAnimator
    IGifAnimatorPtr    m_lpAnimator;
    //ptr 2 storage    
    LPSTORAGE m_lpStorage;
    //the object 2 b insert 2
    LPOLEOBJECT    m_lpObject;

    //Create lockbytes
    sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
    if (sc != S_OK)
        AfxThrowOleException(sc);
    ASSERT(lpLockBytes != NULL);
    
    //use lockbytes to create storage
    sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,
        STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &m_lpStorage);
    if (sc != S_OK)
    {
        VERIFY(lpLockBytes->Release() == 0);
        lpLockBytes = NULL;
        AfxThrowOleException(sc);
    }
    ASSERT(m_lpStorage != NULL);
    
    //get the ClientSite of the very RichEditCtrl
    GetIRichEditOle()->GetClientSite(&m_lpClientSite);
    ASSERT(m_lpClientSite != NULL);

    try
    {
        //Initlize COM interface
        hr = ::CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
        if( FAILED(hr) )
            _com_issue_error(hr);
        
        //Get GifAnimator object
        //here, I used a smart point, so I do not need to free it
        hr = m_lpAnimator.CreateInstance(CLSID_GifAnimator);    
        if( FAILED(hr) )
                _com_issue_error(hr);
        //COM operation need BSTR, so get a BSTR
        BSTR path = strPicPath.AllocSysString();

        //Load the gif
        hr = m_lpAnimator->LoadFromFile(path);
        if( FAILED(hr) )
            _com_issue_error(hr);
            
        TRACE0( m_lpAnimator->GetFilePath() );
        
        //get the IOleObject
        hr = m_lpAnimator.QueryInterface(IID_IOleObject, (void**)&m_lpObject);
        if( FAILED(hr) )
            _com_issue_error(hr);
        
        //Set it 2 b inserted
        OleSetContainedObject(m_lpObject, TRUE);
        
        //2 insert in 2 richedit, you need a struct of REOBJECT
        REOBJECT reobject;
        ZeroMemory(&reobject, sizeof(REOBJECT));

        reobject.cbStruct = sizeof(REOBJECT);    
        CLSID clsid;
        sc = m_lpObject->GetUserClassID(&clsid);
        if (sc != S_OK)
            AfxThrowOleException(sc);
        //set clsid
        reobject.clsid = clsid;
        //can be selected
        reobject.cp = REO_CP_SELECTION;
        //content, but not static
        reobject.dvaspect = DVASPECT_CONTENT;
        //goes in the same line of text line
        reobject.dwFlags = REO_BELOWBASELINE; //REO_RESIZABLE |
        reobject.dwUser = 0;
        //the very object
        reobject.poleobj = m_lpObject;
        //client site contain the object
        reobject.polesite = m_lpClientSite;
        //the storage 
        reobject.pstg = m_lpStorage;
        
        SIZEL sizel;
        sizel.cx = sizel.cy = 0;
        reobject.sizel = sizel;
        HWND hWndRT = this->m_hWnd;
        
        //Sel all text
//        ::SendMessage(hWndRT, EM_SETSEL, 0, -1);
//        DWORD dwStart, dwEnd;
//        ::SendMessage(hWndRT, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
//        ::SendMessage(hWndRT, EM_SETSEL, dwEnd+1, dwEnd+1);

        //Insert after the line of text
        GetIRichEditOle()->InsertObject(&reobject);
        ::SendMessage(hWndRT, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
        VARIANT_BOOL ret;
        //do frame changing
        ret = m_lpAnimator->TriggerFrameChange();
        //show it
        m_lpObject->DoVerb(OLEIVERB_UIACTIVATE, NULL, m_lpClientSite, 0, 
                                                             m_hWnd, NULL);
        m_lpObject->DoVerb(OLEIVERB_SHOW, NULL, m_lpClientSite, 0, m_hWnd, 
                                                                       NULL);
        
        //redraw the window to show animation
        RedrawWindow();

        if (m_lpClientSite)
        {
            m_lpClientSite->Release();
            m_lpClientSite = NULL;
        }
        if (m_lpObject)
        {
            m_lpObject->Release();
            m_lpObject = NULL;
        }
        if (m_lpStorage)
        {
            m_lpStorage->Release();
            m_lpStorage = NULL;
        }
        
        SysFreeString(path);
    }
    catch( _com_error e )
    {
        AfxMessageBox(e.ErrorMessage());
        ::CoUninitialize();    
    }

之后,您的CEditCtrl可以显示动画 GIF。

© . All rights reserved.