CRichEditCtrl实现MSN/QQ动画表情
来源:
knowsky
作者:
若水
2008-04-09/14:21
首先对标题说明一下,在MSN中,聊天的窗口可能是一个自定义的类。大家用Spy ++可以看看。对与自定义窗口,可以使用CreateWindow, SetWindowLong或者是SubclassWindow实现,不过这不是我现在讨论的话题。 好, 先看看效果再说:
关于这个的实现。我们首先应该明了,我们必须实现一个OLE对象。而且这个对象能够播放GIF.对于播放GIF,代码已经很多了。
有了这个就完了么?当然不是。你还有写一个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
#import "D:\\Program files\\tencent\\qq\\ImageOle.dll" named_guidsnamed_guids 表示让编译器为我把对应库的GUID和声明对应起来。我们就可以用CLSID_GifAnimator引用对应的接口了。不用那一长串的东西。然后它就会为我们生成两个文件。
更多内容请看MSN图像 MSN专题 QQ表情专题,或
ImageOle.tlh
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
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#p#分页标题#e#
79} // namespace ImageOleLib
80#pragma pack(pop)
更多内容请看MSN图像 MSN专题 QQ表情专题,或
ImageOle.tli
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}
有了这些,你使用接口和函数就很方便了。
然后就这样:
WINOLEAPI CoInitializeEx(LPVOID pvReserved, DWord dwCoInit)
为什么要这样呢?因为我们使用这个函数。那有必要这样么?在MSDN是说要 #define _DCOM_ 就行了。可惜我没有成功。就只好这样了。
接着就是实现代码了。现在看应该没有很大的问题。假如还是很难明白。那么我你得好好研究CRichEditCtrl和OLE了。听说Inside OLE和Inside COM很好。我没有弄到。就看了《COM+编程指南》和潘爱民的《COM原理和应用》,不错!还有一个好东西。就是 ActiveX Control Test Container 的源代码。
更多内容请看MSN图像 MSN专题 QQ表情专题,或
最后该出场的就是实现代码了。
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_EXCLUSIVESTGM_CREATESTGM_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) )#p#分页标题#e#
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 }
更多内容请看MSN图像 MSN专题 QQ表情专题,或
附:Delphi版的实现
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ActiveX, ComCtrls, RxRichEd, ImageOleLib_TLB;
//RxRichEd单元是Rxlib下的RxRichEdit,一套增强功能的RichEdit
//ImageOleLib_TLB是从qq的ImageOle.dll引入的类型库
const
IID_IOleObject: TGUID = (
D1: $00000112; D2: $0000; D3: $0000; D4: ($C0, $00, $00, $00, $00, $00, $00,
$46));
EM_GETOLEINTERFACE= WM_USER + 60;
type
TForm1 = class(TForm)
Button1: TButton;
Editor: TRxRichEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
FRTF: IRichEditOle;
FLockBytes: ILockBytes;
FStorage: ISTORAGE;
FClientSite: IOLECLIENTSITE;
m_lpObject: IOleObject;
m_lpAnimator: TGifAnimator;
i_GifAnimator: IGifAnimator;
reobject: TReObject;
clsid: TGuid;
sizel: tagSize;
dwStart, dwEnd: DWORD;
Rect:TRect;
begin
try
if CreateILockBytesOnHGlobal(0, True, FLockBytes) <> S_OK then
begin
showmessage('Error to create Global Heap');
exit;
end;
//建立一个混合文档存取对象
if StgCreateDocfileOnILockBytes(FLockBytes, STGM_SHARE_EXCLUSIVE or
STGM_CREATE or STGM_READWRITE, 0, FStorage) <> S_OK then
begin
Showmessage('Error to create storage');
exit;
end;
//取得RichEdit的接口
Sendmessage(Editor.handle,EM_GETOLEINTERFACE,0,LongInt(@FRTF));
if FRTF.GetClientSite(FClientSite)<>S_OK then
begin
ShowMessage('Error to get ClentSite');
Exit;
end;
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
m_lpAnimator := TGifAnimator.Create(Self);
i_GifAnimator := m_lpAnimator.ControlInterface;
i_GifAnimator.LoadFromFile('c:\ti.gif');#p#分页标题#e#
i_GifAnimator.QueryInterface(IID_IOleObject, m_lpObject);
OleSetContainedObject(m_lpObject, True);
FillChar(ReObject, SizeOf(ReObject), 0);
ReObject.cbStruct := SizeOf(ReObject);
m_lpObject.GetUserClassID(clsid);
ReObject.clsid := clsid;
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 := FClientSite;
//the storage
reobject.pstg := FStorage;
sizel.cx := 0;
sizel.cy := 0;
reobject.sizel := sizel;
//Sel all text
SendMessage(Editor.Handle, EM_SETSEL, 0, -1);
SendMessage(Editor.Handle, EM_GETSEL, dwStart, dwEnd);
SendMessage(Editor.Handle, EM_SETSEL, dwEnd + 1, dwEnd + 1);
//Insert after the line of text
FRTF.InsertObject(reobject);
SendMessage(Editor.Handle, EM_SCROLLCARET, 0, 0);
//VARIANT_BOOL ret;
//do frame changing
m_lpAnimator.TriggerFrameChange();
//show it
m_lpObject.DoVerb(OLEIVERB_UIACTIVATE, Nil, FClientSite, 0, Editor.Handle,Rect);
// m_lpObject.DoVerb(
m_lpObject.DoVerb(OLEIVERB_SHOW, Nil, FClientSite, 0, Editor.Handle, Rect);
//redraw the window to show animation
redrawwindow(Handle, nil, 0, RDW_ERASE or RDW_INVALIDATE or RDW_FRAME or RDW_ERASENOW or RDW_ALLCHILDREN);
finally
FRTF:=nil;
FClientSite := nil;
FStorage :=nil;
end;
end;
end
{{cmoun}}人已赞