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

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 docPropertiesIf 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" ThenMsgBox(
"A match") Else End If Next index NextBut 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.