0

I am developing a VSTO in Outlook, which calls upon MSWord to create a word document, save it as a PDF after which the VSTO quits word. I am using VS2017 Professional and Office365 - version 16 of MSWord.

If MSWord is not open - the code below works like a treat. However if MSWord is open, I get a warning that "Word Cannot Save the file because it is already open elsewhere (normal.dotm)". Now I know this has been cavassed many times and I have tried all of the solutions that I can locate on StackExchange and elsewhere. The problem was previous solved by referencing answers in this question, but since a recent update of Office365, the same warning has come back.

Below is a subset of the code I am using - all filenames and so forth are valid.

                // start word to create PDF file
                Word.Application wordApp = new Word.Application();
                Word.Document wrdDoc = new Word.Document();
                object saveOption = false; 
                //Word.WdSaveOptions.wdDoNotSaveChanges;
                object oMissing = Type.Missing;
                Word.Documents d = wordApp.Documents;

                try
                { 
                    // set the word app invisible
                    wordApp.Visible = false;
                    // open the document with the tmpfilename
                    wrdDoc = d.Open(FileNameIn, Visible: true);
                    //set the page size ...
                    //wrdDoc.PageSetup.PaperSize = Word.WdPaperSize.wdPaperA4;

                }
                catch (COMException es)
                {
                    throw (es);
                }

                try
                {
                    //Save/Export our document to a PDF
                    wrdDoc.ExportAsFixedFormat(OutputFileName: FileNameOut, ExportFormat: Word.WdExportFormat.wdExportFormatPDF,
                    OpenAfterExport: false, OptimizeFor: Word.WdExportOptimizeFor.wdExportOptimizeForPrint,
                    Range: Word.WdExportRange.wdExportAllDocument, From: 0, To: 0, Item: Word.WdExportItem.wdExportDocumentContent,
                    IncludeDocProps: true, KeepIRM: true, CreateBookmarks: Word.WdExportCreateBookmarks.wdExportCreateNoBookmarks,
                    DocStructureTags: true, BitmapMissingFonts: true, UseISO19005_1: false);

                    // trick our Word App into thinking that we have been saved
                    wordApp.NormalTemplate.Saved = true;

                }
                catch (COMException es)
                {
                    // there is an actual error reporting mechanism here
                    throw (es);
                }
                finally
                {
                    try
                    {
                       // make our document think its been saved
                       wrdDoc.Saved = true;
                       // close our document
                       wrdDoc.Close(ref saveOption, ref oMissing, ref oMissing);

                       // trick our Word App into thinking that we have been saved
                       wordApp.NormalTemplate.Saved = true;
                       wordApp.Quit(ref saveOption, ref oMissing, ref oMissing);
                       // release our document object
                       Marshal.ReleaseComObject(wrdDoc);
                       // release our documents object
                       Marshal.ReleaseComObject(d);
                       // release our application object
                       Marshal.ReleaseComObject(wordApp);

                    }
                    catch (Exception es)
                    {
                       // there is an actual error reporting mechanism here
                       throw (es);
                    }
                    finally
                    {
                        //
                    }
                }

I'd be obliged for any and all assistance in resolving this issue. Many thanks in advance DWE

DWE
  • 127
  • 12
  • Can't try your code right now, but why do you have this line `Word.Document wrdDoc = new Word.Document();`? You later override wrdDoc with d.Open(), and I suspect there might be a handing instance of a new document lurking aroung. Try to remove it and see if it changes something for you. – Nick Nov 18 '18 at 09:48
  • Nick, making that change works on the computer that I am working on at the moment ... but so did the code that we're testing ... so I'll try it on the computer where it was failing tomorrow (I'm in Australia) ... do you know if this code is affected by threading or multiple monitors ? In a work environment we use two monitors and the code runs on a thread ... would either make a difference? – DWE Nov 18 '18 at 11:09
  • You must use automation only from one thread that runs in a STA. Such are the UI threads of WinForms and WPF. Never, ever use Office automation from Tasks and other threads! As for multiple monitors, we have use cases of Automating Word with on multiple monitor setups at both dev and production. No issues because of this. – Nick Nov 18 '18 at 15:03
  • Nick, I've checked the code in the working environment and it still fails on the relevant machine (with two monitors) whereas it did not fail on the machine with the single monitor; bizarre. In addition I can confirm that the code, whilst running on a thread, is running in an STA. – DWE Nov 18 '18 at 19:55

1 Answers1

0

Well I learned quite a few things over the last few days about the error I sought some assistance above.

What I learnt was that in order to properly debug a problem with Outlook and or MSWord, one should unload all third party addins that are currently running and then try to work on the problem issue. What I found in my particular case is that a program called Nuance Paperport has an addin for converting word documents to PDF which locks the normal.dotm template and doesnt abide the rules when word is requested to quit in code. Whilst it might be trivial to some, it was a valuable lesson for me.

The code to convert a word document to PDF is below and is what I have settled on using for the moment.

        /// <summary>
    /// A test to see if word is working
    /// this function takes a valid word document and converts it to a PDF 
    /// and takes the user to explorer with the converted file selected
    /// </summary>
    /// <param name="WordDocIn">a valid path\filename to an existing word document (doc, docx, rtf, htm, mhtml</param>
    /// <returns>returns False if there are no errors and the function completed successfully</returns>
    private bool WordToPDF(string WordDocIn)
    {
        // check and make sure our file parameter actually exists
        if(!File.Exists(WordDocIn))
        {
            MessageBox.Show("The file does not exist\n\n" + WordDocIn + "\n\n", "Office Assist: Error()", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return true; // true meaning there are errors
        }
        string tmpFilename = WordDocIn;
        // create a temp filename in temp directory
        string opFileName = GetTempFilePathWithExtension("pdf");
        // set our return value
        bool ErrorsPresent = false; // false meaning there are NO errors

        // create our word app
        Word.Application wordApp = new Word.Application();
        // get our documents collection (if any)
        Word.Documents d = wordApp.Documents;
        // create our new document
        Word.Document wrdDoc = new Word.Document();
        // create our word options
        object saveOption = false;// Word.WdSaveOptions.wdDoNotSaveChanges;
        // create our missing object
        object oMissing = Type.Missing; ;// Type.Missing;

        try
        {
            // set the word app visisble for the moment sp
            // we can see what we're doing
            wordApp.Visible = false;

            // suppress our alerts
            wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;

            // we dont need any addins - unload all of our addins
            // need to open word with no addins loaded ... 
            wordApp.AddIns.Unload(true);   // True to remove the unloaded add-ins from the AddIns collection 
                                            // (the names are removed from the Templates and Add-ins dialog box). 
                                            // False to leave the unloaded add-ins in the collection.

            // open the document with the tmofilename
            wrdDoc = d.Open(tmpFilename, Visible: true);
            //set the page size ... to A4 (note this will throw an error if the default printer does not produce A4)
            wrdDoc.PageSetup.PaperSize = Word.WdPaperSize.wdPaperA4;

            //
            // export our document to a PDF
            //
            wrdDoc.ExportAsFixedFormat(OutputFileName: opFileName, ExportFormat: Word.WdExportFormat.wdExportFormatPDF,
            OpenAfterExport: false, OptimizeFor: Word.WdExportOptimizeFor.wdExportOptimizeForPrint,
            Range: Word.WdExportRange.wdExportAllDocument, From: 0, To: 0, Item: Word.WdExportItem.wdExportDocumentContent,
            IncludeDocProps: true, KeepIRM: true, CreateBookmarks: Word.WdExportCreateBookmarks.wdExportCreateNoBookmarks,
            DocStructureTags: true, BitmapMissingFonts: true, UseISO19005_1: false);

            // trick our Word App into thinking that the document has been saved
            wrdDoc.Saved = true;
            // close our word document
            wrdDoc.Close(ref saveOption, ref oMissing, ref oMissing);
            // close our documents collection
            //d.Close(ref saveOption, ref oMissing, ref oMissing);

            foreach (Word.Template template in wordApp.Templates)
            {
                switch (template.Name.ToLower())
                {
                    case "normal.dotm":
                        template.Saved = true;
                        break;
                }
            }
            // quit word
            wordApp.Quit(ref saveOption, ref oMissing, ref oMissing);
        }
        // catch our errors
        catch (COMException es)
        {
            string errcode = "ComException : " + es.Message + "\n\n" + "Show this message to your admin";
            MessageBox.Show("Check the default printer has the ability to print A4\nSelect a new printer and try again\n\n" + errcode, "Office Assist: Error()", MessageBoxButtons.OK, MessageBoxIcon.Error);
            ErrorsPresent = true;
            //throw (es);
        }
        catch (Exception es)
        {
            string errcode = "Exception : " + es.Message + " \n\n " + "Show this message to your admin";
            MessageBox.Show(errcode, "Office Assist: Error()", MessageBoxButtons.OK, MessageBoxIcon.Error);
            ErrorsPresent = true;
            //throw (es);
        }
        finally
        {
            try
            {
                // release our objects
                Marshal.ReleaseComObject(wrdDoc);
                Marshal.ReleaseComObject(d);
                Marshal.ReleaseComObject(wordApp);
            }
            catch (Exception es)
            {
                string errcode = "Exception : " + es.Message + " \n\n " + "Show this message to your admin";
                MessageBox.Show(errcode, "Office Assist: Error()", MessageBoxButtons.OK, MessageBoxIcon.Error);
                ErrorsPresent = true;
            }
        }


        // if there are no errors present
        // open explorer and select the pdf created
        //
        if (!ErrorsPresent)
        {
            // set our switches
            string argument = "/select, \"" + opFileName + "\"";
            // open explorer and select the file
            Process.Start("explorer.exe", argument);
        }

        return ErrorsPresent;
    }

Given the amount of time I spent trying to resolve this problem and the amount of posts that I see which relate to converting a word document to PDF and similar natured or like issues with the norm template, I thought it was appropriate to comment on what I learnt and post the code that is working for me in the hope it will assist someone else in the future.

DWE
  • 127
  • 12