Question

I have a WinForms application where I am using Word Automation to build documents via a template, and then save them to the database. After the document is created, I retrieve the document from the database, write it to the file system in a temp directory, and then open the document using the Word Interop services.

There is a list of documents loaded and the user can open only 1 instance of each document, but can open multiple different documents simultaneously. I don't have any problems with opening, saving, and closing when they open 1 document, however, when they open multiple documents simultaneously, I get the following error when closing any of my instances of Word:

The file is in use by another application or user. (C:\...\Templates\Normal.dotm) 
This error is commonly encountered when a read lock is set on the file that you are attempting to open.

I am using the following code to open the document and handle the BeforeDocumentClosed event:

public void OpenDocument(string filePath, Protocol protocol, string docTitle, byte[] document)
{
    _protocol = protocol;
    documentTitle = docTitle;
    _path = filePath;

    if (!_wordDocuments.ContainsKey(_path))
    {
        FileUtility.WriteToFileSystem(filePath, document);

        Word.Application wordApplication = new Word.Application();
        wordApplication.DocumentBeforeClose += WordApplicationDocumentBeforeClose;

        wordApplication.Documents.Open(_path);

        _wordDocuments.Add(_path, wordApplication);
    }
    _wordApplication = _wordDocuments[_path];
    _currentWordDocument = _wordApplication.ActiveDocument;

    ShowWordApplication();
}

public void ShowWordApplication()
{
    if (_wordApplication != null)
    {
        _wordApplication.Visible = true;
        _wordApplication.Activate();
        _wordApplication.ActiveWindow.SetFocus();
    }
}

private void WordApplicationDocumentBeforeClose(Document doc, ref bool cancel)
{
    if (!_currentWordDocument.Saved)
    {
        DialogResult dr = MessageHandler.ShowConfirmationYnc(String.Format(Strings.DocumentNotSavedMsg, _documentTitle), Strings.DocumentNotSavedCaption);

        switch (dr)
        {
            case DialogResult.Yes:
                SaveDocument(_path);
                break;
            case DialogResult.Cancel:
                cancel = true;
                return;
        }
    }

    try
    {
        if (_currentWordDocument != null)
            _currentWordDocument.Close();
    }
    finally
    {
        Cleanup();
    }
}

public void Cleanup()
{
    if (_currentWordDocument != null)
        while(Marshal.ReleaseComObject(_currentWordDocument) > 0);

    if (_wordApplication != null)
    {
        _wordApplication.Quit();
        while (Marshal.ReleaseComObject(_wordApplication) > 0);
        _wordDocuments.Remove(_path);
    }
}

Does anyone see anything wrong that I am doing to allow opening of multiple documents at the same time? I am fairly new to Word Automation and the Word Interop services, so any advice is appreciated. Thanks.

Was it helpful?

Solution

I found the solution via this MSDN article: http://support.microsoft.com/kb/285885

You need to do this before calling Application.Quit();

_wordApplication.NormalTemplate.Saved = true;

This prevents Word from trying to save the Normal.dotm template. I hope this helps someone else.

OTHER TIPS

I have used Word in C# doc2pdf application. Before close doc, set the save option like this:

object saveOption = Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges;
oDoc.Close(ref saveOption, ref oMissing, ref oMissing);
oWord.Quit(ref saveOption, ref oMissing, ref oMissing);

I have help links in my application and wanted to open a particular word doc to a particular bookmark. If the document is already open, it should not open it again. If Word is already open, it should not open a new instance of Word.

This code worked for me:

object filename = @"C:\Documents and Settings\blah\My Documents\chapters.docx";
object confirmConversions = false;
object readOnly = true;
object visible = true;
object missing = Type.Missing;
Application wordApp;

object word;
try
{
    word = System.Runtime.InteropServices.Marshal.GetActiveObject("Word.Application");
}
catch (COMException)
{
    Type type = Type.GetTypeFromProgID("Word.Application");
    word = System.Activator.CreateInstance(type);
}

wordApp = (Application) word;
wordApp.Visible = true;
Console.WriteLine("There are {0} documents open.", wordApp.Documents.Count);
var wordDoc = wordApp.Documents.Open(ref filename, ref confirmConversions, ref readOnly, ref missing,
                                        ref missing, ref missing, ref missing, ref missing,
                                        ref missing, ref missing, ref missing, ref visible,
                                        ref missing, ref missing, ref missing, ref missing);
wordApp.Activate(); 
object bookmarkName = "Chapter2";
if (wordDoc.Bookmarks.Exists(bookmarkName.ToString()))
{
    var bookmark = wordDoc.Bookmarks.get_Item(bookmarkName);
    bookmark.Select();
}

Keep in mind that the code:

Word.Application wordApplication = new Word.Application();

will always spin up a new instance of Word, even if there's already an instance loaded.

Usually, you're better off checking for a loaded instance (via GETOBJECT) and using it if there is one, and only spinning up a new instance if you need to.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top