Managing deletion of bookmark controls

Hello,

I am implementing a Word customization that ue bookmark controls to identify pieces of text in a document.

I would like to perform some cleanup procedures when the user deletes one of the bookmark when editing the contents of the document.

It appears that the VSTO runtime has a way to keep track of this because he properly updates the Control collection in this case. For example, if I create a bookmark control using Visual Studio, then delete the range in the word design surface, the control list is automatically updated to reflect this change.

Does anyone know how I can achieve the same behavior in my customization

Any help is greatly appreciated.

Thanks

-- Cyril



Answer this question

Managing deletion of bookmark controls

  • SaurabhGangwal

    This is not a simple question to tackle.

    First thing that needs to be understood is that Word is a COM server. The COM objects exposed by Word live in a Single Threaded Apartment (STA). This means that every call into Word objects is serialized by main thread's message pump.

    The good implication from this model is that you do not have to worry about concurrent access into Word OM. However, Word's OM is not particularly re-entrant and the call may be made when Word does not expect an outside interference, so some operations can be done from a background thread and some of those will just cause Word process to crash. As a rule of thumb - it is usually safe (but not guaranteed) to perform read operations e.g. read number of the bookmarks. But it is not safe to write a text into the bookmark's range.

    The bad implication of the STA background thread is that background threads do not really give you any performance benefit because all the calls are serialized into the main thread and are essentially executed on the main thread. This means they are still blocking the execution of user UI. In your scenarion, if the scan of the document is a time consuming operation - the effect will be noticeable by the end-user. So, the architecture that will give you significant boost is if you perform one quick call into Word OM and cache the result (e.g. read the content of the entire document as an XML blob) and then you process it on the background thread without calling into Word anymore.

    VSTO uses a different approach. We do not have one single mammoth operation that we need to do. Instead we have a lot of quick checks to perform which we can not do using the events exposed by Word (Word lacks a lot of events that otherwise are very useful). The best approximation of what VSTO does is a simple System.Windows.Forms.Timer() that is invoked every interval of time (e.g. every 0.2 seconds) on the main thread. Every time the timer is invoked VSTO does one quick check and then releases control to Word, next time the timer is invoked we do another quick check and release the control again.

    The choice here is essentially up to you and depends on the task you would like to perform.

    I hope this helps.

     



  • KurtH

    I kind of figured it out, or atleast I am going to use the following. Don't know it is the best, but oh well.

    For Each prop In docProperties

    If prop.Type = MsoDocProperties.msoPropertyTypeNumber Then

    myInt = prop.Name.IndexOf("_")

    If myInt > -1 Then

    propName = prop.Name.Substring(0, myInt)

    Else

    propName = prop.Name

    End If

    For Each bk In doc.Bookmarks

    If Not bk.Name.StartsWith("PhraseBoxID") Or bk.Name.StartsWith("ImageID") Then

    myInt = bk.Name.IndexOf("_")

    If myInt > -1 Then

    bkmName = bk.Name.Substring(0, myInt)

    Else

    bkmName = bk.Name

    End If

    If bkmName = propName Then

    bkmFound = True

    Exit For

    Else

    bkmFound = False

    End If

    End If

    Next

    If bkmFound = False Then

    DeletePropertyCollection(prop.Name)

    End If

    End If

    Next


  • hackwrench

    I am not aware of any way to detect bookmark's deletion based on an event from Word OM. VSTO runtime does not track this either. However, at design time VSTO can detect when bookmark is deleted. This is achieved by constantly checking (using idle time processing) whether number of bookmarks has changed.

    Hope this helps.



  • frussell69

    Cyril

    Just wondering if you ever had figured this out   I need to do the same type of thing.  This is what I currently am trying:

    Dim index As Integer

    Dim docProperties As DocumentProperties = CType(doc.CustomDocumentProperties, DocumentProperties)

    For i As Integer = 1 To doc.Bookmarks.Count

    Dim bk As Word.Bookmark = doc.Bookmarks(i)

    For index = 1 To docProperties.Count

    Dim propValue As String = docProperties.Item(index).Value

    Dim propName As String = docProperties.Item(index).Name

    If InStr(propName, bk.Name) > 0 OrElse propName = "_AssemblyName" OrElse propName = "_AssemblyLocation" OrElse propName = "Solution ID" Then

    MsgBox("A match")

    Else

    End If

    Next index

    Next

    But it isn't exactly what I need.  How can I get this to work properly

    Thanks for your time!

    Brenda


  • adias

    Hello Misha,

    Thanks for your answer.

    Can you elaborate a bit more on the pitfalls of using a background thread to perform operations on the document

    Typically, if I have a thread that polls the number of bookmarks. When I detect a change, I will have to scan all the document to lookup for removed bookmark. While I perform this scan, how can I avoid concurrent access to the document by joining to the word thread Can you please give me some hints about how it is done in the VS designer

    Thanks

  • Jiming

    Misha,

    Thanks for your reply. The part about Word not being reentrant is scary, but I was expecting it.

    To clarify a little bit more, my concern was about the ability to execute a bulk of operations in Word thread. Even if Word is STA, I imagine that if you perform several calls to the Word OM in a method that is run on a separate thread, there is no guarantee that these calls are not interlaced with other modifications made by the user. For example, if I query the number of bookmarks, and then perform a foreach loop on the collection, there is no guarantee that the number of bookmarks has not changed between the two calls (else here is some magic in P/Invoke and COM interop that I am not aware of).

    Roughly speaking, I need a kind of "Conrol.Invoke" method that posts the whole method in Word thread. I was wondering how you solved this issue. I am going to make some experiments (including trying to use Control.Invoke on the ActionPane control, which is owned by the Word thread).

    Also, thanks for the advices on UI responsiveness and the tip about using the XML representation of the document. I will get to that if my processing reduce significantly the UI responsiveness.

    Thanks again.

  • apex1

    Brenda,

    I have not yet investigated the issue further. I have a whole stack of experiments that I must make, and this one is not yet at the top ;) ...

    By the way, due to some other reasons, I am not going to use bookmarks, but I still need a mechanism to detect changes in the document to make up for the lack of events sent by Word.

  • Managing deletion of bookmark controls