Fog Creek Software
Discussion Board

HowTo: Full-Text Justification w/ RichTextBox

A couple of weeks ago I made a post asking if it was possible to do full text justification with a RichTextBox.

Here's the answer:

There are four different versions of the RichEdit Window Class.  They are 1.0, 2.0, 3.0 and 4.0.  Version 1.0 and 2.0 do not support advanced typography options such as full-text-justification.  Versions 3.0 and 4.0, however, do support advanced typography features.

Bear with me.  The Visual Basic RichTextBox Control is based on Version 1.0 of the RichEdit Window Class.  Thus limiting it to Version 1.0 abilities.  The MFC version of the RichTextBox Control is based on Version 1.0 or 2.0 of the RichEdit Window Class, depending on which MFC version you use.  The .NET RichTextBox Control is also based on Version 2.0 of the RichEdit Window Class.  Thus none of these ActiveX Controls support the advanced typography options available in Versions 3.0 and 4.0 of the RichEdit Window Class.  Version 1.0 also does not support Unicode.

So, you may have guessed that you simply need to create a Version 3.0 or 4.0 RichEdit Window Class in order to use the advanced typography options.  You would be correct.

Here's the deal:

You have to pass the correct class name of the version of the RichEdit window you want to create to the CreateWindowEx function.

- Class Name is Case Sensitive
- DLL name is not case sensitive but Version 4.0 is usually all capitals

Version 1.0:
- DLL Name: "riched32.dll"
- ANSI (No Unicode) Window Class Name: "RICHEDIT"

Version 2.0 and 3.0:
- DLL Name: "riched20.dll"
- ANSI Window Class Name: "RichEdit20A"    
- Unicode Window Class Name: "RichEdit20W"
Version 4.0:
- DLL Name: "msftedit.dll" or "RICHED20.DLL"
- Window Class Name: ?

These names are defined in the RichEdit header file.  The documentation says to pass "RICHEDIT_CLASS" which is a macro defined in richedit.h whose value is based on several system defines.

I have created a VC++ 7.0 sample that creates a RichEdit Window.  The advanced features will work if you have version 3.0+ of the riched20.dll. (If you don't have at least version 2.0 you'll get an error message "Can't create window class" and won't be able to see the richtextbox.)
(This is not a fancy sample, it just allowed me to basically play around with the RichEdit Window Class.)


- -

DLL Version 4.0?

- -

Why MS doesn't keep the ActiveX Controls up to date with latest versions of RichEdit, I don't know.  Maybe it would break too much code. (And put some samples on MSDN)

At any rate, I now have 1600 lines of Visual basic code that is based on the VB RichEdit ActiveX Control version 1.0.  I guess I could re-write that code in C++ and just write the whole app in C++ or create my own ActiveX control in VB based on version 3.0 of the RichEdit Window Class.

I found 2 attempts on the internet to try to create a control that uses the new window class version.  One is written in C++ Builder version 4.0/5.0 the other is in VB 6.0, but neither of them are IMO to the point where they are very useful except as an example.  They also rely on certain "hacks" and different components to get the job done.

Getting the DLL version seems to be more complex than I thought.  I tried GetFileVersionInfo and VerQueryInfo, but they return large numbers.  Anyone have any advice on getting/interpreting DLL/File Version.  When you right click on the DLL and select properties you get the Version 1.0, 2.0 etc, but when you use GetFileVersionInfo you get the oddball numbers.

Anyway, just my conclusions.

Dave B.
Monday, June 09, 2003

Thanks for the update.

The Microsoft DLL Help database:
shows the different versions of riched20.dll and the operating system or product that installed the version.

The version number reported by GetFileVersionInfo will be the complete version string (, for example), but this version number always increases with each new release of a Microsoft DLL. So, if you want to check that at least "v3.0" of riched20.dll is installed, check that the full version number is at least, or for this DLL, check that the first part of the version string is at least "5.30".

The "v3.0" (or later) DLL should exist if any of Office 2000 (or later), Windows XP, Windows ME, Windows 2000, Windows Server 2003, Visio 2002, or Visual Studio .NET is installed.

Sample VB code to obtain the full version number:

Private Declare Function GetFileVersionInfo Lib "Version.dll" Alias "GetFileVersionInfoA" (ByVal lptstrFilename As String, ByVal dwHandle As Long, ByVal dwLen As Long, lpData As Any) As Long
Private Declare Function GetFileVersionInfoSize Lib "Version.dll" Alias "GetFileVersionInfoSizeA" (ByVal lptstrFilename As String, lpdwHandle As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function VerQueryValue Lib "Version.dll" Alias "VerQueryValueA" (pBlock As Any, ByVal lpSubBlock As String, lplpBuffer As Any, puLen As Long) As Long
        dwSignature As Long
        dwStrucVersion As Long        '  e.g. 0x00000042 = "0.42"
'        dwFileVersionMS As Long        '  e.g. 0x00030075 = "3.75"
' separate into high-order and low-order integers
        dwFileVersionMS_lo As Integer        '  e.g. "3"
        dwFileVersionMS_hi As Integer        '  e.g. ".75"
'        dwFileVersionLS As Long        '  e.g. 0x00000031 = "0.31"
' separate into high-order and low-order integers
        dwFileVersionLS_lo As Integer        '  e.g. "0"
        dwFileVersionLS_hi As Integer        '  e.g. ".31"
        dwProductVersionMS As Long    '  e.g. 0x00030010 = "3.10"
        dwProductVersionLS As Long    '  e.g. 0x00000031 = "0.31"
        dwFileFlagsMask As Long        '  = 0x3F for version "0.42"
        dwFileFlags As Long            '  e.g. VFF_DEBUG Or VFF_PRERELEASE
        dwFileOS As Long              '  e.g. VOS_DOS_WINDOWS16
        dwFileType As Long            '  e.g. VFT_DRIVER
        dwFileSubtype As Long          '  e.g. VFT2_DRV_KEYBOARD
        dwFileDateMS As Long          '  e.g. 0
        dwFileDateLS As Long          '  e.g. 0
End Type

Private Function GetFileVer(ByVal strFilePath As String) As String
    Dim lngBufferLen As Long
    Dim lngHandle As Long
    Dim strBuffer() As Byte
    Dim lngPointerBuffer As Long
    Dim lngVerLen As Long
    Dim strFileVer As String

    ' Check for existence of file
    If Dir(strFilePath) = "" Then
        GetFileVer = "-1"
        Exit Function
    End If
    lngBufferLen = GetFileVersionInfoSize(strFilePath, lngHandle) ' lngHandle is unused
    If lngBufferLen = 0 Then
        GetFileVer = "0"
        Exit Function
    End If
    ReDim strBuffer(lngBufferLen)
    Call GetFileVersionInfo(strFilePath, 0&, lngBufferLen, strBuffer(0))
    Call VerQueryValue(strBuffer(0), "\", lngPointerBuffer, lngVerLen)
    Call CopyMemory(VerBuffer, ByVal lngPointerBuffer, Len(VerBuffer))
    strFileVer = Format(VerBuffer.dwFileVersionMS_hi) & "."
    strFileVer = strFileVer & Format$(VerBuffer.dwFileVersionMS_lo, "0") & "."
    If VerBuffer.dwFileVersionLS_hi > 0 Then
        strFileVer = strFileVer & Format(VerBuffer.dwFileVersionLS_hi, "0") & "."
    End If
    strFileVer = strFileVer & Format(VerBuffer.dwFileVersionLS_lo, "0")
    GetFileVer = strFileVer
End Function

Philip Dickerson
Monday, June 09, 2003

Try this (simpler version of the) URL for the Microsoft DLL Help database:
"This database contains information about DLL files that ship with selected Microsoft products. Use DLL Help to identify which software installed a specific version of a DLL. "

Philip Dickerson
Monday, June 09, 2003

Thanks Philip those links help a TON!

Dave B.
Monday, June 09, 2003

*  Recent Topics

*  Fog Creek Home