CRichEditControl50W
Developing an MFC Rich Edit
4.1 Control Application Using MSFTEDIT.DLL
(Published at
CodeProject.com and
CodeGuru.com, Mar 2005)
Download Source - 33 Kb,
for VS 2008
Download Demo App - 21 KbI've developed the
first known Microsoft Foundation Classes (MFC) source code of a rich edit 4.1 control on the 'Net. It
even handles hyperlinks!
Introduction:
CRichEditControl50W
is a CWnd
-derived
Rich Text Edit control v. 4.1 using the new, poorly documented msftedit.dll
(MSFTEDIT_CLASS
, or "RichEdit50W
"
classname) that ships with Windows XP. The CRichEditCtrl
provided in VC++.NET only uses the old v. 3.0 rich edit control (RICHEDIT_CLASS
,
or "RichEdit20W
"). There are no examples of
using the new control in MFC that I could find anywhere on the net. So I decided
to create my own after looking at the CRichEditCtrl
and CRichEditView
classes in VS. The lack of
documentation made me do a whole lot of research to realize that msftedit.dll
was not supported by VS.NET, VS 2008, or the CRichEditView
class. I figured I'd save somebody else the headache of sorting this mess out.
The latest version, published March 10, 2008, is Unicode and Multi-Byte
Character Set (MBCS) compliant.
Background:
I tried to update my ping/traceroute application's
CRichEditCtrl
with the new "RichEdit50W
"
window class. You can't simply substitute MSFTEDIT_CLASS
for RICHEDIT_CLASS
in richedit.h.
Microsoft uses the 4.1 version in Wordpad XP, and has developed new,
undocumented classes CRichEdit2View
,
CRichEdit2Ctrl
, CRichEdit2Doc
,
CRichEdit2Cntr
(which I found by looking at
Wordpad.exe with a Hex Editor)... but they don't appear in Visual Studio
.NET 2003 or 2005, or VS 2008. They left the old WORDPAD example using riched20.dll.
The "RichEdit50W
" (or "MSFTEDIT_CLASS
")
class does not work with the current CRichEditView
in VS; CRichEditView
is hard-coded to load
riched20.dll.
So I've done the work for the control...just
download the
demo application or source code
and adapt for your uses. Note that I've not
created any new base classes like MS did, so you won't be able to use the
document/view architecture to embed objects, unless you develop your own and
recompile VS .NET...the
RichEdit50W
(or
MSFTEDIT_CLASS
) class does not work
with the current
CRichEditView
in VS. No matter what you do,
CRichEditView
is hard-coded to load
riched20.dll
.

Rich Edit 50W Control Application developed with Visual
Studio 2008
Using the code:
Steps:
-
Create a new MFC application in Visual Studio. In the "Application Type"
tab, select "Single Document", and deselect the Document/View
Architecture option...you don't need this. I named my application
RE50W
. Once
complete, remove all references to the
CChildView
class, which is
the default view constructed by
CMainFrame
; you don't need this,
either.
-
Add the files
RichEditControl50W.cpp
and
RichEditControl50W.h
to your project. This is the
CRichEditControl50W
control I created.
-
In the application's
re50w.cpp
, add:
#include "RichEditControl50W.h" // before
#include "MainFrm.h"
-
In
MainFrame.cpp
, add:
#include "RichEditControl50W.h" // before
#include "MainFrm.h"
-
In
MainFrame.f
, declare
CRichEditControl50W m_REControl50W
as a protected member:
etc..........................
protected: // control bar embedded members
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
CRichEditControl50W m_REControl50W;
etc..................
-
In
MainFrame.cpp
, under
OnCreate
, change the
way the
view is created:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
DWORD w_RichEd = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_VSCROLL |
ES_AUTOVSCROLL | WS_HSCROLL | ES_AUTOHSCROLL | ES_MULTILINE;
if (!m_REControl50W.Create(w_RichEd, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST))
{
TRACE0("Failed to create view window\n");
return -1;
}
m_REControl50W.LimitText50W(-1);
//Set the control to accept the maximum amount of text
DWORD REOptions = (ECO_AUTOVSCROLL | ECO_AUTOHSCROLL | ECO_NOHIDESEL | ECO_SAVESEL | ECO_SELECTIONBAR);
m_REControl50W.SetOptions50W(
ECOOP_OR, //The type of operation
REOptions ); //Options
m_REControl50W.SendMessage( EM_AUTOURLDETECT, TRUE, 0);
m_REControl50W.SetEventMask50W(
ENM_SELCHANGE | ENM_LINK //New event mask for the rich edit control
);
m_REControl50W.SetDefaultCharFormat50W(
CFM_COLOR | CFM_BOLD | CFM_SIZE |
CFM_FACE | CFM_BACKCOLOR,
RGB(0,0,0),
!CFE_BOLD,
"Trebuchet MS",
200,
RGB(255,255,255));
m_csMessage = _T("{\\rtf1 RE50W by Jim Dunne Copyright (C) 2005\\par http://www.topjimmy.net/tjs \\par
{\\field{\\*\\fldinst{HYPERLINK mailto:jim@dunnes.net }}{\\fldrslt{\\cf1\\ul jim@dunnes.net}}}}");
m_REControl50W.SetSel50W(-1, -1);
m_REControl50W.SetTextTo50WControl(
m_csMessage,
ST_SELECTION,
1200);
etc..........................
-
Add the
OnContextMenu
function to
CMainFrame
to
handle your custom popup menu for the rich edit control. Create a custom popup
menu named
IDR_REPOPUP
(see source code for more details):
void CMainFrame::OnContextMenu(CWnd* pWnd, CPoint point)
{
CMenu menu;
if (menu.LoadMenu(IDR_REPOPUP))
{
CMenu* pPopup = menu.GetSubMenu(0);
ASSERT(pPopup != NULL);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point.x , point.y, AfxGetMainWnd());
}
}
-
Add command and update handlers and functions for your popup menu. An
example for the
Copy
function:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_WM_SETFOCUS()
ON_WM_CONTEXTMENU()
ON_COMMAND(ID_REPOPUP_COPY, OnPopupCopy)
ON_UPDATE_COMMAND_UI(ID_REPOPUP_COPY, OnUpdatePopupCopy)
etc.................
void CMainFrame::OnPopupCopy()
{
m_REControl50W.SendMessage(WM_COPY, 0, 0);
}
void CMainFrame::OnUpdatePopupCopy(CCmdUI* pCmdUI)
{
m_REControl50W.SendMessage(EM_EXGETSEL, 0, (LPARAM)&m_crStatus);
pCmdUI->Enable(m_crStatus.cpMin != m_crStatus.cpMax);
}
-
Add
#include <richedit.h>
to the bottom of
stdafx.h
. (This isn't actually necessary, but just to be safe...).
Handling Hyperlinks:
This was a lot of fun, due to the serious lack of documentation. WordPad XP doesn't handle RTF hyperlinks, but Word 2003 does, so I knew it could be done. The problem is the GetTextRange()
function, or EM_GETTEXTRANGE
message, which you send to the control to get the hyperlink's text.
Under an MBCS implementation, if you use this per the instructions in Visual Studio, the TEXTRANGE lpstrText LPSTR
buffer only returns the first character of the range. I found others on the 'Net who have called this a bug in the Rich Edit Control. The problem is really Unicode vs. ANSI. If you use Unicode in your richedit control, EM_GETTEXTRANGE
won't work right.
Instead of TEXTRANGE
, you have to use the TEXTRANGEW
structure, defined in richedit.h. The lpstrText
buffer in this structure is LPWSTR
(wide-string) to handle Unicode.
This works fine for a UNICODE build. However, for an MBCS build, after you get the lpstrText
back from EM_GETTEXTRANGE
, you have to convert it back to ANSI, so you can use it with MFC. To do this, you fill a LPSTR
buffer with the LPWSTR
buffer contents using WideCharToMultiByte()
. Then, you
can use the LPSTR
buffer contents in a ShellExecute()
to open your default email, web browser, etc. app, based on the hyperlink (i.e., http:// opens your default web browser).
Setting the ENM_LINK
event mask for the control in the CMainFrame OnCreate
function causes the control to send a WM_NOTIFY
message to the parent window,
CMainFrame
, whenever a hyperlink in the control is acted on. To catch and use this message; using the Class View in Visual Studio, add the OnNotify
function to CMainFrame
, then modify it:
BOOL CMainFrame::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
ENLINK *p_enRE50W;
p_enRE50W = (ENLINK *)lParam;
if(p_enRE50W && p_enRE50W->msg == WM_LBUTTONUP)
{
m_REControl50W.GetTextRange50W(p_enRE50W->chrg.cpMin, p_enRE50W->chrg.cpMax);
#ifdef _UNICODE
ShellExecute(m_hWnd, _T("Open"), m_REControl50W.m_trRE50W.lpstrText,
NULL, NULL, SW_MAXIMIZE);
#else
ShellExecute(m_hWnd, _T("Open"), m_REControl50W.m_lpszChar, NULL, NULL, SW_MAXIMIZE);
#endif
}
return CFrameWnd::OnNotify(wParam, lParam, pResult);
}
GetTextRange50W()
(see below) retrieves the text in a given
range, which ShellExecute()
uses to open the appropriate application for your hyperlink.
If you open the demo app, and click on the webpage or email links, your
default application(s) should handle them.
A look at CRichEditControl50W:
- On
Create
, it loads
msftedit.dll
and creates the
control as a
CWnd
:
BOOL CRichEditControl50W::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
m_hInstRichEdit50W = LoadLibrary("msftedit.dll");
if (!m_hInstRichEdit50W)
{
AfxMessageBox("MSFTEDIT.DLL Didn't Load");
return(0);
}
CWnd* pWnd = this;
return pWnd->Create("RichEdit50W", NULL, dwStyle, rect, pParentWnd, nID);
}
- It implements some of the functions of
CRichEditCtrl
, which
I've modified to make easier to use. For example, my
SetTextTo50WControl()
function:
void CRichEditControl50W::SetTextTo50WControl(CString csText, int nSTFlags, int nSTCodepage)
{
m_st50W.codepage = nSTCodepage;
m_st50W.flags = nSTFlags;
#ifdef _UNICODE
USES_CONVERSION;
m_lpcsUnicode = W2A( csText.LockBuffer( ) );
csText.UnlockBuffer( );
SendMessage(EM_SETTEXTEX,(WPARAM)&m_st50W,(LPARAM)(LPCTSTR)m_lpcsUnicode);
#else
SendMessage(EM_SETTEXTEX, (WPARAM)&m_st50W,(LPARAM)(LPCTSTR)csText);
#endif
}
specifies both the text to be written to the control and the flags for the
SETTEXT
structure m_st50W
, which is a member variable
declared in RichEditControl50W.h
. If you are working with a UNICODE
build, the text string csText
must be converted to an
LPCSTR
, which is done with the W2A
macro.
-
GetTextRange50W()
retrieves the text in a given range.
The TEXTRANGEW
member lpstrText
is set equal to a wide character buffer.
If
you're using an MBCS build,
m_lpszWChar
must be converted to a regular character buffer
m_lpszChar
using
WideCharToMultiByte()
:
void CRichEditControl50W::GetTextRange50W(int ncharrMin, int ncharrMax)
{
m_trRE50W.chrg.cpMin = ncharrMin;
m_trRE50W.chrg.cpMax = ncharrMax;
int nLength = int((m_trRE50W.chrg.cpMax - m_trRE50W.chrg.cpMin +1));
#ifdef _UNICODE
m_lpszWChar = new WCHAR[nLength];
m_trRE50W.lpstrText = m_lpszWChar;
SendMessage(EM_GETTEXTRANGE, 0, (LPARAM) &m_trRE50W);
#else
m_lpszChar = new CHAR[nLength];
m_lpszWChar = new WCHAR[nLength];
m_trRE50W.lpstrText = m_lpszWChar;
SendMessage(EM_GETTEXTRANGE, 0, (LPARAM) &m_trRE50W);
WideCharToMultiByte(CP_ACP, 0, m_lpszWChar, -1, m_lpszChar, nLength, NULL, NULL);
#endif
return;
}
- The destructor releases
msftedit.dll
:
CRichEditControl50W::~CRichEditControl50W()
{
if(m_hInstRichEdit50W)
FreeLibrary(m_hInstRichEdit50W);
}
- You can write your own functions to emulate and/or improve upon the
CRichEditCtrl
functions. Just have a look at
WINCTRL4.CPP
,
AFXCMN.H
,
AFXCMN.INL
, and
VIEWRICH.CPP
in Visual Studio
and you can get an idea of how to implement them.
Msftedit.dll
and
riched20.dll
are very similar... all of the
CRichEditCtrl
messages should work for
CRichEditControl50W
. The only big
differences I could find by looking at both with the OLE/COM Object Viewer in
Visual Studio is that
msftedit.dll
implements the
ITextDocument2
and
ITextMsgFilter
interfaces, which
I can find no documentation on.
- As you can see from the screenshot below of my updated app, you can create
nice tables and do all kinds of cool text formatting with the new rich edit
control, which you can't do with RichEdit 3.0 (try creating a table with no
interior borders with
riched20.dll
, and you'll see what I mean):

Check the MSDN documentation for many more things you can do with rich
edit controls. For information on RTF codes, see the Microsoft Office Word 2003 Rich Text Format (RTF) Specification
White Paper.
Download: |
MSFTEDIT.DLL, v. 5.41.15.1509,
Rich Text Edit Control, v4.1, Zip format, 311 Kb
|
History:
10 Mar 08, v. 2.0:
- Changed to accommodate UNICODE and MBCS builds.
- Recompiled in VS 2008.
11 Mar 05:
- Added hyperlink handling capability
- Added more text-editing options to the control's context popup menu
Back
Updated
March 10, 2008