TOM Classes for .NET

About

TOM Classes for .NET is a managed assembly, written in C++/CLI, that provides a set of wrapper classes for the Text Object Model (TOM) interfaces. This allows .NET developers to access the functionality of the native ITextDocument interface without the need to use dynamic programming or COM interop. The assembly includes the following classes:

  • TextDocument (ITextDocument)
  • TextRange (ITextRange)
  • TextSelection (ITextSelection)
  • TextFont (ITextFont)
  • TextPara (ITextPara)
  • TextStoryRanges (ITextStoryRanges)

TOM 2 support

The latest version includes support for TOM 2 (available on Windows 8 and higher). Each of the above classes wraps both the TOM 1 and TOM 2 interfaces (e.g. ITextDocument and ITextDocument2), and will automatically select the best available version. In addition, TOM 2 adds the following classes:

  • TextStory (ITextStory)
  • TextRow (ITextRow)
  • TextStrings (ITextStrings)

Also included

  • RichTextBoxEx – an extended version of the Windows Forms RichTextBox control which automatically loads the newest available version of the native RichEdit control (required for advanced functionality).
  • TextExtensions – provides extension methods for working with ranges using a more familiar syntax, as well as conversion of math text to OMML and MathML.

Requirements

Download

Current version: 1.0.2

Binaries:
ManagedTOM2-1.0.2-bin.zip (includes x86/x64 DLLs, TOM 1-only DLLs, IntelliSense XML and help file)

Source:
ManagedTOM2-1.0.2-src.zip (Visual Studio 2012 solution, includes demo application and help file)

Changes:

  • Fixed RichTextBoxEx being unable to select the correct RichEdit control where multiple versions of Office have been installed.
  • Fixed the SuspendRedo and ResumeRedo methods in TextDocument not working as documented.
  • Added the ability to manually specify a DLL and window class name for RichTextBoxEx to use.
  • Other minor tweaks and enhancements.

Instructions

  1. Extract the binaries to a location on disk.
  2. Note: You may have to right-click the DLLs and select the ‘Unblock’ option on the Properties window (in order to fix trust issues in Visual Studio).
  3. Add a reference to the required assembly (x86 or x64, TOM 2 or TOM 1-only) to your project.
  4. Note: You may have to manually add RichTextBoxEx to the Visual Studio toolbox if it does not automatically appear.
  5. Explore the TextObjectModel namespace and refer to the documentation to start using the TOM classes.

Documentation

Help is included in the download packages, and is also available online: TOM Classes for .NET – Help

Related articles

Managed Wrapper for TOM 2 / RichEdit 8
Managed Wrapper for the Text Object Model (TOM)

24 thoughts on “TOM Classes for .NET

  1. vote

    Hi Brad. This looks like a tremendous opportunity. Interfacing via TOM looks like a massive improvement for the RichTextBox. Thanks for the insight. And building your own wrapper libraries, wow. Just one question: are you aware of compatability issues with Windows 7 ? I am getting a FileNoteFound error.

    This article (http://blogs.msdn.com/b/murrays/archive/2012/03/03/richedit-8-0-preview.aspx) implies significant availability for TOM for Windows 8, but his previous posts (eg: http://blogs.msdn.com/b/murrays/archive/2009/09/05/richedit-versions-update-to-7-0.aspx) mention material version differences between what comes with the OS and MS Office.

    My target is to cater for Win7 + Office 2010 and above. I’m wondering if I have to make it Windows 8. Any insights woud be appreciated. Thanks in advance.

    Reply
    • vote

      Hi Dean – I’ve tested the assembly on Windows 7 and Windows 8 (64-bit in both cases, but that shouldn’t matter), so the functionality definitely works on those OSes. Some things to note, however:

      • You must have the 32-bit version of the Visual C++ 2010 Redistributable installed. If this is absent, your application will compile but you will get an exception at run-time.
      • Your application must target either Any CPU or x86. On x64 you will get a BadImageFormatException, because the native code in TextObjectModel.dll is 32-bit.

      Hope this helps!

      Reply
  2. vote

    Very Strange. My dev machine is Win7 x64, running VS2010. I’m using your C# demo project, but had to remove the C++ project from the solution because its not backward compatible (not that I’d know how to read/troubleshoot it anyway). Instead I added an assembly reference to TextObjectModel.dll. Is still set to build x86. Seems simple enough. Hit [F5] and the OnShown method throws FileNotFoundException: “Could not load file or assembly ‘TextObjectModel.dll’ or one of its dependencies. The specified module could not be found.”.

    I downloaded the VC++ redistributable just in case but the install tells me that it cannot install because a newer version already exists. Framework 4 is obviously installed already. Any suggestions on how to check for other “dependencies” ?

    Reply
  3. vote

    In the file TextRange.h, is following code an implementation of IDisposable.DIspose() method?

    !TextRange() {
    _range->Release();
    _range = NULL;
    }

    Reply
    • vote

      In C++/CLI, the destructor syntax (~) is used to implement IDisposable.Dispose to release managed resources, whereas the finalizer syntax (!) is used to release unmanaged resources. In this case, I am releasing unmanaged resources (the COM object that represents the range).

      Reply
  4. vote

    Oh! I see.

    I asked the question because I was not sure whether or not to call Dispose() on TextRange object.

    Anyways, I am using following code: (where doc is an object of type TextDocument and startIndex/endIndex are character positions)

    doc.Range(startIndex, endIndex).Text = “Bla Bla”;

    Will that cause a memory leak? Or should I use following code:

    TextRange rngTemp = doc.Range(startIndex, endIndex);

    rngTemp.Text = “Bla Bla”;

    rngTemp.Dispose();
    rngTemp = null;

    Reply
    • vote

      If you want to release the resources immediately after you have finished using them, you can call the Dispose method (or wrap with a using statement), but it will not cause a memory leak if you choose not to; the resources will be released by the garbage collector at a later point in time.

      Reply
  5. vote

    Just an FYI:

    I am developing under WinXP using VS2010. with .NET 4.0. Your Tom2 seems to work fine (I have office 2007 installed). BUT….

    When I create a TextObjectModel.RichTextBoxEx and try to enter text into it. The first character entered always causes a “RichEdit assert” error as follows:

    “The specified module could not be found (msptls.dll)”

    So I had to copy the file msptls.dll from “C:\Program Files\Microsoft Visual Studio 10.0\Common7\Packages\vwd\msptls.dll” to my application directory where the ManagedTom2.dll is. No problems after that.

    Reply
    • +1
      vote

      I suspect this is a component of the vs 2012 runtime which is required to build/use the assembly. I did not actually expect it to work on Windows XP, but it’s good to know that this is possible. Thanks!

      Reply
  6. vote

    Another FYI:

    Coding under: WinXP .NET 4.0 Visual Basic, Office 2007 installed.

    With the TOM2 dll, when I inherit from TextObjectModel.RichTextBoxEx, attempting to change WordWrap throws an out of memory exception.

    The Class:

    Public Class xRichTextBox
    Inherits TextObjectModel.RichTextBoxEx
    ……

    In the Main Form:

    Public WithEvents RTB As New xRichTextBox
    ……

    With a RichText file loaded, The following code throws the exception:
    RTB.WordWrap = Not RTB.WordWrap.

    Reply
    • vote

      A reminder that this project’s minimum requirements are Windows Vista and .NET 4.5 – although it may work under XP and .NET 4, this environment is untested and unsupported.

      Rather unsurprisingly, I couldn’t reproduce your problem under the recommended environment – text wrapping was changed successfully whether loading a file using the RichTextBoxEx.LoadFile method or the TextDocument.Open method – verified this on x86 (Win8 RichEdit control) and x64 (Office2013 RichEdit control).

      It may be worthwhile checking which version of the native RichEdit control is being loaded by RichTextBoxEx – using the debugger, place a breakpoint on the line of code which throws the exception and inspect the variable – you will be able to see the native control name under CreateParams (and also under _windowClassName in Non-Public Members). If it shows RICHEDIT60W, it will be using the Office version. If RICHEDIT50W or RICHEDIT20W, it will be using the Windows version. One of these may not support the WordWrap property.

      Reply
      • vote

        No doubt it’s XP that’s the problem. I went back to Tom1. Eventually I’ll get a better machine and use the full capabilities of Tom2. Anyway, thanks for the wrapper. It really extends the usefulness of the RichTextBox!

        Reply
  7. vote

    I cannot get lists to display properly. The bullets nor any other numbering scheme don’t show up. All that happens is that the paragraph(s) get slightly indented, as if there is a list. But no numbers or bullets or letters! I have set the listnumberingformat and liststart several different ways.

    I now run Windows 10, .NET 4.5, Visual Basic 2015. Office 2007 installed. I create the textdocument from the standard windows forms richtextbox. Have also tried it from your richtextboxex, with the same result.

    Any thoughts?

    Reply
    • vote

      I haven’t tested the code on Windows 10, which may have a different version of the TOM classes than Windows 8/8.1. This could also be an architecture mismatch, i.e. 64-bit process with 32-bit Office, or vice versa.

      Also try the TOM1-only version of the DLL, as you may get different results.

      Reply
  8. vote

    Another problem. This time with hyperlinks. Here’s an example.

    Define a RichTextBox and a TextDocument:

    Public WithEvents RTB As New RichTextBox
    Public WithEvents DOC As TextDocument = TextDocument.FromRichTextBox(RTB)

    now add text and select:

    RTB.Text = “Hello”
    DOC.EntireRange.Select()

    Now DOC.Selection.rtf looks like this:

    {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
    {\*\generator Riched20 15.0.4420}\viewkind4\uc1
    \pard\f0\fs17 Hello\par
    }

    Now set a link:

    DOC.EntireRange.Select()
    DOC.Selection.Font.Effects = CharacterEffects.Link

    DOC.Selection.rtf now looks like this:

    {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
    {\colortbl ;\red0\green0\blue255;}
    {\*\generator Riched20 15.0.4420}\viewkind4\uc1
    \pard {\f0\fs17{\field{\*\fldinst{HYPERLINK Hello }}{\fldrslt{Hello\ul0\cf0}}}}\f0\fs17\par
    }

    Now Save as Hello.rtf:

    DOC.Save(“Hello.rtf”, TextOpenSave.RTF, TextSaveFlags.None)

    The text of Hello.rtf looks this in notepad:

    {\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
    {\colortbl ;\red0\green0\blue255;}
    {\*\generator Riched20 15.0.4420}\viewkind4\uc1
    \pard {\f0\fs17\lang1033{\field{\*\fldinst{HYPERLINK Hello }}{\fldrslt{Hello\ul0\cf0}}}}\f0\fs17\par
    }

    Now Load the file back into DOC:

    DOC.Open(“Hello.rtf”, TextOpenSave.RTF, TextOpenFlags.None)

    Now DOC.EntireRange.Select() looks like this!!!!

    {\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
    {\*\generator Riched20 15.0.4420}\viewkind4\uc1
    \pard\f0\fs17\lang1033 Hello\par
    }

    The hyperlink has disappeared! What happened to the hyperlink between saving and loading????

    Reply
    • vote

      This is an implementation detail of the native RichEdit control; if the DetectUrls property of the control is set to true (which is the default), then it will discard any hyperlinks without a recognised URL scheme upon loading. You must set this property before you obtain the TextDocument object from the control.

      Reply
  9. vote

    A curiosity:
    I am Using your TOM2 x64 on Windows 10:
    (“TOM2 Riched20 10.0.17134”)

    I noticed that setting a text selection to AllCaps seems to block the ability to then do a ToggleCase.
    As in the following (where DOC = TextDocument):

    DOC.Selection.Font.AllCaps = True ‘(changes everything to upper case)
    DOC.Selection.ChangeCase(TextCasing.ToggleCase) ‘(should now change everything to lower case but doesn’t)

    Same thing happens using SmallCaps followed by ToggleCase.

    Is this a bug? Just curious. There is a workaround.

    Reply
    • vote

      My understanding is that AllCaps and SmallCaps are actually font styles and thus they don’t change the underlying casing of the characters, so you would have to remove those styles from the selection before your changes would be visible. So not a bug, just an implementation detail by Microsoft in this case.

      Reply
  10. vote

    I am trying to use TOM2 but keep getting the following error:
    System.IO.FileNotFoundException: ‘Could not load file or assembly ‘ManagedTOM.dll’ or one of its dependencies. The specified module could not be found.’

    Win 7 SP1
    VS2017
    32-bit version of the Visual C++ 2010 Redistributable installed
    targeting x86 (also tried AnyCPU)
    Tried Dep Walker but it just lists Win 8 .dll’s missing

    Any thoughts?

    Reply
    • vote

      The current version of the DLL was built against the Visual C++ Redistributable for Visual Studio 2012 Update 4 – try installing this. I will update the page accordingly.

      Alternatively, you could try grabbing the source code and re-targeting the solution to use the latest Windows SDK version.

      Reply
  11. vote

    I have a .NET 8 winforms project on Windows 10 set to build as Any CPU, and I’m referencing the x64 version of the dll. I’m getting the following warning:

    “There was a mismatch between the processor architecture of the project being built “MSIL” and the processor architecture of the reference “ManagedTOM2”, “AMD64″. This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project.”

    Upgrading the TOM projects in Visual Studio 2022 fails (they won’t build after), and I don’t know enough C++ to know why or how to fix it.

    Also, one of my chief use cases is to use RTF annotations. The standard RIchTextBox seems to strip out a lot of valid RTF syntax. Will your library help? Any idea how I might get annotations, and even custom RTF templates (colortbl, fonttbl, stylesheet…) to work?

    Reply
    • vote

      It’s been a long time since I revisited this project, so I may not have all the answers you want. But i’ll do my best:

      Firstly, regarding the processor mismatch – yes, ManagedTOM2 must be built for either x86 or x64 (and cannot be processor-neutral) due to its dependencies on native Win32 APIs (it does not use PInvoke, rather, C++/CLI to directly call those APIs). This means that any projects which reference the assembly must target the same processor architecture. I’m sure there are better ways of approaching this in the .NET Core era, but this was the most optimal solution at the time.

      I can’t say specifically why the projects wouldn’t build on the latest VC runtime, but my first guess would be that you don’t have RichEd20.lib which WAS part of the Windows SDK but may be missing in some versions.

      I don’t think that TOM/TOM2 has any particular API for RTF annotations; remember that TOM is an object model for the RichEdit control and NOT for the RTF format itself. TOM’s functionality is similar to that of Wordpad (although there are some additional APIs for working with math formulas). If all you want to do is parse/convert RTF then there are a number of NuGet packages which can do this using pure .NET/MSIL.

      Reply

Leave a reply

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> 

required