Click here to Skip to main content
Email Password   helpLost your password?

Introduction

Most of us know that in MSN Messenger, while chatting, we can insert animated emoticons in the chat window. That's very cool. In China, there is another famous messenger called QQ (the former OICQ) that can display GIFs as emoticons. After I read some code about RichEdit and COM, and after many tests on QQ and MSN Messenger, I got the code and put it on my blog on CSDN . :)

Background

First, how does MSN Messenger show animated emoticons? In MSN Messenger, it uses PNG files as emoticons, every PNG image shows a list of frames for a whole animation. In QQ, it uses GIFs as emoticons, so the user can customize the emoticons by changing the image, in fact, in QQ, we can send any image to a friend, and it can set to be an emoticon. If we just need to display static emoticons, you can refer to Insert any HBITMAP (Bitmap) in your RichEdit Control .

Can CRichEditCtrl display a dynamic GIF? Of course not. But CRichEditCtrl can display a COM object, then what can a COM object do? Nearly anything. So we insert a COM object into the CRichEditCtrl instance, then what we can see will be the COM object. Is this what we want to see? No. What we want to see is animated emoticons! So we display the GIF in the COM object. Then what we can see will be the emoticons.

Using the code

First we need a COM object to display a GIF and for it to be inserted in the richedit. This COM object can be developed using ATL. If you do not know how to display a GIF, you can get the Gif89a source code or CPictureEx source code. If you do not care about the size of your application, GDI+ can be your choice. If you want to ask me which of the above I used to display GIF, my answer may be "none". Because I used a DLL in QQ, this DLL is a COM object, named ImageOle.dll. It is inserted and can show GIFs (in this module, it use GDI+ to show GIFs).

Follow these steps to get your own emoticons RichEdit:

First, open your OLE/COM Viewer in Microsoft Visual Studio 6.0 Tools. Use View TypeLib... to open ImageOle.dll (you' d better register it with regsvr32.exe), then you can get the text below:

[
  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);
};

This object implements an interface called IGifAnimator, we can use it to display GIFs. To see the effect, you can run ActiveX Control Test Container to test it. First invoke LoadFromFile, then 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;
}

How can we use it? Here is the code:

    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();    
    }

After that, your CEditCtrl can show animated GIFs.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalbad word filter to protect my kids.
ZUPERKOOL
7:30 7 Feb '10  
a warm hello to all msn gurus Smile

hi i am trying to develop a badword filter for msn/messenger to help protect my kids from bad words... "yes those words ...that later i we have to make up a fake definition when they ask dad what is the meaning of f**k :x"

anyways, i started using winpcap to capture the incoming socket but later found out that since sp2 xp will not allowed this"

the big question:

while reading this article it came to my attention that maybe i could filter at the application level....

do you think if taking this/your aproche would eventually end up in a simple badword_filter.dll.

thanks


Ivo Gomez
http://ivogomez.com




GeneralNice article, but...
Classical_boy
22:40 28 May '08  
Ya... When I type some characters into ExtRichEdit, then I insert an image, it don't look very good....
When I enter, the problem is same...
How can u fix this error???

Thx alot!


GeneralRe: Nice article, but...
Vu H. Pham
0:34 3 Nov '09  
Well, it's seem your ExtRichCtrl is buggy. I used a standard MFC's CRichEditCtrl and used exactly your code snippet, it works perfectly.
Thx alot.
GeneralError:
inamnak
21:09 23 Aug '07  
hi

I got error "class is not registered " how is overcome this.

Thanks in advance

inamnak
Generalsource code download
GABBBGA
23:17 11 Dec '06  
http://bbs.gameres.com/upload/sf_20061212171850.rar[^]
GeneralRe: source code download
ductv
10:47 8 Oct '07  
thank you so much. I have waited for so long... Smile
Generalhelp
lsmart
0:22 18 May '06  
I really want to know how to copy and paste the gif,how to get the gif which in CRichEditEx,so that I can save them to file

..........
Generalhelp
lsmart
0:18 18 May '06  
how can I copy and paste the text and the gif together? thanks!

lsmart
Generalneed help!
lsmart
0:14 18 May '06  
hi

..........
GeneralSouce Code ???
__Silver__
2:16 25 May '05  
Hi !

I'd like to adapt the DLL in order to embed gif (& not load it from file).
So I'd like to get source code of this article (isn't the goal of this website ???)

Thanks

__Silver__
GeneralRe: Souce Code ???
gxulg
20:18 26 May '05  
That DLL is not written by him, it was written by an IM company of China. The site of the company is www.qq.com.

learn to be still.
GeneralRe: Souce Code ???
jialei_sun
19:09 18 Oct '05  
Yes , u r rite...
The author is using that DLL
GeneralIMPORTANT : in vb.net??
maguxx
4:17 15 Apr '05  
i would like to know the same but in vb.net, could you help me??
GeneralRe: IMPORTANT : in vb.net??
tecznology
3:58 25 Jan '06  
yeah.. vb version would be usefull, but i think it wont work.. some events are not compatible with vb
GeneralMemory and resources
cguy
11:36 14 Apr '05  
Nice article. Have you done any stress testing with this in regards to memory and resources? For example, in a chat client the user could go crazy and put many emoticons in, or over a long chat, they could all add up. I'm not familiar with the IGifAnimator interface. Can you tell me if this particular solution loads the image over and over? Does it share resources between similar emoticons? Just some thoughts.
GeneralRe: Memory and resources
dTianx
3:50 22 Apr '05  
I have been done no stress testing.
the com server load the gif file while it be inserted into,
free the file while you delete the gif.
so you can insert any gif into the richedit
Generalwhere is the Source code?
zhaozhencn
21:57 12 Apr '05  
where is the Source code?
GeneralRe: where is the Source code?
mier
23:17 12 Apr '05  
I agree - don't know why people bother posting articles without source code. You've got my 1
GeneralRe: where is the Source code?
lam cuong
21:48 14 Apr '05  
yeah, plese share the source code. I like this article so much, but a little sad becoz without source code.
GeneralRe: where is the Source code?
toxcct
3:25 16 May '05  
i do agree, we are on CP to share...


TOXCCT >>> GEII power
[toxcct][VisualCalc]
GeneralRe: where is the Source code?
Miss Fress
18:35 23 May '05  
CodeProject admin, remove this useless article. No source code! Hmmm
GeneralRe: where is the Source code?
ranmoon
22:09 11 Aug '06  
bigcat damao

signal man
GeneralJust what I nedded and planned to do!
Robert W.
1:53 9 Apr '05  
So thank you! Smile
GeneralBut ... where is the source code???!
Robert W.
4:47 11 Apr '05  
Confused


Last Updated 16 May 2005 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010