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
<script type="text/javascript"> function ToggleSourceCodeRegion(regionNumber) { var divRegion = document.getElementById('region' + regionNumber); var divRegionBlock = document.getElementById('regionBlock' + regionNumber); if (divRegion.style.display == 'inline') { divRegion.style.display = 'none'; divRegionBlock.style.display = 'inline'; } else { divRegion.style.display = 'inline'; divRegionBlock.style.display = 'none'; } } </script>
1// Created by Microsoft (R) C/C++ Compiler Version 12.00.8168.0 (9de7951a). 2// 3// d:/myproject/msger/debug/ImageOle.tlh 4// 5// C++ source equivalent of Win32 type library D://Program files//tencent//qq//ImageOle.dll 6// compiler-generated file created 10/25/04 at 22:00:58 - DO NOT EDIT! 7#pragma once 8#pragma pack(push, 8) 9#include <comdef.h> 10 11namespace ImageOleLib { 12 13// 14// Forward references and typedefs 15// 16 17struct /* coclass */ GifAnimator; 18struct __declspec(uuid("0c1cf2df-05a3-4fef-8cd4-f5cfc4355a16")) 19/* dual interface */ IGifAnimator; 20 21// 22// Smart pointer typedef declarations 23// 24 25_COM_SMARTPTR_TYPEDEF(IGifAnimator, __uuidof(IGifAnimator)); 26 27// 28// Type library items 29// 30 31struct __declspec(uuid("06ada938-0fb0-4bc0-b19b-0a38ab17f182")) 32GifAnimator; 33 // [ default ] interface IGifAnimator 34 35struct __declspec(uuid("0c1cf2df-05a3-4fef-8cd4-f5cfc4355a16")) 36IGifAnimator : IDispatch 37{ 38 // 39 // Wrapper methods for error-handling 40 // 41 42 HRESULT LoadFromFile ( 43 _bstr_t FileName ); 44 VARIANT_BOOL TriggerFrameChange ( ); 45 _bstr_t GetFilePath ( ); 46 HRESULT ShowText ( 47 _bstr_t Text ); 48 49 // 50 // Raw methods provided by interface 51 // 52 53 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}; 62 63// 64// Named GUID constants initializations 65// 66 67extern "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}}; 73 74// 75// Wrapper method implementations 76// 77#include "d:/myproject/msger/debug/ImageOle.tli" 78 79} // namespace ImageOleLib 80#pragma pack(pop) 81
ImageOle.tli
<script type="text/javascript"> function ToggleSourceCodeRegion(regionNumber) { var divRegion = document.getElementById('region' + regionNumber); var divRegionBlock = document.getElementById('regionBlock' + regionNumber); if (divRegion.style.display == 'inline') { divRegion.style.display = 'none'; divRegionBlock.style.display = 'inline'; } else { divRegion.style.display = 'inline'; divRegionBlock.style.display = 'none'; } } </script>1// Created by Microsoft (R) C/C++ Compiler Version 12.00.8168.0 (9de7951a). 2// 3// d:/myproject/msger/debug/ImageOle.tli 4// 5// Wrapper implementations for Win32 type library D://Program files//tencent//qq//ImageOle.dll 6// compiler-generated file created 10/25/04 at 22:00:58 - DO NOT EDIT! 7#pragma once 8 9// 10// interface IGifAnimator wrapper method implementations 11// 12 13inline 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} 18 19inline 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} 25 26inline _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} 32 33inline 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 ...
<script type="text/javascript"> function ToggleSourceCodeRegion(regionNumber) { var divRegion = document.getElementById('region' + regionNumber); var divRegionBlock = document.getElementById('regionBlock' + regionNumber); if (divRegion.style.display == 'inline') { divRegion.style.display = 'none'; divRegionBlock.style.display = 'inline'; } else { divRegion.style.display = 'inline'; divRegionBlock.style.display = 'none'; } } </script>1 LPLOCKBYTES lpLockBytes = NULL; 2 SCODE sc; 3 HRESULT hr; 4 //print to RichEdit' s IClientSite 5 LPOLECLIENTSITE m_lpClientSite; 6 //A smart point to IAnimator 7 IGifAnimatorPtr m_lpAnimator; 8 //ptr 2 storage 9 LPSTORAGE m_lpStorage; 10 //the object 2 b insert 2 11 LPOLEOBJECT m_lpObject; 12 13 //Create lockbytes 14 sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes); 15 if (sc != S_OK) 16 AfxThrowOleException(sc); 17 ASSERT(lpLockBytes != NULL); 18 19 //use lockbytes to create storage 20 sc = ::StgCreateDocfileOnILockBytes(lpLockBytes, 21 STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &m_lpStorage); 22 if (sc != S_OK) 23 { 24 VERIFY(lpLockBytes->Release() == 0); 25 lpLockBytes = NULL; 26 AfxThrowOleException(sc); 27 } 28 ASSERT(m_lpStorage != NULL); 29 30 //get the ClientSite of the very RichEditCtrl 31 GetIRichEditOle()->GetClientSite(&m_lpClientSite); 32 ASSERT(m_lpClientSite != NULL); 33 34 try 35 { 36 //Initlize COM interface 37 hr = ::CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ); 38 if( FAILED(hr) ) 39 _com_issue_error(hr); 40 41 //Get GifAnimator object 42 //here, I used a smart point, so I do not need to free it 43 hr = m_lpAnimator.CreateInstance(CLSID_GifAnimator); 44 if( FAILED(hr) ) 45 _com_issue_error(hr); 46 //COM operation need BSTR, so get a BSTR 47 BSTR path = strPicPath.AllocSysString(); 48 49 //Load the gif 50 hr = m_lpAnimator->LoadFromFile(path); 51 if( FAILED(hr) ) 52 _com_issue_error(hr); 53 54 TRACE0( m_lpAnimator->GetFilePath() ); 55 56 //get the IOleObject 57 hr = m_lpAnimator.QueryInterface(IID_IOleObject, (void**)&m_lpObject); 58 if( FAILED(hr) ) 59 _com_issue_error(hr); 60 61 //Set it 2 b inserted 62 OleSetContainedObject(m_lpObject, TRUE); 63 64 //2 insert in 2 richedit, you need a struct of REOBJECT 65 REOBJECT reobject; 66 ZeroMemory(&reobject, sizeof(REOBJECT)); 67 68 reobject.cbStruct = sizeof(REOBJECT); 69 CLSID clsid; 70 sc = m_lpObject->GetUserClassID(&clsid); 71 if (sc != S_OK) 72 AfxThrowOleException(sc); 73 //set clsid 74 reobject.clsid = clsid; 75 //can be selected 76 reobject.cp = REO_CP_SELECTION; 77 //content, but not static 78 reobject.dvaspect = DVASPECT_CONTENT; 79 //goes in the same line of text line 80 reobject.dwFlags = REO_BELOWBASELINE; //REO_RESIZABLE | 81 reobject.dwUser = 0; 82 //the very object 83 reobject.poleobj = m_lpObject; 84 //client site contain the object 85 reobject.polesite = m_lpClientSite; 86 //the storage 87 reobject.pstg = m_lpStorage; 88 89 SIZEL sizel; 90 sizel.cx = sizel.cy = 0; 91 reobject.sizel = sizel; 92 HWND hWndRT = this->m_hWnd; 93 //Sel all text 94// ::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 text 99 GetIRichEditOle()->InsertObject(&reobject); 100 ::SendMessage(hWndRT, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0); 101 VARIANT_BOOL ret; 102 //do frame changing 103 ret = m_lpAnimator->TriggerFrameChange(); 104 //show it 105 m_lpObject->DoVerb(OLEIVERB_UIACTIVATE, NULL, m_lpClientSite, 0, m_hWnd, NULL); 106 m_lpObject->DoVerb(OLEIVERB_SHOW, NULL, m_lpClientSite, 0, m_hWnd, NULL); 107 108 //redraw the window to show animation 109 RedrawWindow(); 110 111 if (m_lpClientSite) 112 { 113 m_lpClientSite->Release(); 114 m_lpClientSite = NULL; 115 } 116 if (m_lpObject) 117 { 118 m_lpObject->Release(); 119 m_lpObject = NULL; 120 } 121 if (m_lpStorage) 122 { 123 m_lpStorage->Release(); 124 m_lpStorage = NULL; 125 } 126 127 SysFreeString(path); 128 } 129 catch( _com_error e ) 130 { 131 AfxMessageBox(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.tli 4// 5// Wrapper implementations for Win32 type library D://Program files//tencent//qq//ImageOle.dll 6// compiler-generated file created 10/25/04 at 22:00:58 - DO NOT EDIT! 7#pragma once 8 9// 10// interface IGifAnimator wrapper method implementations 11// 12 13inline 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} 18 19inline 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} 25 26inline _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} 32 33inline 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 ...
<script type="text/javascript"> function ToggleSourceCodeRegion(regionNumber) { var divRegion = document.getElementById('region' + regionNumber); var divRegionBlock = document.getElementById('regionBlock' + regionNumber); if (divRegion.style.display == 'inline') { divRegion.style.display = 'none'; divRegionBlock.style.display = 'inline'; } else { divRegion.style.display = 'inline'; divRegionBlock.style.display = 'none'; } } </script>1 LPLOCKBYTES lpLockBytes = NULL; 2 SCODE sc; 3 HRESULT hr; 4 //print to RichEdit' s IClientSite 5 LPOLECLIENTSITE m_lpClientSite; 6 //A smart point to IAnimator 7 IGifAnimatorPtr m_lpAnimator; 8 //ptr 2 storage 9 LPSTORAGE m_lpStorage; 10 //the object 2 b insert 2 11 LPOLEOBJECT m_lpObject; 12 13 //Create lockbytes 14 sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes); 15 if (sc != S_OK) 16 AfxThrowOleException(sc); 17 ASSERT(lpLockBytes != NULL); 18 19 //use lockbytes to create storage 20 sc = ::StgCreateDocfileOnILockBytes(lpLockBytes, 21 STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &m_lpStorage); 22 if (sc != S_OK) 23 { 24 VERIFY(lpLockBytes->Release() == 0); 25 lpLockBytes = NULL; 26 AfxThrowOleException(sc); 27 } 28 ASSERT(m_lpStorage != NULL); 29 30 //get the ClientSite of the very RichEditCtrl 31 GetIRichEditOle()->GetClientSite(&m_lpClientSite); 32 ASSERT(m_lpClientSite != NULL); 33 34 try 35 { 36 //Initlize COM interface 37 hr = ::CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ); 38 if( FAILED(hr) ) 39 _com_issue_error(hr); 40 41 //Get GifAnimator object 42 //here, I used a smart point, so I do not need to free it 43 hr = m_lpAnimator.CreateInstance(CLSID_GifAnimator); 44 if( FAILED(hr) ) 45 _com_issue_error(hr); 46 //COM operation need BSTR, so get a BSTR 47 BSTR path = strPicPath.AllocSysString(); 48 49 //Load the gif 50 hr = m_lpAnimator->LoadFromFile(path); 51 if( FAILED(hr) ) 52 _com_issue_error(hr); 53 54 TRACE0( m_lpAnimator->GetFilePath() ); 55 56 //get the IOleObject 57 hr = m_lpAnimator.QueryInterface(IID_IOleObject, (void**)&m_lpObject); 58 if( FAILED(hr) ) 59 _com_issue_error(hr); 60 61 //Set it 2 b inserted 62 OleSetContainedObject(m_lpObject, TRUE); 63 64 //2 insert in 2 richedit, you need a struct of REOBJECT 65 REOBJECT reobject; 66 ZeroMemory(&reobject, sizeof(REOBJECT)); 67 68 reobject.cbStruct = sizeof(REOBJECT); 69 CLSID clsid; 70 sc = m_lpObject->GetUserClassID(&clsid); 71 if (sc != S_OK) 72 AfxThrowOleException(sc); 73 //set clsid 74 reobject.clsid = clsid; 75 //can be selected 76 reobject.cp = REO_CP_SELECTION; 77 //content, but not static 78 reobject.dvaspect = DVASPECT_CONTENT; 79 //goes in the same line of text line 80 reobject.dwFlags = REO_BELOWBASELINE; //REO_RESIZABLE | 81 reobject.dwUser = 0; 82 //the very object 83 reobject.poleobj = m_lpObject; 84 //client site contain the object 85 reobject.polesite = m_lpClientSite; 86 //the storage 87 reobject.pstg = m_lpStorage; 88 89 SIZEL sizel; 90 sizel.cx = sizel.cy = 0; 91 reobject.sizel = sizel; 92 HWND hWndRT = this->m_hWnd; 93 //Sel all text 94// ::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 text 99 GetIRichEditOle()->InsertObject(&reobject); 100 ::SendMessage(hWndRT, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0); 101 VARIANT_BOOL ret; 102 //do frame changing 103 ret = m_lpAnimator->TriggerFrameChange(); 104 //show it 105 m_lpObject->DoVerb(OLEIVERB_UIACTIVATE, NULL, m_lpClientSite, 0, m_hWnd, NULL); 106 m_lpObject->DoVerb(OLEIVERB_SHOW, NULL, m_lpClientSite, 0, m_hWnd, NULL); 107 108 //redraw the window to show animation 109 RedrawWindow(); 110 111 if (m_lpClientSite) 112 { 113 m_lpClientSite->Release(); 114 m_lpClientSite = NULL; 115 } 116 if (m_lpObject) 117 { 118 m_lpObject->Release(); 119 m_lpObject = NULL; 120 } 121 if (m_lpStorage) 122 { 123 m_lpStorage->Release(); 124 m_lpStorage = NULL; 125 } 126 127 SysFreeString(path); 128 } 129 catch( _com_error e ) 130 { 131 AfxMessageBox(e.ErrorMessage()); 132 ::CoUninitialize(); 133 }That 's all.
感谢大家的关注。其实实现代码就这么多了。一句不少!
如果大家是再不能实现,我交付了代码后就会给大家完整的工程的。:)