Environment: VC6 SP4, Platform SDK November 2001 recommended
Introduction to the Windowless RichEdit
It is a little-known fact that the RichEdit from version 2.0 onwards comes with a COM interface to drive it. To use this interface, you don't need to create a RichEdit window, so it is perfect for applications that don't need the extra handling required by having just another window. The example given here illustrates this point by providing a class that can draw RTF text anywhere you'd like.
The main COM interfaces involved are ITextHost, ITextServices, and ITextDocument. When you create a windowless RichEdit, you have to provide it with a class that implements ITextHost. This class is usually quite simple, even more so in my example because I just use it for drawing and don't provide editing capabilities. ITextServices is the main interface you use to communicate with RichEdit. It provides the functions for setting, getting, and drawing the text. Moreover, it lets you send messages to the RichEdit that's hidden behind it. So you can still use all the standard messages such as EM_SETCHARFORMAT. ITextDocument is the interface you can use to bypass the message passing. By using it, you can get an ITextSelection interface to the current selection—an ITextRange interface for any range, for example. These in turn let you get ITextFont objects and so on.
ITextDocument is not specific to the windowless RichEdit, though. When you have created a windowed RichEdit, you can use the EM_GETOLEINTERFACE message to retrieve a COM interface to the RichEdit, anyhow. This interface can be queried for ITextDocument and you will be able to use all its functionality.
This is where it gets a little complicated. The MSDN documentation on ITextHost is pretty much non-existant, so you have to work from the few indications you get. If you want to implement a fully editable ITextServices object, you will have to fill in all the functions I have left blank in my example. This is because, for the sole purpose of drawing RTF text, you don't need to provide much information to ITextServices, aside from the RTF text, the formatting rectangle, and the HDC. It is recommended that the class that implements ITextHost be a COM class because that's the way ITextServices communicates with it. To make my example easier, I left out the COM bits and provided myself the basic IUnknown functions. It is interesting to note that just after creating the TextServices object, it queries the TextHost for some other interface. I have not been able to figure out what this is, though.
Using the Example Code
All the windowless RichEdit functionality is provided by the CFormattedTextDraw class. It implements ITextHost minimally and also the custom interface (or rather purely abstract class) IFormattedTextDraw. The reason for using IFormattedTextDraw is that you should not call any functions on ITextHost directly; access should always be through IFormattedTextDraw instead of CFormattedTextDraw. The usage of the class can be seen in the Win32Test.cpp file. It is based on the sample "Hello World" Win32 project that VC6 generates. Here is the simplicity with which you use this class:
IFormattedTextDraw *g_Formatter; BSTR bstrText; RECT rt; HDC hdc; g_Formatter = new CFormattedTextDraw; g_Formatter->Create(); // Fill in the Unicode text, the rectangle, and get an HDC g_Formatter->put_RTFText(bstrText); g_Formatter->Draw(hdc, &rt);
To use this code in your own projects, you will have to link with riched20.lib. Moreover, you will most probably get a linker error related to _IID_ITextServices and _IID_ITextHost. If this is the case, open your version of textserv.h (you will find it in the ..\Microsoft SDK\include directory) and comment out the two following lines:
//EXTERN_C const IID IID_ITextServices; //EXTERN_C const IID IID_ITextHost;
DownloadsDownload demo project - 17 Kb
Download source - 13 Kb