MSN / QQ 中的CRichEditCtrl (四) —— 动画表情(Full Source Code)

来源:互联网 发布:广电网络三网合一 编辑:程序博客网 时间:2024/05/08 19:25

Full Source Code Here : 

http://www.codeproject.com/richedit/AnimatedEmoticon.asp

首先对标题说明一下,在MSN中,聊天的窗口可能是一个自定义的类。大家用Spy ++可以看看。
对与自定义窗口,可以使用CreateWindow, SetWindowLong或者是SubclassWindow实现,不过这不
是我现在讨论的话题。

好, 先看看效果再说:


我实现的主要就是:(一)字体格式; (二)超链接;(三)背景图片;(四)动画表情



关于这个的实现。我们首先应该明了,我们必须实现一个OLE对象。而且这个对象能够播放GIF.
对于播放GIF,代码已经很多了。比较有名气的就是那个谁封装的Gif89a,还不错,我喜欢那个CPictureEx.
可以在vchelp找到。
有了这个就完了么?当然不是。你还有写一个OLE/COM对象。实现IOleObject等。
你是用ATL还是MFC呢?
呵呵,我什么也没有用。在这个目录下%program file%/tencent/qq/,你可以看到一个ImageOle.dll,她就是
你日夜思念的人!
好吧,让我们来看看他的真面目。怎么看?X Ray? 当然不是——OLE/COM Viewer.
Click on “All Objects”,

View TypeLib... 打开那个文件,你可以看到:
[
  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);
};

这个接口就是我们要的。你可以用ActiveX Control Test Container测试一下。还挺管用的。

那么怎么在我们的程序中使用呢?我也没有那么多的时间,先给出代码吧,有时间再说啊,见谅。
首先我们导入ImageOle.dll
1#import "D://Program files//tencent//qq//ImageOle.dll" named_guids

 named_guids 表示让编译器为我把对应库的GUID和声明对应起来。我们就可以用CLSID_GifAnimator引用对应的接口了。不用那一长串的东西。然后它就会为我们生成两个文件。
ImageOle.tlh

 

1// Created by Microsoft (R) C/C++ Compiler Version 12.00.8168.0 (9de7951a).2//3// d:/myproject/msger/debug/ImageOle.tlh4//5// C++ source equivalent of Win32 type library D://Program files//tencent//qq//ImageOle.dll6// compiler-generated file created 10/25/04 at 22:00:58 - DO NOT EDIT!7#pragma once8#pragma pack(push, 8)9#include <comdef.h>1011namespace ImageOleLib {1213//14// Forward references and typedefs15//1617struct /* coclass */ GifAnimator;18struct __declspec(uuid("0c1cf2df-05a3-4fef-8cd4-f5cfc4355a16"))19/* dual interface */ IGifAnimator;2021//22// Smart pointer typedef declarations23//2425_COM_SMARTPTR_TYPEDEF(IGifAnimator, __uuidof(IGifAnimator));2627//28// Type library items29//3031struct __declspec(uuid("06ada938-0fb0-4bc0-b19b-0a38ab17f182"))32GifAnimator;33    // [ default ] interface IGifAnimator3435struct __declspec(uuid("0c1cf2df-05a3-4fef-8cd4-f5cfc4355a16"))36IGifAnimator : IDispatch37{38    //39    // Wrapper methods for error-handling40    //4142    HRESULT LoadFromFile (43        _bstr_t FileName );44    VARIANT_BOOL TriggerFrameChange ( );45    _bstr_t GetFilePath ( );46    HRESULT ShowText (47        _bstr_t Text );4849    //50    // Raw methods provided by interface51    //5253    virtual HRESULT __stdcall raw_LoadFromFile (54        BSTR FileName ) = 0;55    virtual HRESULT __stdcall raw_TriggerFrameChange (56        VARIANT_BOOL * pbChanged ) = 0;57    virtual HRESULT __stdcall raw_GetFilePath (58        BSTR * pFilePath ) = 0;59    virtual HRESULT __stdcall raw_ShowText (60        BSTR Text ) = 0;61};6263//64// Named GUID constants initializations65//6667extern "C" const GUID __declspec(selectany) LIBID_ImageOleLib =68    {0x710993a2,0x4f87,0x41d7,{0xb6,0xfe,0xf5,0xa2,0x03,0x68,0x46,0x5f}};69extern "C" const GUID __declspec(selectany) CLSID_GifAnimator =70    {0x06ada938,0x0fb0,0x4bc0,{0xb1,0x9b,0x0a,0x38,0xab,0x17,0xf1,0x82}};71extern "C" const GUID __declspec(selectany) IID_IGifAnimator =72    {0x0c1cf2df,0x05a3,0x4fef,{0x8c,0xd4,0xf5,0xcf,0xc4,0x35,0x5a,0x16}};7374//75// Wrapper method implementations76//77#include "d:/myproject/msger/debug/ImageOle.tli"7879} // namespace ImageOleLib80#pragma pack(pop)81
ImageOle.tli
1// Created by Microsoft (R) C/C++ Compiler Version 12.00.8168.0 (9de7951a).2//3// d:/myproject/msger/debug/ImageOle.tli4//5// Wrapper implementations for Win32 type library D://Program files//tencent//qq//ImageOle.dll6// compiler-generated file created 10/25/04 at 22:00:58 - DO NOT EDIT!7#pragma once89//10// interface IGifAnimator wrapper method implementations11//1213inline HRESULT IGifAnimator::LoadFromFile ( _bstr_t FileName ) {14    HRESULT _hr = raw_LoadFromFile(FileName);15    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));16    return _hr;17}1819inline VARIANT_BOOL IGifAnimator::TriggerFrameChange ( ) {20    VARIANT_BOOL _result;21    HRESULT _hr = raw_TriggerFrameChange(&_result);22    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));23    return _result;24}2526inline _bstr_t IGifAnimator::GetFilePath ( ) {27    BSTR _result;28    HRESULT _hr = raw_GetFilePath(&_result);29    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));30    return _bstr_t(_result, false);31}3233inline HRESULT IGifAnimator::ShowText ( _bstr_t Text ) {34    HRESULT _hr = raw_ShowText(Text);35    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));36    return _hr;37}38

有了这些,你使用接口和函数就很方便了:)
然后就这样:
WINOLEAPI  CoInitializeEx(LPVOID pvReserved, DWORD dwCoInit);
为什么要这样呢?因为我们使用这个函数。那有必要这样么?
在MSDN是说要 #define _DCOM_ 就行了。可惜我没有成功。就只好这样了。

接着就是实现代码了。现在看应该没有很大的问题。如果还是很难明白。那么我你得好好研究CRichEditCtrl和OLE了。
听说Inside OLE和Inside COM很好。我没有弄到。就看了《COM+编程指南》和潘爱民的《COM原理和应用》,不错!
还有一个好东西。就是 ActiveX Control Test Container 的源代码。

最后该出场的就是实现代码了。 Now let 's begin ...
1      LPLOCKBYTES lpLockBytes = NULL;2    SCODE sc;3    HRESULT hr;4    //print to RichEdit' s IClientSite5    LPOLECLIENTSITE m_lpClientSite;6    //A smart point to IAnimator7    IGifAnimatorPtrm_lpAnimator;8    //ptr 2 storage9    LPSTORAGE m_lpStorage;10    //the object 2 b insert 211    LPOLEOBJECTm_lpObject;1213    //Create lockbytes14    sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);15    if (sc != S_OK)16AfxThrowOleException(sc);17    ASSERT(lpLockBytes != NULL);1819    //use lockbytes to create storage20    sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,21STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &m_lpStorage);22    if (sc != S_OK)23    {24VERIFY(lpLockBytes->Release() == 0);25lpLockBytes = NULL;26AfxThrowOleException(sc);27    }28    ASSERT(m_lpStorage != NULL);2930    //get the ClientSite of the very RichEditCtrl31    GetIRichEditOle()->GetClientSite(&m_lpClientSite);32    ASSERT(m_lpClientSite != NULL);3334    try35    {36//Initlize COM interface37hr = ::CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );38if( FAILED(hr) )39_com_issue_error(hr);4041//Get GifAnimator object42//here, I used a smart point, so I do not need to free it43hr = m_lpAnimator.CreateInstance(CLSID_GifAnimator);44if( FAILED(hr) )45_com_issue_error(hr);46//COM operation need BSTR, so get a BSTR47BSTR path = strPicPath.AllocSysString();4849//Load the gif50hr = m_lpAnimator->LoadFromFile(path);51if( FAILED(hr) )52_com_issue_error(hr);5354TRACE0( m_lpAnimator->GetFilePath() );5556//get the IOleObject57hr = m_lpAnimator.QueryInterface(IID_IOleObject, (void**)&m_lpObject);58if( FAILED(hr) )59_com_issue_error(hr);6061//Set it 2 b inserted62OleSetContainedObject(m_lpObject, TRUE);6364//2 insert in 2 richedit, you need a struct of REOBJECT65REOBJECT reobject;66ZeroMemory(&reobject, sizeof(REOBJECT));6768reobject.cbStruct = sizeof(REOBJECT);69CLSID clsid;70sc = m_lpObject->GetUserClassID(&clsid);71if (sc != S_OK)72AfxThrowOleException(sc);73//set clsid74reobject.clsid = clsid;75//can be selected76reobject.cp = REO_CP_SELECTION;77//content, but not static78reobject.dvaspect = DVASPECT_CONTENT;79//goes in the same line of text line80reobject.dwFlags = REO_BELOWBASELINE; //REO_RESIZABLE |81reobject.dwUser = 0;82//the very object83reobject.poleobj = m_lpObject;84//client site contain the object85reobject.polesite = m_lpClientSite;86//the storage 87reobject.pstg = m_lpStorage;8889SIZEL sizel;90sizel.cx = sizel.cy = 0;91reobject.sizel = sizel;92HWND hWndRT = this->m_hWnd;93//Sel all text94//::SendMessage(hWndRT, EM_SETSEL, 0, -1);95//DWORD dwStart, dwEnd;96//::SendMessage(hWndRT, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);97//::SendMessage(hWndRT, EM_SETSEL, dwEnd+1, dwEnd+1);98//Insert after the line of text99GetIRichEditOle()->InsertObject(&reobject);100::SendMessage(hWndRT, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);101VARIANT_BOOL ret;102//do frame changing103ret = m_lpAnimator->TriggerFrameChange();104//show it105m_lpObject->DoVerb(OLEIVERB_UIACTIVATE, NULL, m_lpClientSite, 0, m_hWnd, NULL);106m_lpObject->DoVerb(OLEIVERB_SHOW, NULL, m_lpClientSite, 0, m_hWnd, NULL);107108//redraw the window to show animation109RedrawWindow();110111if (m_lpClientSite)112{113m_lpClientSite->Release();114m_lpClientSite = NULL;115}116if (m_lpObject)117{118m_lpObject->Release();119m_lpObject = NULL;120}121if (m_lpStorage)122{123m_lpStorage->Release();124m_lpStorage = NULL;125}126127SysFreeString(path);128}129catch( _com_error e )130{131AfxMessageBox(e.ErrorMessage());132::CoUninitialize();133}
That 's all.
感谢大家的关注。其实实现代码就这么多了。一句不少!
如果大家是再不能实现,我交付了代码后就会给大家完整的工程的。
:)





1// Created by Microsoft (R) C/C++ Compiler Version 12.00.8168.0 (9de7951a).2//3// d:/myproject/msger/debug/ImageOle.tli4//5// Wrapper implementations for Win32 type library D://Program files//tencent//qq//ImageOle.dll6// compiler-generated file created 10/25/04 at 22:00:58 - DO NOT EDIT!7#pragma once89//10// interface IGifAnimator wrapper method implementations11//1213inline HRESULT IGifAnimator::LoadFromFile ( _bstr_t FileName ) {14    HRESULT _hr = raw_LoadFromFile(FileName);15    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));16    return _hr;17}1819inline VARIANT_BOOL IGifAnimator::TriggerFrameChange ( ) {20    VARIANT_BOOL _result;21    HRESULT _hr = raw_TriggerFrameChange(&_result);22    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));23    return _result;24}2526inline _bstr_t IGifAnimator::GetFilePath ( ) {27    BSTR _result;28    HRESULT _hr = raw_GetFilePath(&_result);29    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));30    return _bstr_t(_result, false);31}3233inline HRESULT IGifAnimator::ShowText ( _bstr_t Text ) {34    HRESULT _hr = raw_ShowText(Text);35    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));36    return _hr;37}38

有了这些,你使用接口和函数就很方便了:)
然后就这样:
WINOLEAPI  CoInitializeEx(LPVOID pvReserved, DWORD dwCoInit);
为什么要这样呢?因为我们使用这个函数。那有必要这样么?
在MSDN是说要 #define _DCOM_ 就行了。可惜我没有成功。就只好这样了。

接着就是实现代码了。现在看应该没有很大的问题。如果还是很难明白。那么我你得好好研究CRichEditCtrl和OLE了。
听说Inside OLE和Inside COM很好。我没有弄到。就看了《COM+编程指南》和潘爱民的《COM原理和应用》,不错!
还有一个好东西。就是 ActiveX Control Test Container 的源代码。

最后该出场的就是实现代码了。 Now let 's begin ...
1      LPLOCKBYTES lpLockBytes = NULL;2    SCODE sc;3    HRESULT hr;4    //print to RichEdit' s IClientSite5    LPOLECLIENTSITE m_lpClientSite;6    //A smart point to IAnimator7    IGifAnimatorPtrm_lpAnimator;8    //ptr 2 storage9    LPSTORAGE m_lpStorage;10    //the object 2 b insert 211    LPOLEOBJECTm_lpObject;1213    //Create lockbytes14    sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);15    if (sc != S_OK)16AfxThrowOleException(sc);17    ASSERT(lpLockBytes != NULL);1819    //use lockbytes to create storage20    sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,21STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &m_lpStorage);22    if (sc != S_OK)23    {24VERIFY(lpLockBytes->Release() == 0);25lpLockBytes = NULL;26AfxThrowOleException(sc);27    }28    ASSERT(m_lpStorage != NULL);2930    //get the ClientSite of the very RichEditCtrl31    GetIRichEditOle()->GetClientSite(&m_lpClientSite);32    ASSERT(m_lpClientSite != NULL);3334    try35    {36//Initlize COM interface37hr = ::CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );38if( FAILED(hr) )39_com_issue_error(hr);4041//Get GifAnimator object42//here, I used a smart point, so I do not need to free it43hr = m_lpAnimator.CreateInstance(CLSID_GifAnimator);44if( FAILED(hr) )45_com_issue_error(hr);46//COM operation need BSTR, so get a BSTR47BSTR path = strPicPath.AllocSysString();4849//Load the gif50hr = m_lpAnimator->LoadFromFile(path);51if( FAILED(hr) )52_com_issue_error(hr);5354TRACE0( m_lpAnimator->GetFilePath() );5556//get the IOleObject57hr = m_lpAnimator.QueryInterface(IID_IOleObject, (void**)&m_lpObject);58if( FAILED(hr) )59_com_issue_error(hr);6061//Set it 2 b inserted62OleSetContainedObject(m_lpObject, TRUE);6364//2 insert in 2 richedit, you need a struct of REOBJECT65REOBJECT reobject;66ZeroMemory(&reobject, sizeof(REOBJECT));6768reobject.cbStruct = sizeof(REOBJECT);69CLSID clsid;70sc = m_lpObject->GetUserClassID(&clsid);71if (sc != S_OK)72AfxThrowOleException(sc);73//set clsid74reobject.clsid = clsid;75//can be selected76reobject.cp = REO_CP_SELECTION;77//content, but not static78reobject.dvaspect = DVASPECT_CONTENT;79//goes in the same line of text line80reobject.dwFlags = REO_BELOWBASELINE; //REO_RESIZABLE |81reobject.dwUser = 0;82//the very object83reobject.poleobj = m_lpObject;84//client site contain the object85reobject.polesite = m_lpClientSite;86//the storage 87reobject.pstg = m_lpStorage;8889SIZEL sizel;90sizel.cx = sizel.cy = 0;91reobject.sizel = sizel;92HWND hWndRT = this->m_hWnd;93//Sel all text94//::SendMessage(hWndRT, EM_SETSEL, 0, -1);95//DWORD dwStart, dwEnd;96//::SendMessage(hWndRT, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);97//::SendMessage(hWndRT, EM_SETSEL, dwEnd+1, dwEnd+1);98//Insert after the line of text99GetIRichEditOle()->InsertObject(&reobject);100::SendMessage(hWndRT, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);101VARIANT_BOOL ret;102//do frame changing103ret = m_lpAnimator->TriggerFrameChange();104//show it105m_lpObject->DoVerb(OLEIVERB_UIACTIVATE, NULL, m_lpClientSite, 0, m_hWnd, NULL);106m_lpObject->DoVerb(OLEIVERB_SHOW, NULL, m_lpClientSite, 0, m_hWnd, NULL);107108//redraw the window to show animation109RedrawWindow();110111if (m_lpClientSite)112{113m_lpClientSite->Release();114m_lpClientSite = NULL;115}116if (m_lpObject)117{118m_lpObject->Release();119m_lpObject = NULL;120}121if (m_lpStorage)122{123m_lpStorage->Release();124m_lpStorage = NULL;125}126127SysFreeString(path);128}129catch( _com_error e )130{131AfxMessageBox(e.ErrorMessage());132::CoUninitialize();133}
That 's all.
感谢大家的关注。其实实现代码就这么多了。一句不少!
如果大家是再不能实现,我交付了代码后就会给大家完整的工程的。
:)





原创粉丝点击