Movatterモバイル変換


[0]ホーム

URL:


Wayback Machine
52 captures
25 Jan 2008 - 14 Jun 2025
OctDECMar
Previous capture31Next capture
201020112013
success
fail
COLLECTED BY
Organization:Alexa Crawls
Starting in 1996,Alexa Internet has been donating their crawl data to the Internet Archive. Flowing in every day, these data are added to theWayback Machine after an embargo period.
Collection:Alexa Crawls
Starting in 1996,Alexa Internet has been donating their crawl data to the Internet Archive. Flowing in every day, these data are added to theWayback Machine after an embargo period.
TIMESTAMPS
loading
The Wayback Machine - https://web.archive.org/web/20111231051214/http://www.codeproject.com:80/KB/edit/COleRichEditCtrl.aspx
Click here to Skip to main content
8,374,081 members and growing!
EmailPassword Lost password?
Home
Search within:




Licence 
First Posted 9 Feb 2005
Views 189,490
Downloads 7,305
Bookmarked 129 times

A Rich Edit Control That Displays Bitmaps and Other OLE Objects

ByMike O'Neill | 9 Feb 2005
COleRichEditCtrl will display RTF text as well as bitmaps, video clips, Word, Excel and PowerPoint documents, and any other kind of OLE objects.
 
See Also
Print Article
add
Add to your CodeProject bookmarks
Discuss
Discuss this article
71
  4.88 (57 votes)
2 votes, 3.5%
1

2
1 vote, 1.8%
3
2 votes, 3.5%
4
52 votes, 91.2%
5
4.88/5 - 57 votes
3 removed
μ 4.68, σa 1.40 [?]
Sponsored Links

Bitmaps and other OLE objects displayed in a rich edit control

Contents

Introduction

I needed a rich edit control that could display bitmaps. Searching around the Web, I could see that others have faced the same need. For me, I wanted a "light-weight" Help functionality that I could embed as an RTF resource and display from my application, so that I did not need a full-blown Help companion. And what good is a Help file without screenshots of your application -- hence the need for bitmaps in a rich edit control.

That same searching around the Web also convinced me that the solution was not easy to come by. By chance, I found the hint that made it all work, in acomment to an article (i.e., not the article itself) posted by Stephane Lesagehere which he titled "Getting Images with a StreamIn/ClipBoard/Drag'n'Drop operation":

Quote from Mr. Lesage:

"If you want object insertion operations to work in your RichEdit control, you have to supply anIRichEditOleCallback interface and implement theGetNewStorage method."

OLE was the needed hint, and informed with code suggested by Mr. Lesage, I was able to write theCOleRichEditCtrl class, which is derived from MFC'sCRichEditCtrl class.

top

The COleRichEditCtrl Class

The code for the class is actually straightforward. Inside the header, I defined a nested class (actually, it's not really aclass; it's aninterface) derived fromIRichEditOleCallback, which is documented atMSDN. The most significant method implemented in this nested class isGetNewStorage which provides storage for a new object pasted from the clipboard or read in from an RTF stream. Here's the header for classCOleRichEditCtrl, with most of the ClassWizard boilerplate deleted for simplification:

#include<richole.h>/////////////////////////////////////////////////////////////////////////////// COleRichEditCtrl windowclass COleRichEditCtrl :public CRichEditCtrl{// Constructionpublic:    COleRichEditCtrl();virtual ~COleRichEditCtrl();long StreamInFromResource(int iRes, LPCTSTR sType);protected:static DWORD CALLBACK readFunction(DWORD dwCookie,         LPBYTE lpBuf,// the buffer to fill         LONG nCount,// number of bytes to read         LONG* nRead);// number of bytes actually readinterface IExRichEditOleCallback;// forward declaration (see below in this header file)    IExRichEditOleCallback* m_pIRichEditOleCallback;    BOOL m_bCallbackSet;interface IExRichEditOleCallback :public IRichEditOleCallback    {public:        IExRichEditOleCallback();virtual ~IExRichEditOleCallback();int m_iNumStorages;        IStorage* pStorage;        DWORD m_dwRef;virtual HRESULT STDMETHODCALLTYPE GetNewStorage(LPSTORAGE* lplpstg);virtual HRESULT STDMETHODCALLTYPE                 QueryInterface(REFIID iid,void ** ppvObject);virtual ULONG STDMETHODCALLTYPE AddRef();virtual ULONG STDMETHODCALLTYPE Release();virtual HRESULT STDMETHODCALLTYPE                 GetInPlaceContext(LPOLEINPLACEFRAME FAR *lplpFrame,                 LPOLEINPLACEUIWINDOW FAR *lplpDoc,                 LPOLEINPLACEFRAMEINFO lpFrameInfo);virtual HRESULT STDMETHODCALLTYPE ShowContainerUI(BOOL fShow);virtual HRESULT STDMETHODCALLTYPE                  QueryInsertObject(LPCLSID lpclsid, LPSTORAGE lpstg, LONG cp);virtual HRESULT STDMETHODCALLTYPE DeleteObject(LPOLEOBJECT lpoleobj);virtual HRESULT STDMETHODCALLTYPE                  QueryAcceptData(LPDATAOBJECT lpdataobj, CLIPFORMAT FAR *lpcfFormat,                  DWORD reco, BOOL fReally, HGLOBAL hMetaPict);virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);virtual HRESULT STDMETHODCALLTYPE                  GetClipboardData(CHARRANGE FAR *lpchrg,                  DWORD reco, LPDATAOBJECT FAR *lplpdataobj);virtual HRESULT STDMETHODCALLTYPE                  GetDragDropEffect(BOOL fDrag,                  DWORD grfKeyState, LPDWORD pdwEffect);virtual HRESULT STDMETHODCALLTYPE                  GetContextMenu(WORD seltyp, LPOLEOBJECT lpoleobj,                  CHARRANGE FAR *lpchrg, HMENU FAR *lphmenu);    };public:// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(COleRichEditCtrl)protected:virtualvoid PreSubclassWindow();//}}AFX_VIRTUAL// Implementationpublic:// Generated message map functionsprotected://{{AFX_MSG(COleRichEditCtrl)    afx_msgint OnCreate(LPCREATESTRUCT lpCreateStruct);//}}AFX_MSG    DECLARE_MESSAGE_MAP()};

AnIExRichEditOleCallback object is allocated from the heap in theOnCreate handler for the class (not precisely: see below where I describea problem encountered during development). Implementation of itsGetNewStorage method followed a few examples I found elsewhere, and really is a textbook use of various APIs specifically designed for the task:

HRESULT STDMETHODCALLTYPE COleRichEditCtrl::IExRichEditOleCallback::GetNewStorage(LPSTORAGE* lplpstg){    m_iNumStorages++;    WCHAR tName[50];    swprintf(tName, L"REOLEStorage%d", m_iNumStorages);    HRESULT hResult = pStorage->CreateStorage(tName,         STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE ,0,0, lplpstg );if (hResult != S_OK )    {        ::AfxThrowOleException( hResult );    }return hResult;}

Finally, since my purpose was to use the class by streaming in an RTF stream stored as a resource in the executable, I provided aStreamInFromResource member function, together with a statically-scoped callback function used in theEDITSTREAM structure:

long COleRichEditCtrl::StreamInFromResource(int iRes, LPCTSTR sType){    HINSTANCE hInst = AfxGetInstanceHandle();    HRSRC hRsrc = ::FindResource(hInst,        MAKEINTRESOURCE(iRes), sType);        DWORD len = SizeofResource(hInst, hRsrc);BYTE* lpRsrc = (BYTE*)LoadResource(hInst, hRsrc);     ASSERT(lpRsrc);      CMemFile mfile;    mfile.Attach(lpRsrc, len);     EDITSTREAM es;    es.pfnCallback = readFunction;    es.dwError =0;    es.dwCookie = (DWORD) &mfile;return StreamIn( SF_RTF, es );}/* static */DWORD CALLBACK COleRichEditCtrl::readFunction(DWORD dwCookie,         LPBYTE lpBuf,// the buffer to fill         LONG nCount,// number of bytes to read         LONG* nRead)// number of bytes actually read{    CFile* fp = (CFile *)dwCookie;    *nRead = fp->Read(lpBuf,nCount);return0;}

One amazing (to me) benefit from this architecture is that I got a tremendous "bang for the buck". When I set out, my only goal was to display a bitmap, yet I ended up with a control that could displayany OLE object. Compound documents that contained completely arbitrary objects work just fine: bitmaps, video and audio clips, Office documents (Word, Excel, PowerPoint), and the like. Any other content could also be contained (like PDF files and HTML files), and the content would be launched by double-clicking on the content's icon, but there will not be an in-place display of those objects unless the OLE server application were written and configured for in-place OLE display.

top

The Demo Program

The demo program is a pathetic shell of an MFC SDI doc/view application, whose only purpose in life is to launch a dialog that contains aCOleRichEditCtrl. Under the assumption that the dialog contains Help-type information, a left-click will launch the dialog modelessly, so that the user can still interact with the main application. The code for the dialog uses a simple technique involving aBOOL flag that's tested inOnPostNcDestroy so as to delete the memory allocated for the dialog object; but since that's not the point of this article, you'll need to look at the code for theCRichEditHelpDialog class if you're interested. A right-click will launch the dialog in the more-familiar modal mode, so that you can see the difference.

Run the program and click anywhere in the view to launch the Help dialog with itsCOleRichEditCtrl. The contents of the control are streamed in from an RTF resource that's part of the executable; the contents include standard RTF text, a bitmap, a video clip, an Excel spreadsheet, and a PowerPoint presentation. If you can't see one or more of these objects, then you probably do not have the associated program installed.

top

A Problem During The Development of the COleRichEditCtrl Class

I thought I was finished with the development of theCOleRichEditCtrl class, and I was nearly finished writing this article, when I ran into a stumbling block that sent me back to the coding table. You can skip this part if you want, and proceed directly tousing the class in your project, by clickinghere.

The problem is rooted in the fact that the class wraps a control. Because it's a control, the class must expect that its Windows window will be created in either of two different ways: by an explicit call to::CreateWindow (or::CreateWindowEx, both of which are wrapped by theCWnd::Create member of allCWnd-derived classes), or automatically by Windows during a call to::CreateDialogParam which builds a dialog based on a template defined in the program's resources. The latter is more common (and in fact is the way that the next section describes the use of the class), but the former is also used often (and in fact is the method used in the demo project).

To implement OLE functionality, the control needs the OLE callback function very early on, before almost anything else was done. When I initially wrote the wrapper class, I therefore put the call toSetOLECallback in theCOleRichEditCtrl::OnCreate handler, since this handler is called in response to one of the very-first messages sent by Windows to the control. That was a silly oversight, and I should have known better, since it only handles the first method of window creation (i.e., through::CreateWindow) and not the second method (i.e., from a dialog resource template).

The correct way to accommodate both methods of window creation is well-known: put this kind of early initialization inCWnd::PreSubclassWindow. ThePreSubclassWindow function is a virtual function that's called by the MFC framework just before the Windows window is subclassed to the C++CWnd object, and it's called regardless of whether the window is created by the first or second method. Paul Dilascia discusses this in significantly more detail in his C++ Q&A column from the March 2002 issue of MSDN Magazine, seeMSDN. So, I moved all theSetOLECallback code into a newCOleRichEditCtrl::PreSubclassWindow handler.

And this is where I encountered the real problem. For although creation via dialog template now worked great, creation viaCreateWindow did not. The call toSetOLECallback returned an error code signifying that the call failed, and indeed OLE capabilities were broken. I traced through the code but was unable to find the reason for the failure, and diligent searching of the web turned up nothing.

I figured that the problem was related to a too-soon call toSetOLECallback, reasoning that whenSetOLECallback was called inPreSubclassWindow after creation via a dialog template, the Windows window for the rich edit control was ready to accept messages, whereas after creation byCreateWindow, it might not yet be ready. So I looked for ways to delay the call toSetOLECallback. I looked for other functions called early by the MFC framework (and found none), and considered installing anOnNcCreate handler for theWM_NCCREATE message (which is actually sent before theWM_CREATE message) or PostMessage'ing my own custom message. None of these really seemed right.

In the end, I added aBOOL flag to the class to capture the result of the call toSetOLECallback fromPreSubclassWindow. Then, I maintained theOnCreate handler, and inside it, I tested the flag to see ifSetOLECallback had successfully been called already from insidePreSubclassWindow. If not, I simply called it again. Here's the code:

int COleRichEditCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct){if (CRichEditCtrl::OnCreate(lpCreateStruct) == -1)return -1;// m_pIRichEditOleCallback should have been created in PreSubclassWindow     ASSERT( m_pIRichEditOleCallback != NULL );// set the IExRichEditOleCallback pointer if it wasn't set// successfully in PreSubclassWindowif ( !m_bCallbackSet )    {        SetOLECallback( m_pIRichEditOleCallback );    }return0;}void COleRichEditCtrl::PreSubclassWindow() {// base class first    CRichEditCtrl::PreSubclassWindow();        m_pIRichEditOleCallback = NULL;    m_pIRichEditOleCallback =new IExRichEditOleCallback;    ASSERT( m_pIRichEditOleCallback != NULL );    m_bCallbackSet = SetOLECallback( m_pIRichEditOleCallback );}

I'm not totally satisfied with the solution or the explanation of the problem, but it works. If anyone has seen this kind of behavior before, and knows what causes it or has another solution, please let us know.

top

How To Use The COleRichEditCtrl Control In Your Project

These instructions are for use with VC++ version 6.0, but it should be easy to use them with other versions like .NET.

To use the control in your project, download the source and header files (i.e.,COleRichEditCtrl.cpp andCOleRichEditCtrl.h) to a convenient folder and then include both of them in your project ("Project"->"Add To Project"->"Files...").

Create your dialog resource template and add a standard rich edit control using the control toolbar:

Using the control toolbar to add a rich edit control

Open the "Properties" window for the just-added rich edit control, and under the "Styles" tab, select "Multi-line", "Vertical scroll" and "Want return", and de-select "Auto H-scroll". These are typical styles for most likely uses of the control, but you might want to play with them if you're not totally pleased with the result. For example, you might also want the "Read-only" style:

Setting styles of the rich edit

Now, we will add a member variable of typeCOleRichEditCtrl to your dialog. (Seefootnote 1.) Open ClassWizard and select the class that corresponds to your dialog. Then, add a "control"-style variable of typeCRichEditCtrl, which is the base class forCOleRichEditCtrl. You should see something like this screenshot:

Screenshot of ClassWizard, showing how to add a member variable of type CRichEditCtrl

Click "OK" everywhere to exit out of ClassWizard, and then manually edit your dialog class to substitute the real target class,COleRichEditCtrl. Here's how.

First, open the header file for your dialog class, and add#include"OleRichEditCtrl.h" at the top. If you added theCOleRichEditCtrl.cpp andCOleRichEditCtrl.h files to a folder that's different from that of your main project, you'll need to give the folder name too, like so:#include"../components/OleRichEditCtrl.h". Then, to use the OLE rich edit control instead of the standard rich edit control, page down in the header file until you see a line like:

CRichEditCtrl    m_ctlRichEdit;

and replace it with the following:

COleRichEditCtrl    m_ctlRichEdit;

Before you build and run your program, youmust be certain that it callsAfxInitRichEdit() somewhere, usually in theCWinApp::InitInstance() call. If you did not choose "Compound Document Support" when creating your application with the AppWizard, then you must edit your code manually to insert a call toAfxInitRichEdit(). Do it now. Now build and run your program.

To include RTF text as part of your program's resources, use a "bare-bones" RTF editor like standard WordPad to create your document. (I recommend simple RTF editors like WordPad because more complex editors like Word often insert large amounts of needless and confusing tags into the RTF stream.) Save the document in RTF format and then move acopy of it into the/res folder of your project. I suggest a copy instead of a direct save, since Visual Studio has a nasty habit of erasing the contents of customized resources if you're not careful (which is very frustrating, believe me). Let's say that the name of the file istext.rtf. Go to the "Resources" tab of the ClassView, right-click the project, and select to "Import" a resource from the pop-up menu:

Importing a custom resource

Select yourtext.rtf file and then define an alphabetic "type" for your custom resource. I tend to use something obvious, like "RTF_TEXT", as shown in this screenshot:

Defining the alphabetic type of the resource

Now, to stream in this resource when the dialog opens, simply use theCOleRichEditCtrl::StreamInFromResource function. In your dialog'sOnInitDialog() function, simply add the following line of code:

m_ctlRichEdit.StreamInFromResource( IDR_RTF_TEXT1,"RTF_TEXT" );

That's it: build and run your program. You might need to do a "Rebuild All" to get your custom resource built into your executable, since Visual Studio is not very good at detecting when a non-standard resource (like your "RTF_TEXT" resource) has been added into a project.

top

Some Final Words

Here are a few practical suggestions if you run into trouble using the class.

  1. If your program worked fine before addingCOleRichEditCtrl to it, but then doesn't seem to work at all afterwards, then you probably need to insert a call toAfxInitRichEdit(). The best place for this is inside your application'sCMyApp::InitInstance function.
  2. If you added a customized "RTF_TEXT" resource like the fictitioustest.rtf file mentioned above, but after building, you can't see the contents of the resource in the control, then you probably need to do a "Rebuild All". Visual Studio is not very good at detecting when a non-standard resource (like your "RTF_TEXT" resource) has been added into a project.
  3. I noticed some odd behavior in the demo program, in which the rich edit control doesn't seem to erase and repaint itself properly if you give it a particular sequence of re-sizings and scrolls. In the demo, the dialog has a re-sizing border and can be re-sized in the conventional way by dragging on the border. The control itself also has a vertical scroll bar for vertical scrolling of the contents. If you launch the dialog and then re-size the dialog before touching the scroll bar, then you'll see the odd behavior, particularly if you re-size to a narrow width and then make the dialog wider again (this makes the control calculate new line-break positions). As soon as you touch the scroll bar at least once, then all is good from there on out. In other words, once you scroll once, then you can re-size all you want, and the control will erase and repaint itself perfectly. I don't know the reason for this behavior, and nothing turned up in my web searches. If anyone has a fix for this, then please let us know.

top

Version and Revision History

  • February 4, 2005 - First release.

top

Bibliography

Here, in one place, is a list of all the articles and links mentioned in the article:

top

Footnotes

  1. At this point, you really should be able to use ClassWizard to add a variable of typeCOleRichEditCtrl directly, without the steps mentioned in the main text. However, I have found that the ClassWizard has problems enrolling the new class in its database. You can try the following procedure to force ClassWizard to re-build its database, but even though this has worked for me in the past with other controls, I just tried it and for some reason it didn't work. The procedure in the main text above always works, but in case this alternative procedure works for you, here it is: the class database is stored in a file whose extension is ".clw" in your project's folder. To force ClassWizard to re-build the class database, open your project's workspace with Explorer and find the file whose extension is ".clw". Delete it. (Trust me, but if you don't, rename it to an extension like ".clw~1".) Now, open ClassWizard, and you'll get a message saying that the ".clw" file doesn't exist, and asking if you would like to re-build it from your source files. Of course you should select "Yes". From the resulting dialog, select "Add All". In addition, make certain that you also addCOleRichEditCtrl.cpp andCOleRichEditCtrl.h from whatever folder you stored them to.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be foundhere

About the Author

Mike O'Neill



United States United States

Member
Mike O'Neill is a patent attorney in Southern California, where he specializes in computer and software-related patents. He programs as a hobby, and in a vain attempt to keep up with and understand the technology of his clients.

loading...
Sign Up to vote  PoorExcellent
Add a reason or comment to your vote:x
Votes of 3 or less require a comment

Comments and Discussions

 
 RefreshFirstPrevNext
GeneralHow do i handle NM_CLICK Eventmembervss1113:22 28 Apr '11  
GeneralFinal Word 3 - Not Erasing and Repainting within bounds of CTRL - SolutionmemberPhil Outram23:53 26 May '10  
QuestionLicense terms?membermark gooding0:35 9 Nov '09  
GeneralBUG: Resizing help dialog doesn't redraw rich edit controlmemberols60009:19 3 Jul '09  
GeneralUnable to estimate print sizemembermhorowit16:00 29 Apr '09  
Questionobjects copy?? [modified]memberLeeWonJong20:04 8 Feb '09  
QuestionScrollbar down, Add new RTF - no visiblememberpajero_pn2:04 8 Feb '09  
GeneralFree Memory problemmemberMember 39838884:50 22 Dec '08  
Generalload rtf Memory have a problemmemberjackjons21:44 18 Dec '08  
Generalload rtf Memory have a problemmemberjackjons21:43 18 Dec '08  
Last Visit: 19:00 31 Dec '99     Last Update: 19:12 30 Dec '1112345678Next »

General General   News News   Suggestion Suggestion   Question Question   Bug Bug   Answer Answer   Joke Joke   Rant Rant   Admin Admin   

Permalink |Advertise |Privacy |Mobile
Web01 |2.5.111208.1 |Last Updated 10 Feb 2005
Article Copyright 2005 by Mike O'Neill
Everything elseCopyright ©CodeProject, 1999-2011
Terms of Use
Layout:fixed|fluid

See Also...
The Daily Insider

[8]ページ先頭

©2009-2025 Movatter.jp