Link to home
Start Free TrialLog in
Avatar of JeremyHigginson
JeremyHigginson

asked on

Use an existing instance of Word from within VB .NET (2008)

I am trying to automate the creation of a Word document from an existing template and populate with data from a selected record. If possible I want to use an existing instance of Word so that the user doesn't end up with multiple instantiations which then have to be closed individually.
In Access VBA I could do this with the following code:

    Dim WordApp As Word.Application
    Set WordApp = GetObject(, "Word.Application")
    If Err.Number <> 0 Then
        Set WordApp = CreateObject("Word.Application")
    End If
    On Error GoTo ErrPoint
    WordApp.Documents.Add Template:="C:\Somewhere\Template.dot", NewTemplate:=False

This approach doesn't seem to work in VB .NET; the best I can do is this:

        Dim wordApp As Microsoft.Office.Interop.Word.Application
        On Error Resume Next
        wordApp = New Microsoft.Office.Interop.Word.Application
        On Error GoTo ErrPoint
         wordApp.Documents.Add(Template:=:="C:\Somewhere\Template.dot", NewTemplate:=False, DocumentType:=0)

But this seems to force the creation of a new instance of Word, with no possibility of piggybacking on an existing instance.
Once the document has been populated, it is then made visible to the user, who can save it somewhere if desired, or just close it. But when the Word session is closed, the message:
<<This file is in use by another application or user. (C:\Documents and
Settings\...\Normal.dot")>> appears and Word refuses to close. This can only be resolved by saving the normal.dot template under another name (normal1.dot).

So I have two problems: first I can't use an existinf Word instance (tiresome but not the end of the world) and second the user can't close Word (not acceptable) - I don't know if the second is a direct result of the first, but it never occurred in Access VBA. Why doesn't my version of VB .NET utilise the GetObject / CreateObject operators (I've seen various posting about earlier version of VB .NET and they all seem to use the GetObject/CreateObject approach), and when (and why) did the Interop prefix become necessary when defining the Word object?

Any ideas?
Avatar of Hawkvalley1
Hawkvalley1
Flag of United States of America image

I don't like the CreateObject method (though still available) as it is harder to dispose of it you do it manually - if they close it [X] that is fine. I use this format (class objects):
Private oWord As Microsoft.Office.Interop.Word.Application = Nothing
Private oDoc As Microsoft.Office.Interop.Word.Document = Nothing

Then when I open the template:
oWord = New Microsoft.Office.Interop.Word.Application
oDoc = New Microsoft.Office.Interop.Word.Document
oDoc = oWord.Documents.Add("your\File\Path.dot")

Since they class level you can call them later for same document. Word process can be hard to kill, so to guarantee disposal I use the sub below:

Private Sub ReleaseObject(ByVal o As Object)
    Dim i As Integer
    If Not o Is Nothing Then
      Try
        i = System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
        While i > 0
          i = System.Runtime.InteropServices.Marshal.ReleaseComObject(o)
        End While
      Catch
      Finally
        o = Nothing
      End Try
    End If
End Sub

Any of this help?
Avatar of JeremyHigginson
JeremyHigginson

ASKER

Thanks very much for the feedback. I'll make use of the disposal subroutine, but the main problem I have is that the Word session remains active after I've finished running my code, so that the user can view the open document. Whilst my code may be able to dispose of the object from the point of view of my app, the Word session is still running and it is when the user tries to close the Word session (independently of my app) that the problem occurs with the normal.dot template. #

I've seen lots of references to this problem, and indeed over the years I've encountered it in other (non-coding) situations. If I could understand more about what causes it, I might be able to prevent it. The text describing the error message is:
***************************
This error is commonly encountered when a read lock is set on the file you are attempting to open. This can be caused by:
Another user has the file open either on the same computer you are using or on another computer.
Word crashed at some point in the past and left a read lock on the file.
Another application has an exclusive lock on the file, which would not allow Word to open the file.
A custom application is running and has opened this file (possibly on another user's computer). It may have opened the file using an incorrect method.
To correct this problem, close all applications, restart Word and try to open this file again. If this does not work, examine the properties of the file to determine if someone else has it open. Occasionally a phantom process (dead or disassociated program) may have a lock on the file, and the only means of clearing it is to restart your computer.
***********************
The implication is that some other part of Word has placed a lock on the normal.dot template file (which I understand is used by default whenever Word starts up), but when the session closes I don't understand why the app is trying to write something back to that template file. This appears to be stored in the folder: <<C:\Users\Jeremy Higginson\AppData\Roaming\Microsoft\Templates>>, but if you try to save back to this file, it says it is read only - hence I get round this problem by saving to a random file I created called normal1.dot. Then I can close Word.

Maybe there is something about the parameters I pass to Word either when it is instantiated [wordApp = New Microsoft.Office.Interop.Word.Application] or when I create the document based on the template [wordApp.Documents.Add(Template:=:="C:\Somewhere\Template.dot", NewTemplate:=False, DocumentType:=0]. The latter seems  more likely, but it is essentially the same statement as was used in VBA, when I never had this problem. Incidentally, all references to the document are made as wordApp.ActiveDocument, and not via a specifically instantiated document object; but since I've used this approach in both the VBA case and the .NET case  I don't think this can be relevant.

Incidentally, I can still run the Access version of this on the same PC, also using Word 2003, and I experience no problems with closing Word.
I think your program has rights to it when they are trying to close it independant of the app, 1 poss solution is to disable the close button - but if they close your program -> dispose of the object -> and enable the close button. I am currently looking into this myself. See if this helps:

https://www.experts-exchange.com/questions/24549702/Disable-Word-Close-Button-Programmatically-in-VB-Net.html?sfQueryTermInfo=1+button+close+disabl+word

The above is for an open document side-by-side with app. Are you saying your document is not visible and running and then they try to open it from it's location?
Hi Hawkvalley1

The app displays a list of projects, the user can select one to view and then display the details by clicking on a button. The "display" operation within my app puts the project details into a Word document based on a template, which is made visible to the user by the app, and the user can then (indepednetly of the app) save the Word document, print it, or just close Word and dispose of it. The app continues to be available for whatever activity they next want to perform.
I've done a search of Microsoft support and there is the following article:

http://support.microsoft.com/kb/953579

It describes the cause as follows:
When you use a Word template to export a document, to print a document, or to send an e-mail in Microsoft Office Accounting Professional, in Microsoft Office Accounting Express, or in Microsoft Office Small Business Accounting 2006, you receive the following error message:
The file is in use by another Application or user. (path\Normal.dot)

And the resolution is as follows:
To resolve this problem, shut down the other instances of Word. To do this, follow these steps:
Close all open instances of Word and all applications that use Word, such as Microsoft Office Accounting Professional, Microsoft Office Accounting Express, Microsoft Office Small Business Accounting 2006, or Microsoft Outlook.
Click Start, click Run, type taskmgr, and then click OK.
On the Processes tab, click WINWORD.EXE, and then click End Process.
Click Yes.

Of course in my situation this isn't terribly helpful, but it does point the finger at the fact that the problem relates to running more than one instance of Word. (Even if there isn't an obvious instance running, it could have been initiated by Outlook if you have selected Word as your email editor of choice.)

So if anyone can tell me how to piggyback on an existing Word instance in VB .NET 2008 that would be really helpful.
Sorry, the cause for this problem is described by Microsoft as : "This problem occurs when more than one instance of Word tries to open the same Normal.dot template."
It must be the way you call the doc in your '.Add statement.' I can open multiple docs from the same template. Also the ReleaseObject Sub will definitely help close those pesky processes. My add statement: oDoc = oWord.Documents.Add("path\to\your\file.dot").
   
HTH
Don
Hi

1) These are the statements I am now using (as far as I can see, identical to yours):
        Dim wordApp As Microsoft.Office.Interop.Word.Application = Nothing
        Dim wordDocument As Microsoft.Office.Interop.Word.Document = Nothing
        Dim wordBookmark As Microsoft.Office.Interop.Word.Bookmark
        ......  
        wordApp = New Microsoft.Office.Interop.Word.Application
        wordDocument = New Microsoft.Office.Interop.Word.Document
        wordDocument = wordApp.Documents.Add(Template:=CStr(DocumentLocation))
        ......  
        For Each wordBookmark In wordDocument.Bookmarks
            Select Case wordBookmark.Name
                Case Is = "ApplicationName"
                    wordBookmark.Range.Text = DBGetTextNA(CStr(ApplicationTitle))
                 .......
        Next wordBookmark
        .......
        wordApp.Visible = True
        wordApp = Nothing
        ReleaseObject(wordApp)

All of the code to populate the document derived from the template is the same as it was in Access, so I know that there is nothing fundamentally wrong with it. But it still causes problems.

2) First of all, I have rebooted my PC so that no other Word processes can be running, then started Visual Studio, and my app in debug mode. Now when I select the menu option to generate the Word document, display the Word session and the document on screen, and then (as a user) save the document and close the Word session, I get no error message.

3) This clearly indicates to me that when there is an existing Word session running, it has locked the normal.dot template file; when I start up my parallel Word session and it tries to close itself and save to the normal.dot template file, it cannot do so. When there is no other Word session running, the problem does not occur.

4) Even if I close my VB app first and then close the Word session (whilst another one is running in parallel), I still get the problem.

5) The person who is testing this at their own (health sector) site has exactly the same problem so it is not unique to my PCs or local setup.

So I'm still stuck!

ASKER CERTIFIED SOLUTION
Avatar of Hawkvalley1
Hawkvalley1
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi again

Ultimately there's some good news, but first....

I tried the tweak to the wordDocument = code line, but  no luck.

Then I dug around and found a Microsoft support article (http://support.microsoft.com/default.aspx?scid=kb;en-us;285885) dealing with "Prompt to save Normal.dot when using Word as an automation server". It suggests two things:

1) Before you quit Word or transfer control to the user, set the Saved property of the Normal.dot template to True as follows:
Application.NormalTemplate.Saved = True
2) Set the SaveChanges argument for the Quit method as follows:
Application.Quit SaveChanges:=wdDoNotSaveChanges

The second one isn't relevant for me as I'm not quitting the Word session, but I put the line of code in for the first option.

Then I tried it on my development PC and - same as before! Then I found an MSDN article (http://msdn.microsoft.com/en-us/library/h1e6ht9c(VS.80).aspx), which contained the following text:

Word Locks Normal.dot While Open in Visual Studio
-------------------------------------------------------------
When Word is open in Visual Studio, it locks the default template Normal.dot. When you run your solution for debugging, a copy of Word is opened in another process. If you make application-level customizations to the open copy of Word, such as modifying the toolbars or menus, you cannot save those changes because Normal.dot is locked by the process that is open inside Visual Studio.
At run time, Word opens separate instances of documents in a single process, so it is not as likely that one open document will lock Normal.dot and prevent application-level changes.
For more information, see the Knowledge Base article "PRB: Prompt to Save Normal.dot When Using Word as an Automation Server" (http://support.microsoft.com/default.aspx?scid=kb;en-us;285885).

So it pointed me back to the article I'd previously found, but I thought I would try it out after I'd built my project and installed it on another test PC, and allelujah it worked!!

Now I don't know which of the changes I made actually caused the problem to go away (since I didn't think to do any testing idependently of my Visual Studio environment) - it could have been yours or it could have been Microsoft's. But I haven't currently got the strength to remove them one by one until it fails again, so I think it's only fair to give you the points anyway.

Thanks for staying with me on this one. You may find the extracts from the MS knowledge base useful if anyone else has the same kind of problem. (Incidentally I couldn't get GetObject / CreateObject to compile on my version of Visual Studio - even when I switched all the options and error messaging off, otherwise I might well have gone down this less stressful, but less perfect, route.)
Good to see you got working, I have not come across that specific problem or able reproduce it, but it is a good learning experience for all. Thanks for posting the KB articles.
Thank you for your suggestions. It also worked on my remote tester's PC, which was the acid test. Overall, though, I would consider it to be a flaw in Microsoft's coding somewhere. Also I think it would be much more sensible if you could simply piggyback on an existing Word instance if there is one; then when a user closes Word, every one of the open files will be closed in turn, rather than leaving some behind that have to be closed by switching to the second instance - which is a bit messy.