天天看點

在richedit控件中插入動态GIF

這是CSDN的VC論壇上的一個老FAQ了。我在寫自定義在RichEdit中插入對象的圖示(http://www.blogcn.com/user3/jiangsheng/blog/1319738.html)這片文章的時候就是想用這個技術做動畫GIF的,但是怎麼判斷一個内嵌在RichEdit的對象是GIF這個問題一直沒有解決。好在QQ附帶的一個控件支援動畫GIF,可以插入這個對象來解決問題。

首先需要一個定時器來定時更新GIF。

public: System::Void OnLoad(System::Object^  sender, System::EventArgs^  e)

   {

   this->typingRichTextBox->RichTextShortcutsEnabled=false;

   this->timer1->Start();

   }

private: System::Void OnFormClosing(System::Object^  sender, System::Windows::Forms::FormClosingEventArgs^  e)

   {

    this->timer1->Stop();

    this->frameClosing=true;

   }

然後在定時器的處理函數裡面通知GIF控件更新顯示。

private: System::Void OnTimer1Elapsed(System::Object^  sender, System::Timers::ElapsedEventArgs^  e)

   {

    if(this->frameClosing==false)

    UnmanagedGifTriggerFrameChange(this->contentRichTextBox->Handle.ToInt32());

   }

最後的工作就是插入GIF了

private: System::Void smileToolStripMenuItem_Click(System::Object^  sender, System::EventArgs^  e)

   {

    insertemotion(sender,e,"c://Program Files//Tencent//QQ//Face//20.gif");

   }

private: System::Void insertemotion(System::Object^  sender, System::EventArgs^  e,String ^ gifPath)

   {

   System::Windows::Forms::RichTextBox^  ptypingRichTextBox=this->typingRichTextBox;

   stdcli::language::pin_ptr< wchar_t> wch = PtrToStringChars(gifPath);

   UnmanagedInsertGif(ptypingRichTextBox->Handle.ToInt32(),wch );

   }

為了偷懶起見關鍵的代碼還是用Native C++來寫,是以這些函數名全部以Unmanaged開頭。

//unmanagedwin32.h

//混合托管和非托管程式設計的話,編譯的時候不能使用/clr:safe和/clr:pure開關,

#pragma once

#pragma unmanaged//用這個開關來切換托管和非托管代碼

extern void UnmanagedScrollToButton(int hwndRichEdit);

extern void UnmanagedInsertGif(int hwndRichEdit,wchar_t * pFilePath);

extern void UnmanagedGifTriggerFrameChange(int hwndRichEdit);

#pragma managed

//unmanagedwin32.cc

//使用了Windows 平台SDK

//必須要使用clr:oldSyntax來避免SDK頭檔案和C++/CLI文法的沖突

//而且在Visual C++ 2005 Express中要添加/d1PrivateNativeTypes 開關以避免混合LNK2022錯誤。

#define UNICODE

#define _UNICODE

#define  _WIN32_DCOM

#include "windows.h"

#include "Richedit.h"

#include "Richole.h"

#pragma comment( lib, "User32.lib" )

#include "UnmanagedWin32.h"

//純用C編寫自動化操作會死人的,幸好可以自動導入

#import "c://Program files//tencent//qq//ImageOle.dll" named_guids

//移動光标到末尾然後調用System::Windows::Forms::RichTextBox的ScrollToCaret方法,需要拖動滾動條才可以看到最後一行之前的文字。老辦法,API伺候。

void UnmanagedScrollToButton(int hwndRichEdit)

{

 HWND h=(HWND)hwndRichEdit;

 int line = SendMessage(h, EM_GETFIRSTVISIBLELINE, 0, 0);

 int linecount = SendMessage(h, EM_GETLINECOUNT, 0, 0);

 SendMessage(h, EM_LINESCROLL, 0, (linecount - line - 2));

}

下面的函數UnmanagedInsertGif(int hwndRichEdit,wchar_t * pFilePath)是插入一個ImageOle::GifAnimator對象,UnmanagedGifTriggerFrameChange(int hwndRichEdit)是枚舉richedit中已經插入的對象,如果是ImageOle::GifAnimator對象,那麼調用其TriggerFrameChange方法。

void UnmanagedInsertGif(int hwndRichEdit,wchar_t * pFilePath)

{

HWND h=(HWND)hwndRichEdit;

LPRICHEDITOLE lpRichEditOle=NULL;

LPOLEOBJECT lpObject=NULL;

LPSTORAGE lpStorage=NULL;

LPOLECLIENTSITE lpClientSite=NULL;

LPLOCKBYTES lpLockBytes = NULL;

REOBJECT reobject;

ZeroMemory(&reobject, sizeof(REOBJECT));

reobject.cbStruct = sizeof(REOBJECT);

HRESULT hr=S_OK;

CLSID clsid=CLSID_NULL;

do{

::SendMessage(h, EM_GETOLEINTERFACE, 0, (LPARAM)&lpRichEditOle);

if(lpRichEditOle==NULL)break;

hr= ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);

if (hr != S_OK||lpLockBytes==NULL) break;

hr= ::StgCreateDocfileOnILockBytes(lpLockBytes,

STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &lpStorage);

if (hr!= S_OK||lpStorage==NULL) break;

hr=lpRichEditOle->GetClientSite(&lpClientSite);

if (hr!= S_OK||lpClientSite==NULL) break;

try

{

ImageOleLib::IGifAnimatorPtr lpAnimator;

hr = lpAnimator.CreateInstance(ImageOleLib::CLSID_GifAnimator);

if( FAILED(hr) ) _com_issue_error(hr);

_bstr_t bstrPath(pFilePath);

hr = lpAnimator->LoadFromFile(bstrPath);

if( FAILED(hr) ) _com_issue_error(hr);

hr = lpAnimator.QueryInterface(IID_IOleObject, (void**)&lpObject);

if( FAILED(hr)||lpObject==NULL) _com_issue_error(hr);

hr=OleSetContainedObject(lpObject, TRUE);

if( FAILED(hr) ) _com_issue_error(hr);

hr=lpObject->GetUserClassID(&clsid);

if( FAILED(hr) ) _com_issue_error(hr);

reobject.clsid = clsid;

reobject.cp = REO_CP_SELECTION;

reobject.dvaspect = DVASPECT_CONTENT;

reobject.dwFlags = REO_BELOWBASELINE;

reobject.dwUser = 0;

reobject.poleobj = lpObject;

reobject.polesite = lpClientSite;

reobject.pstg = lpStorage;

SIZEL sizel={0,0};

reobject.sizel = sizel;

hr=lpRichEditOle->InsertObject(&reobject);

}

catch( _com_error e )

{

LPCTSTR lpszErrMessage=e.ErrorMessage();

}

}while(FALSE);

if(lpLockBytes)

lpObject->Release();

if(lpLockBytes)

lpLockBytes->Release();

if(lpClientSite)

lpClientSite->Release();

if(lpRichEditOle)

lpRichEditOle->Release();

}

void UnmanagedGifTriggerFrameChange(int hwndRichEdit)

{

HWND h=(HWND)hwndRichEdit;

LPRICHEDITOLE lpRichEditOle=NULL;

LPOLECLIENTSITE lpClientSite=NULL;

LPOLECONTAINER lpContainer=NULL;

LPENUMUNKNOWN lpEnumUnknown=NULL;

HRESULT hr=S_OK;

do{

::SendMessage(h, EM_GETOLEINTERFACE, 0, (LPARAM)&lpRichEditOle);

if(lpRichEditOle==NULL)break;

hr=lpRichEditOle->GetClientSite(&lpClientSite);

if (hr!= S_OK||lpClientSite==NULL) break;

hr=lpClientSite->GetContainer(&lpContainer);

if (hr!= S_OK||lpClientSite==NULL) break;

hr=lpContainer->EnumObjects(OLECONTF_EMBEDDINGS,&lpEnumUnknown);

if (hr!= S_OK||lpEnumUnknown==NULL) break;

IUnknown* pUnk=NULL;

ULONG uFetched=0;

for (UINT i = 0; S_OK == lpEnumUnknown->Next(1, &pUnk, &uFetched); i++)

{

ImageOleLib::IGifAnimator* pAnimator=NULL;

do{

hr=pUnk->QueryInterface(__uuidof(ImageOleLib::IGifAnimator),(LPVOID*)&pAnimator);

if (hr!= S_OK) break;

try{

ImageOleLib::IGifAnimatorPtr lpAnimator;

lpAnimator.Attach(pAnimator,true);

lpAnimator->TriggerFrameChange();

}

catch( _com_error e )

{

LPCTSTR lpszErrMessage=e.ErrorMessage();

}

}

while(FALSE);

pUnk->Release();

if(pAnimator)

pAnimator->Release();

}

}while(FALSE);

if(lpEnumUnknown)

lpEnumUnknown->Release();

if(lpContainer)

lpContainer->Release();

if(lpRichEditOle)

lpRichEditOle->Release();

if(lpClientSite)

lpClientSite->Release();

}

這兩個函數裡面的方法也可以用于插入其他類型控件,以及和插入的對象通訊。

使用windows 2000,QQ2004SP1,Visual C++ 2005 Express, Platform SDK (Windows 2003)編譯測試。

繼續閱讀