none
Suppress the Word UI when automating from Visual Studio 2010 RRS feed

  • Question

  • I have been asked to write a small Windows Forms app to loop through all the files in a folder and change the template to Normal.dot.

    The code below works pretty well, but I'd like it to run without showing the user that Word is loading - can the appearance of the Word window be suppressed?  Also, if anyone has any suggestions for improvements I would be DELIGHTED to hear them!

    Microsoft.Office.Interop.Word.

    Application ap = new Microsoft.Office.Interop.Word.Application

    ();

    Microsoft.Office.Interop.Word.

    Template

    rt = ap.NormalTemplate;

     

    try

    {

     

    Document doc = new Document

    ();

     

    // Text box contains the path to the required folder

     

    string[] fileEntries = Directory.GetFiles(this.txtSelectedFolder.Text, "*.doc", SearchOption.AllDirectories)

    ;

     

    foreach (string fileName in

    fileEntries)

    {

    doc = ap.Documents.Open(fileName, ReadOnly:

    false, Visible: false

    );

    doc.Activate();

    doc.set_AttachedTemplate(rt);

    ap.Documents.Close(SaveChanges:

    true, OriginalFormat: false, RouteDocument: false

    );

    }

    }

     

    • Moved by Cindy Meister MVPModerator Tuesday, September 27, 2011 12:37 PM not using VSTO technology (From:Visual Studio Tools for Office)
    Tuesday, September 27, 2011 10:43 AM

Answers

  • Hi Edward

    OK, then I have something new for you to test. Generally, if you don't want to specifically set a parameter in a method, it's best to pass nothing at all and let Word do its default thing. All kinds of weirdo-thingies crop up if you set parameters without intention. Also, you've set a document object, so you should use that and not globally try to close everything. my suggestion:

                           Document doc = ap.Documents.Open(fileName);
                            // If you leave this line out the code barfs
                            //doc.Activate();
                            // Set the document's template to normal.dot(m)
                            doc.set_AttachedTemplate(rt);
                            // Save and close the document
                            doc.Save();
                            doc.Close();
                            doc = null;
                            ap.Quit();
                            ap = null;
                            GC.Collect();
                            GC.WaitForPendingFinalizers();
                            GC.Collect();
                            GC.WaitForPendingFinalizers();
    //Don't use ReleaseComObject, we've already disposed of ap properly


    Cindy Meister, VSTO/Word MVP
    • Marked as answer by Edward444 Wednesday, September 28, 2011 7:31 AM
    Tuesday, September 27, 2011 5:01 PM
    Moderator

All replies

  • Hello,

    Try to comment out doc.Activate().

    You may want to modify the code to handle the Word already running case, see http://support.microsoft.com/kb/316126. In case Documents.Close(OriginalFormat: false) changes the file extension, you'll probably need to handle the scenario "the document of the same name exists in the folder".

     


    Regards from Belarus (GMT + 2),

    Andrei Smolin
    Add-in Express Team Leader
    Tuesday, September 27, 2011 12:02 PM
  • Hi Edward

    As you're not using the VSTO technology, I'm going to move this discussion to the Word for Developers forum. That's also where you'll find more people who may have a comment.

    It would also help readability of code you post if you use the forum tool for code (second button from the right in the editing toolbar) :-)

    Normally, I'd expect Word to start in a "not visible" mode when started as you're doing. But if it's showing up, then try

    ap.Visible = false;

    immediately following new Word.Application();

    And, as Andrei says, leave out any Activate() calls.


    Cindy Meister, VSTO/Word MVP
    Tuesday, September 27, 2011 12:37 PM
    Moderator
  • Thanks, Cindy and Andrei.  I'll look out for it at the other place!
    Tuesday, September 27, 2011 1:14 PM
  • Hi Andrei/Cindy

    This app is a use-once, throw-away solution to a problem that's not thought likely to recur, so I think checking to see if Word is already running is not necessary.  However, if I comment out the doc.Activate() it throws an exception:

    "This method or property is not available because a document window is not active".

    None of the documents are subsequently repaired.  Setting ap.Visible = false; doesn't make any difference either!  I've googled the exception and it appears that you can't do anything with documents that aren't opened or are opened and set to Visible=false.  I'm coming to the conclusion that you can't manipulate Word objects without some sort of screen activity.

    Any other ideas I might pursue ...?

    Thanks

    Edward

    Tuesday, September 27, 2011 1:48 PM
  • What code line produces this?

    If the template contains VBA code, check if the exception is thrown by the VBA code.

    What Word version do you use?


    Regards from Belarus (GMT + 2),

    Andrei Smolin
    Add-in Express Team Leader
    Tuesday, September 27, 2011 2:00 PM
  • Hi Edward

    1. Do NOT do this:

    Document doc = new Document

    The "new" keyword only works with Word.Application, no other kind of object. you can do this:

    Document doc = ap.Documents.Open(fileName, ReadOnly: false, Visible: false );

    See what kind of difference this makes?


    Cindy Meister, VSTO/Word MVP
    Tuesday, September 27, 2011 2:01 PM
    Moderator
  • Hi Cindy

    I tried as you suggested and the code still throws the "This method or property is not available because a document window is not active" exception.

    Hi Andrei

    I'm using v11.0.0.0 of the Microsoft.Office.Interop.Word library - it's Word 2007 SP2.  The template contains no VBA.

    Thanks

    Edward

    Tuesday, September 27, 2011 2:16 PM
  • Hi Edward

    Please show us your code with the correction, pasted into the forum's Code tool. That's the only way I'm going to be able to follow it correctly to see what's where...

    Also, please specify which line of code is causing the error.

    Normally, Word should be able to work with no problem for something like this with the windows invisible. It usually only has difficulties when something affects graphical layout, which doesn't appear should be the case, here.


    Cindy Meister, VSTO/Word MVP
    Tuesday, September 27, 2011 2:19 PM
    Moderator
  • Hi Cindy

    Here's the full text of the exception (with the button click event code shown below)

    {"This method or property is not available because a document window is not active."}

    [System.Runtime.InteropServices.COMException]: {"This method or property is not available because a document window is not active."}

    Data: {System.Collections.ListDictionaryInternal}

    HelpLink: "C:\\Program Files (x86)\\Microsoft Office\\Office12\\1033\\WDMAIN11.CHM#37373"

    InnerException: null

    Message: "This method or property is not available because a document window is not active."

    Source: "Microsoft Word"

    StackTrace: " at Microsoft.Office.Interop.Word.Documents.Close(Object& SaveChanges, Object& OriginalFormat, Object& RouteDocument)\r\n at _246_Tanners.TemplateRepair.Form1.btnRepair_Click(Object sender, EventArgs e) in C:\\Projects\\Tanners\\Source\\246-Tanners.TemplateRepair\\246-Tanners.TemplateRepair\\Form1.cs:line 65"

    TargetSite: {Void Close(System.Object ByRef, System.Object ByRef, System.Object ByRef)}

    Here's the listing for the button clicK event

            private void btnRepair_Click(object sender, EventArgs e)
            {
                Microsoft.Office.Interop.Word.Application ap = new Microsoft.Office.Interop.Word.Application();
                ap.Visible = false;
                Microsoft.Office.Interop.Word.Template rt = ap.NormalTemplate;
               // Document doc = new Document();
    
                try
                {
    
                    // Is there a path in the text box?
                    if (this.txtSelectedFolder.Text.Length <= 0)
                    {
                        MessageBox.Show("Please press the Select Folder button and navigate to the correct top-level folder");
                    }
                    else
                    {
                        // Text box contains the path to the required folder - if checked then include sub-directories
                        string[] fileEntries = (this.chkSubDirectories.Checked) ? Directory.GetFiles(this.txtSelectedFolder.Text, "*.doc", SearchOption.AllDirectories) : Directory.GetFiles(this.txtSelectedFolder.Text, "*.doc");
                        foreach (string fileName in fileEntries)
                        {
                            // Open each document in turn
                            Document doc = ap.Documents.Open(fileName, ReadOnly: false, Visible: false);
                            // If you leave this line out the code barfs
                            //doc.Activate();
                            // Set the document's template to normal.dot(m)
                            doc.set_AttachedTemplate(rt);
                            // Save and close the document
                            ap.Documents.Close(SaveChanges: true, OriginalFormat: false, RouteDocument: false); //THIS IS THE LINE THAT THROWS THE EXCEPTION
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Exception Caught: " + ex.Message); // Could be that the document is already open (/) or Word is in Memory(?) 
                }
                finally
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
    
                    //System.Runtime.InteropServices.Marshal.ReleaseComObject( doc );
                    // Ambiguity between method 'Microsoft.Office.Interop.Word._Application.Quit(ref object, ref object, ref object)' and non-method 'Microsoft.Office.Interop.Word.ApplicationEvents4_Event.Quit'. Using method group.     // ap.Quit( SaveChanges: false, OriginalFormat: false, RouteDocument: false );     
                    ((_Application)ap).Quit(SaveChanges: false, OriginalFormat: false, RouteDocument: false);
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(ap);
                    MessageBox.Show("Done and done");
                }
            }


    • Edited by Edward444 Tuesday, September 27, 2011 2:40 PM
    Tuesday, September 27, 2011 2:30 PM
  • Hi Edward

    OK, then I have something new for you to test. Generally, if you don't want to specifically set a parameter in a method, it's best to pass nothing at all and let Word do its default thing. All kinds of weirdo-thingies crop up if you set parameters without intention. Also, you've set a document object, so you should use that and not globally try to close everything. my suggestion:

                           Document doc = ap.Documents.Open(fileName);
                            // If you leave this line out the code barfs
                            //doc.Activate();
                            // Set the document's template to normal.dot(m)
                            doc.set_AttachedTemplate(rt);
                            // Save and close the document
                            doc.Save();
                            doc.Close();
                            doc = null;
                            ap.Quit();
                            ap = null;
                            GC.Collect();
                            GC.WaitForPendingFinalizers();
                            GC.Collect();
                            GC.WaitForPendingFinalizers();
    //Don't use ReleaseComObject, we've already disposed of ap properly


    Cindy Meister, VSTO/Word MVP
    • Marked as answer by Edward444 Wednesday, September 28, 2011 7:31 AM
    Tuesday, September 27, 2011 5:01 PM
    Moderator
  • Hi Cindy

    You. Are. A. Star.  That works exactly as I wanted - thank you so much for your help.

    Just in case anyone needs it, here's the complete amended listing for the function:

            private void btnRepair_Click(object sender, EventArgs e)
            {
                Microsoft.Office.Interop.Word.Application ap = new Microsoft.Office.Interop.Word.Application();
                ap.Visible = false;
                Microsoft.Office.Interop.Word.Template rt = ap.NormalTemplate;
    
                try
                {
    
                    // Is there a path in the text box?
                    if (this.txtSelectedFolder.Text.Length <= 0)
                    {
                        MessageBox.Show("Please press the Select Folder button and navigate to the correct top-level folder");
                    }
                    else
                    {
                        // Text box contains the path to the required folder - if the "Include sub-directories" check box is checked, then include sub-directories
                        string[] fileEntries = (this.chkSubDirectories.Checked) ? Directory.GetFiles(this.txtSelectedFolder.Text, "*.doc", SearchOption.AllDirectories) : Directory.GetFiles(this.txtSelectedFolder.Text, "*.doc");
                        foreach (string fileName in fileEntries)
                        {
                            Document doc = ap.Documents.Open(fileName);
    
                            // Set the document's template to normal.dot(m)
                            doc.set_AttachedTemplate(rt);
    
                            // Save and close the document
                            doc.Save();
                            ((_Document)doc).Close();
                            doc = null;
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Exception Caught: " + ex.Message); // Could be that the document is already open (/) or Word is in Memory(?) 
                }
                finally
                {
                    ((_Application)ap).Quit();
                    ap = null;
    
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
    
                    MessageBox.Show("Template repair now complete.");
                }
            }
    

     

    Wednesday, September 28, 2011 7:31 AM