CRichEditCtrl实现MSN/QQ动画表情

  • 来源: knowsky  作者: 若水   2008-04-09/14:21
  • 首先对标题说明一下,在MSN中,聊天的窗口可能是一个自定义的类。大家用Spy ++可以看看。对与自定义窗口,可以使用CreateWindow, SetWindowLong或者是SubclassWindow实现,不过这不是我现在讨论的话题。 好, 先看看效果再说:
    CRichEditCtrl实现MSN/QQ动画表情


    关于这个的实现。我们首先应该明了,我们必须实现一个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

    评论 {{userinfo.comments}}

    {{money}}

    {{question.question}}

    A {{question.A}}
    B {{question.B}}
    C {{question.C}}
    D {{question.D}}
    提交

    驱动号 更多