Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. Office converter DLLs and thread safety

Office converter DLLs and thread safety

Scheduled Pinned Locked Moved C / C++ / MFC
toolsquestionannouncementworkspace
5 Posts 2 Posters 2 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Mattias G
    wrote on last edited by
    #1

    Hi all, I've been experimenting with the converter DLLs of Microsoft Office (under HKLM\Software\Microsoft\Shared Tools\Text Converters\Import). In a single-threaded environment, everything runs ok. But I'm running into difficulties when calling the exported conversion functions like ForeignToRtf32 in the multi-threaded version of my program, even if the libraries are loaded within the context of the calling thread. Does anyone know about the thread safety of these converters?

    R 1 Reply Last reply
    0
    • M Mattias G

      Hi all, I've been experimenting with the converter DLLs of Microsoft Office (under HKLM\Software\Microsoft\Shared Tools\Text Converters\Import). In a single-threaded environment, everything runs ok. But I'm running into difficulties when calling the exported conversion functions like ForeignToRtf32 in the multi-threaded version of my program, even if the libraries are loaded within the context of the calling thread. Does anyone know about the thread safety of these converters?

      R Offline
      R Offline
      Roger Stoltz
      wrote on last edited by
      #2

      Mattias G wrote:

      I'm running into difficulties when calling the exported conversion functions like ForeignToRtf32 in the multi-threaded version of my program, even if the libraries are loaded within the context of the calling thread. Does anyone know about the thread safety of these converters?

      If I understand your question/problem correctly, this has nothing to do with the converter DLLs. What do you mean by "loaded within the context of the calling thread"? I suspect you mean that you call ::LoadLibrary() for each thread that uses the library and you assume that it somehow will make calls to the DLL thread safe. Calling ::LoadLibrary() will load the desired DLL and map into the address space of the process the first time it is called. Subsequent calls to ::LoadLibrary() will only increment the reference count for the DLL in the process, which means that the code and variables will be shared among the calling threads. Calling ::FreeLibrary() will decrement the reference count. Read more here[^].

      "It's supposed to be hard, otherwise anybody could do it!" - selfquote
      "High speed never compensates for wrong direction!" - unknown

      M 1 Reply Last reply
      0
      • R Roger Stoltz

        Mattias G wrote:

        I'm running into difficulties when calling the exported conversion functions like ForeignToRtf32 in the multi-threaded version of my program, even if the libraries are loaded within the context of the calling thread. Does anyone know about the thread safety of these converters?

        If I understand your question/problem correctly, this has nothing to do with the converter DLLs. What do you mean by "loaded within the context of the calling thread"? I suspect you mean that you call ::LoadLibrary() for each thread that uses the library and you assume that it somehow will make calls to the DLL thread safe. Calling ::LoadLibrary() will load the desired DLL and map into the address space of the process the first time it is called. Subsequent calls to ::LoadLibrary() will only increment the reference count for the DLL in the process, which means that the code and variables will be shared among the calling threads. Calling ::FreeLibrary() will decrement the reference count. Read more here[^].

        "It's supposed to be hard, otherwise anybody could do it!" - selfquote
        "High speed never compensates for wrong direction!" - unknown

        M Offline
        M Offline
        Mattias G
        wrote on last edited by
        #3

        No, I wasn't hoping that calling LoadLibrary from within the thread (that later will call ForeignToRtf32) would in some magical way make the DLLs thread safe. Did put some more effort into this, but couldn't find any real pattern. Did get rid of one exception type by balancing the calls to OleInitialize and OleUninitialize more carefully for each thread. Still getting a lot of exceptions of types 0x800401FD CO_E_OBJNOTCONNECTED and 0x8001010E RPC_E_WRONG_THREAD (though I'm not using any COM interfaces explicitly in my application). So back to my original question: Does anyone know anything about the thread safety of these libraries? Thanks

        R 1 Reply Last reply
        0
        • M Mattias G

          No, I wasn't hoping that calling LoadLibrary from within the thread (that later will call ForeignToRtf32) would in some magical way make the DLLs thread safe. Did put some more effort into this, but couldn't find any real pattern. Did get rid of one exception type by balancing the calls to OleInitialize and OleUninitialize more carefully for each thread. Still getting a lot of exceptions of types 0x800401FD CO_E_OBJNOTCONNECTED and 0x8001010E RPC_E_WRONG_THREAD (though I'm not using any COM interfaces explicitly in my application). So back to my original question: Does anyone know anything about the thread safety of these libraries? Thanks

          R Offline
          R Offline
          Roger Stoltz
          wrote on last edited by
          #4

          Mattias G wrote:

          No, I wasn't hoping that calling LoadLibrary from within the thread (that later will call ForeignToRtf32) would in some magical way make the DLLs thread safe.

          Ok, I just couldn't find any other explanation to what you meant by "loading a DLL within the context of a thread". I interpreted your text as, whatever you meant by it, you thought it would prevent the problems you got when trying to use multiple threads when accessing the converter DLLs. But never mind. The only thing important is that you only need to call ::LoadLibrary() once for any DLL, subsequent calls will do nothing in practice. I have never used the DLLs you are trying to use, nor have I found anything useful trying to google it. So I have no idea what kind of functionality the DLLs expose, how to use them or what their function prototypes may look like. But that may not be necessary....

          Mattias G wrote:

          Still getting a lot of exceptions of types 0x800401FD CO_E_OBJNOTCONNECTED and 0x8001010E RPC_E_WRONG_THREAD (though I'm not using any COM interfaces explicitly in my application).

          Ok, so that means the DLLs uses COM in order to perform its ForeignToRtf32(). When you use COM from different threads you have to initialize COM for every thread that makes use of the COM library by calling e.g. ::CoInitialize() or one of its equivalents. Failure to do so may seem to work, but will cause unpredictable errors being very hard to realize that they originate from not initializing COM. In addition you have to have a message pump for every thread that creates a COM server. Initializing COM for a thread is called setting up an "apartment" and COM interfaces are not allowed to cross apartment boundaries without being properly marshalled by using e.g. ::CoMarshalInterThreadInterfaceInStream() or the GlobalInterfaceTable(GIT). Using a COM interface from an apartment without proper marshalling will result in the error RPC_E_WRONG_THREAD. Since you do not use any COM interfaces in your application I assume that the converter DLLs create the necessary COM servers "under the hood". This probably means that you cannot possibly marshal any COM interface to any other thread than the one you have used to do whatever initialization needed for the converter DLLs, which should be ex

          M 1 Reply Last reply
          0
          • R Roger Stoltz

            Mattias G wrote:

            No, I wasn't hoping that calling LoadLibrary from within the thread (that later will call ForeignToRtf32) would in some magical way make the DLLs thread safe.

            Ok, I just couldn't find any other explanation to what you meant by "loading a DLL within the context of a thread". I interpreted your text as, whatever you meant by it, you thought it would prevent the problems you got when trying to use multiple threads when accessing the converter DLLs. But never mind. The only thing important is that you only need to call ::LoadLibrary() once for any DLL, subsequent calls will do nothing in practice. I have never used the DLLs you are trying to use, nor have I found anything useful trying to google it. So I have no idea what kind of functionality the DLLs expose, how to use them or what their function prototypes may look like. But that may not be necessary....

            Mattias G wrote:

            Still getting a lot of exceptions of types 0x800401FD CO_E_OBJNOTCONNECTED and 0x8001010E RPC_E_WRONG_THREAD (though I'm not using any COM interfaces explicitly in my application).

            Ok, so that means the DLLs uses COM in order to perform its ForeignToRtf32(). When you use COM from different threads you have to initialize COM for every thread that makes use of the COM library by calling e.g. ::CoInitialize() or one of its equivalents. Failure to do so may seem to work, but will cause unpredictable errors being very hard to realize that they originate from not initializing COM. In addition you have to have a message pump for every thread that creates a COM server. Initializing COM for a thread is called setting up an "apartment" and COM interfaces are not allowed to cross apartment boundaries without being properly marshalled by using e.g. ::CoMarshalInterThreadInterfaceInStream() or the GlobalInterfaceTable(GIT). Using a COM interface from an apartment without proper marshalling will result in the error RPC_E_WRONG_THREAD. Since you do not use any COM interfaces in your application I assume that the converter DLLs create the necessary COM servers "under the hood". This probably means that you cannot possibly marshal any COM interface to any other thread than the one you have used to do whatever initialization needed for the converter DLLs, which should be ex

            M Offline
            M Offline
            Mattias G
            wrote on last edited by
            #5

            A great step forward was to call CoUnitialize for each thread. Tried both with and without a message pump (with MsgWaitForMultipleObjects), the message pump version produced less exceptions, but the actual result of the conversion is the same. I'm really close to giving up on this, the combination of a under-documented API and multi-threading is a bit much :-) Currently, I have a pool of 4 threads for different converters (not only the Office ones, but for HTML and various other formats who are unproblematic). In case of the Office DLL converters, each thread in the pool kicks off another separate thread doing the actual conversion (the only way I could figure out to put a message pump there, and this is how it's done in the MFC/ole/Wordpad sample of MSVC). Everything works fine (with no exceptions at all and correct conversion results) if I block reentry at the call to ForeignToRtf32 with a CRITICAL_SECTION mutex. So if the application should do something with let's say *.docx files only, the performance boost from multi-threading is ... exactly zero (!) Functional, but what a waste of effort... Thanks anyhow for the interesting reading of marshalling!

            1 Reply Last reply
            0
            Reply
            • Reply as topic
            Log in to reply
            • Oldest to Newest
            • Newest to Oldest
            • Most Votes


            • Login

            • Don't have an account? Register

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • World
            • Users
            • Groups