I experienced an interesting (or should that be frustrating) problem recently during an Office 2010 migration.

We have some code in Word that saves the current document into Autonomy WorkSite. The code works by doing a Save As of the current document to a temporary file, and then saving the document again to free up the first file, which is then imported or checked-in into WorkSite, all pretty standard stuff.

The code has been working fine in 2010 until we changed the default document format for Word from DOCX to DOC. As soon as we changed the document format, Word continues to hold both documents open, and therefore the import/check-in process for WorkSite fails with a “The process cannot access the file because it is being used by another process” error (the error number is -2147221472)

You can easily replicate the issue with the following VBA code which saves the current active document to two different temp files, and then tries to delete the first file:

Sub SaveAsDemo()
ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc1.doc”)
ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc2.doc”)
Kill(Environ(“TEMP”) & “\TestDoc1.doc”)
End Sub

If you have the “Save files in this format” setting set to Word Document (*.docx), the code will run fine. Now change the setting to Word 97 – 2003 Document (*.doc) and run the code, and you’ll get a Run-time Error (70), Permission denied. If you look in the temp directory, you’ll see both files are still open (they have corresponding Word temp files)

So how do you get around this “undocumented feature”?

I found a couple of blog posts that mentioned that it’s just a matter of timing, and a pause would fix this issue. In my testing, this didn’t work (since it didn’t work when I just step through the code)

What I did find that worked was just doing it all twice! So the following code works a treat:

Sub SaveAsDemo()
ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc1.doc”)
ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc2.doc”)
ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc1.doc”)
ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc2.doc”)
Kill(Environ(“TEMP”) & “\TestDoc1.doc”)
End Sub

No idea why this works – it just does!

So in the production code I added a check to see what the default save format was, and did an extra lot of saves if it was set to Doc.

Sub SaveAsDemo()
ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc1.doc”)
ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc2.doc”)
If Application.DefaultSaveFormat = “Doc” Then
            ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc1.doc”)
ActiveDocument.SaveAs2(Environ(“TEMP”) & “\TestDoc2.doc”)
End If
        Kill(Environ(“TEMP”) & “\TestDoc1.doc”)
End Sub

And there you have it, a dodgy workaround around for a very dodgy problem. Hopefully it’ll get fixed one day soon (but where would the fun in that be!)

Autonomy recently released WorkSite 8.5 SP3 Update 1, which includes updated integration for Office 2007 and 2010. The integration, so far, is much better than the old version.

The only problem is that it kills all your code that used the old iManOXP.dll (iManO2K.iManageExtensibility) but don’t worry, it’s easy to update your code to get it running.

I’ll walk through updating a Word template (VBA), but the same general principles apply to VB6 or VB.Net.

  • Firstly, open your custom Word template, go to the VBA editor and remove the reference to “WorkSite Office2000 Integration”. Add a new reference to “WorkSite Integration Interfaces Library”.
  • Now change all your objects that use the old iManO2K.iManageExtensibilty object and point instead to WorkSiteAddinInterfaces.iManageExtensibility (you could just do a search through the entire project for iManO2K.iMan and replace with WorkSiteAddinInterfaces.iMan to save hunting around)

For example:

Private WithEvents objWorkSite As iManO2K.iManageExtensibility

becomes

Private WithEvents objWorkSite As WorkSiteAddinInterfaces.iManageExtensibility

  • Then change the name used for the WorkSite COMAddin from either “iManO2K.AddinForWord2000” if you’re coming from Word 2003, or “oUTR02K.Connect” for Word 2007/2010 to “WorkSiteOffice2007Addins.Connect”.

For example:

Set objWorkSite = Application.COMAddIns(“iManO2K.AddinForWord2000”).Object

or

Set objWorkSite = Application.COMAddIns(“oUTR02K.Connect”).Object

becomes

Set objWorkSite = Application.COMAddIns(“WorkSiteOffice2007Addins.Connect”).Object

  • Next you need to update the parameters of any of the events that use the type Object, and change them to use the type Variant instead.

For example:

Private Sub objWorkSite_DocumentBeforePrint(ByVal Doc As Object, IgnoreIManagePrint As Boolean, Cancel As Boolean)

becomes

Private Sub objWorkSite_DocumentBeforePrint(ByVal Doc As Variant, IgnoreIManagePrint As Boolean, Cancel As Boolean)

You shouldn’t need to change the code within any of those subs.

And that should be it – with any luck your addins and templates will start working again. I guess I better get cracking updating all our other apps, addins and code. Keep you posted on what else I discover.

 

Update on Monday, July 4, 2011 at 10:16PM

Turns out that the members of the ConnectionMode enumerated type have changed slightly – they’ve been prefixed with ConnectionMode_.

So if you’re checking the connection mode of WorkSite, then you’d change the code from

If objWorkSite.CurrentMode = nrindeterminatemode Then objWorkSite.Connect

to

If objWorkSite.CurrentMode = ConnectionMode_nrindeterminatemode Then objWorkSite.Connect

 

Update on Wednesday, June 27, 2012 at 4:18PM

As of WorkSite 8.5 SP3 Update 5, the Office Integration is backwards compatible, so there’s no need to do any of this updating, your code should now just work. So forget about the other versions and go straight to Update 5 and newer.

Often we have clients who want Word to open without an initial document being displayed. This is usually because they don’t want the initial document based on the normal template. Doing a winword /a is no good, as it kills off all the other add-ins – so your DMS is usually killed off in the process, let alone all your other add-ins.

Life was simple in the days of Word 2003 – to get rid of the initial you could just do an ActiveDocument.Close in an AutoExec sub and you’re away. But that doesn’t work in 2010 as there no active document yet during the AutoExec.

So how did we get around it? Well it turns out we had to use the Document_Change event instead.

Within one of your startup templates, add a new Class object, call it AppEvents and insert the following code:

Option Explicit

Private WithEvents objApplication As Word.Application

Private Sub Class_Initialize()
objApplication = Word.Application
End Sub

Private Sub objApplication_DocumentChange()
On Error Resume Next
        If StartingUp Then
            If Documents.Count > 0 Then
                ‘ Close any new document created using normal.dot/normal.dotm
                If ActiveDocument.Name = ActiveDocument.Fullname And _
InStr(1, ActiveDocument.AttachedTemplate, “Normal.dot”, vbTextCompare) Then
                    ActiveDocument.Close(wdDoNotSaveChanges)
StartingUp = False
                    Exit Sub
                End If
            End If
        End If
    End Sub

Now create a new module, call it AutoMacros (or anything else you like) and add the following code:

Option Explicit

Dim oAppEvents As AppEvents
Dim StartingUp As Boolean

Sub AutoExec()
StartingUp = True
        oAppEvents = New AppEvents
End Sub

So why add the StartingUp variable? Well if we don’t have it, then any new document you try to create based on Normal will automatically close. Whilst that doesn’t sound too bad in a lot of cases, it has one bad side effect – you can’t do a compare into a new document, as the new document that gets created is always based on Normal. There are probably a few other possible bad side effects too – but we didn’t bother looking any further after that one!

Have you had a play with watermarks in Word 2007 or 2010? It looks great – a nice little graphical drop down so you can see what you’re inserting, the ability to create custom watermarks, what’s not to like?

For simple documents, this works really well. The problem is once you come to a document with multiple sections, or with different page headers. For example, set up a document with a few sections, and make sure the “Link To Previous” setting is off. Now go to any section, and insert a new watermark. See – it looks good. But now look at the other sections – they don’t have the watermark. That’s not too big a deal – so you then go to the other sections and add the watermark again. That looks good too, but now go look at the first section you put the watermark on, and it’s gone! The process removes all watermarks from all other sections, and only inserts the watermark on the new section. Now this is getting dangerous when it comes to legal documents. You get the same issues when you use different first page footers within a section.

It’s very easy to create your own custom code for inserting and deleting bookmarks, and even use the existing built-in bookmarks in 2007/2010. Turns out the bookmarks you see are just auto-text entries, and it’s the name of the entries you see in the list. For example the Draft watermarks are auto-text entries “DRAFT 1” and “DRAFT 2”. So you just need some code to insert the appropriate auto-text into the document, so look no further…

Sub InsertWatermark(ByVal WatermarkAutoTextName As String)
  If Documents.Count > 0 Then
  Application.ScreenUpdating = False
‘ Store the current location in document
ActiveDocument.Bookmarks.Add(Range:=Selection.Range, Name:=“WatermarkTempBookmark”)
‘ Load all building block templates
Templates.LoadBuildingBlocks()
‘ Find autotext entry first
Dim oTemplate As Template
Dim oAutoTextEntry As AutoTextEntry
Dim EntryFound As Boolean
Dim oSection As Section
Dim oRange As Range
EntryFound = False
For Each oTemplate In Templates
For Each oAutoTextEntry In oTemplate.AutoTextEntries
If LCase(oAutoTextEntry.Name) = LCase(WatermarkAutoTextName) Then
‘ Insert autotext in all headers in all sections of document
        For Each oSection In ActiveDocument.Sections
oRange = oSection.Headers(wdHeaderFooterPrimary).Range
oRange.Collapse(wdCollapseStart)
oAutoTextEntry.Insert(rngRange, True)
oRange = oSection.Headers(wdHeaderFooterFirstPage).Range
oRange.Collapse(wdCollapseStart)
oAutoTextEntry.Insert(rngRange, True)
oRange = oSection.Headers(wdHeaderFooterEvenPages).Range
oRange.Collapse(wdCollapseStart)
oAutoTextEntry.Insert(rngRange, True)
Next oSection
EntryFound = True
Exit For
End If
Next oAutoTextEntry
If EntryFound Then Exit For
Next oTemplate
‘ Return to original place in document
If ActiveDocument.Bookmarks.Exists(“WatermarkTempBookmark”) Then
Selection.GoTo(What:=wdGoToBookmark, Name:=“WatermarkTempBookmark”)
ActiveDocument.Bookmarks(“WatermarkTempBookmark”).Delete()
End If
Application.ScreenUpdating = True
End If
End Sub

Now just setup a sub to call the above with the appropriate autotext name, and add a button and callback to call this sub. As an example, the following sub will insert the first DRAFT watermark – “DRAFT 1”.

Sub InsertDraftWatermark()
InsertWatermark(“DRAFT 1”)
End Sub

Now all you need is a routine to remove the watermarks. That’s the easy part. All the watermarks inserted by the default autotexts contain “PowerPlusWaterMarkObject” in the name – so we just need to check the name of the shape objects throughout the document and delete them.

Sub RemoveWatermarks()
‘ Removes all Word 2010 watermarks
On Error Resume Next

  If Documents.Count > 0 Then
Dim WatermarkShape As Shape
‘ Remove from body of document – the normal 2010 option inserts into the body
For Each WatermarkShape In ActiveDocument.Shapes
If InStr(1, WatermarkShape.Name, “PowerPlusWaterMarkObject”, vbTextCompare) = 1 Then
WatermarkShape.Delete()
End If
Next
‘ Remove from headers
Dim oSection As Section
For Each oSection In ActiveDocument.Sections
For Each WatermarkShape In oSection.Headers(wdHeaderFooterPrimary).Shapes
If InStr(1, WatermarkShape.Name, “PowerPlusWaterMarkObject”, vbTextCompare) = 1 Then
WatermarkShape.Delete()
End If
Next
For Each WatermarkShape In oSection.Headers(wdHeaderFooterFirstPage).Shapes
If InStr(1, WatermarkShape.Name, “PowerPlusWaterMarkObject”, vbTextCompare) = 1 Then
WatermarkShape.Delete()
End If
Next
For Each WatermarkShape In oSection.Headers(wdHeaderFooterEvenPages).Shapes
If InStr(1, WatermarkShape.Name, “PowerPlusWaterMarkObject”, vbTextCompare) = 1 Then
WatermarkShape.Delete()
End If
Next
Next oSection
End If
End Sub

So there you have it, code to insert and remove watermarks throughout the entire document, no matter how you setup your sections and headers. The code could probably be done a little better. Also there is the issue of creating custom watermarks. And then there’s the question of getting this functionality into the ribbon. If you need the answers to all those questions (and more) then get in touch with us. You might be surprised what we have hidden up our sleeves (just wish there were some descent cards up there – I still can’t manage a win at poker, or do a card trick that slightly impresses the kids)

 

Update on Monday, February 10, 2014 at 10:32PM

Just to let you all know that this issue has been fixed in Word 2013.

© Copyright - Mosmar