none
InteropServices.COMException (This file is read-only). RRS feed

  • Question

  • I have a product that uses interop to populate word documents for customers.  It works from one document fills in the data and then inserts the document into another that collects these based on region.  I do it this way because of the volumn of documents created.  It has worked flawlessly for years.  Starting last month I get an error as soon as I try to write the data to a temporary document.  The process also works fine if one one document is produced.

    ..I'm at a loss on this one.  Any help is appreciated.

    Here is the error:System.Runtime.InteropServices.COMException (0x800A1423): This file is read-only.
    (C:\...\parent_WALL_9_15_2012_temp.docx)
       at Microsoft.Office.Interop.Word.DocumentClass.SaveAs(Object& FileName, Object& FileFormat, Object& LockComments, Object& Password, Object& AddToRecentFiles, Object& WritePassword, Object& ReadOnlyRecommended, Object& EmbedTrueTypeFonts, Object& SaveNativePictureFormat, Object& SaveFormsData, Object& SaveAsAOCELetter, Object& Encoding, Object& InsertLineBreaks, Object& AllowSubstitutions, Object& LineEnding, Object& AddBiDiMarks)
       at AttendanceCalculator.Form1.MergeDataWithWordTemplate(Application oWord, Document oFinalWordDoc, Document oWordDoc, Object oMissing, String sourceTemplatePath, String outputDocPath, DataSet sourceData, String& errorString)

    This is an excerpt of the code oMissing is Type.Missing

     try{

    //SAVE THE DOCUMENT as temp oOutputPathTemp = outputDocPath.Replace(".docx", "_temp.docx"); oWordDoc.SaveAs(ref oOutputPathTemp, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing); //CLOSE THE DOCUMENT oWordDoc.Close(ref oFalse, ref oMissing, ref oMissing); oWordDoc = null; //NOW ADD THE NEW DOC TO THE MAIN DOC oFinalWordDoc.Activate(); //make current oWord.Selection.InsertFile(oOutputPathTemp.ToString(), ref oMissing, ref oMissing, ref oMissing, ref oMissing); //Add a break if more than one document is included in the file

    if(sourceData.Tables[0].Rows.Count>0) oWord.Selection.InsertBreak(ref sectionStart); count++; } //SAVE THE FINAL DOC oFinalWordDoc.SaveAs(ref oOutputPath, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing); //CLOSE THE FINAL DOC oFinalWordDoc.Close(ref oMissing, ref oMissing, ref oMissing); oFinalWordDoc = null; //now delete the temp file

    File.Delete(oOutputPathTemp.ToString()); return true; } catch (System.Exception ex) { errorString = ex.Message; }


    Leon Faircloth

    Sunday, September 16, 2012 11:37 PM

Answers

  • Hi Leon

    Word is notorious for maintaining file locks, so that could be an issue.

    The other side of the coin is the way COM functions. Unlike .NET, things don't get garbage collected when the app no longer uses them. COM requires the developer to "clean up" pointers to objects. When code goes out of scope (application is ended) then things might get cleaned up, but may be not. This is the reason behind many so-called "memory leaks".

    Without seeing the entirety of your code, so that we can follow object declaration, instantiation and release, it's impossible to say for sure. But your thought is definitely a possibility.

    One thing you can (and should!) do when working with Office COM applications is to explicitly release all object you instantiate - an in the reverse order you intantiate them. It is correct that you do: oWordDoc = null.

    After that, GC is allowed to dispose of oWordDoc - the question is when will it do that. If you have something that's time-critical, as in this case, you should explicitly trigger GC.Collect(); GC.WaitForPendingFinalizers; twice in succession.


    Cindy Meister, VSTO/Word MVP, my blog

    Wednesday, September 19, 2012 9:03 AM
    Moderator

All replies

  • I have a product that uses interop to populate word documents for customers.  It works from one document fills in the data and then inserts the document into another that collects these based on region.  I do it this way because of the volumn of documents created.  It has worked flawlessly for years.  Starting last month I get an error as soon as I try to write the data to a temporary document.  The process also works fine if one one document is produced.

    ..I'm at a loss on this one.  Any help is appreciated.

    Here is the error:System.Runtime.InteropServices.COMException (0x800A1423): This file is read-only.
     (C:\...\parent_WALL_9_15_2012_temp.docx)
       at Microsoft.Office.Interop.Word.DocumentClass.SaveAs(Object& FileName, Object& FileFormat, Object& LockComments, Object& Password, Object& AddToRecentFiles, Object& WritePassword, Object& ReadOnlyRecommended, Object& EmbedTrueTypeFonts, Object& SaveNativePictureFormat, Object& SaveFormsData, Object& SaveAsAOCELetter, Object& Encoding, Object& InsertLineBreaks, Object& AllowSubstitutions, Object& LineEnding, Object& AddBiDiMarks)
       at AttendanceCalculator.Form1.MergeDataWithWordTemplate(Application oWord, Document oFinalWordDoc, Document oWordDoc, Object oMissing, String sourceTemplatePath, String outputDocPath, DataSet sourceData, String& errorString)

    This is an excerpt of the code oMissing is Type.Missing

     try{

    //SAVE THE DOCUMENT as temp oOutputPathTemp = outputDocPath.Replace(".docx""_temp.docx"); oWordDoc.SaveAs(ref oOutputPathTemp, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing); //CLOSE THE DOCUMENT oWordDoc.Close(ref oFalse, ref oMissing, ref oMissing); oWordDoc = null; //NOW ADD THE NEW DOC TO THE MAIN DOC oFinalWordDoc.Activate(); //make current oWord.Selection.InsertFile(oOutputPathTemp.ToString(), ref oMissing, ref oMissing, ref oMissing, ref oMissing); //Add a break if more than one document is included in the file

    if(sourceData.Tables[0].Rows.Count>0) oWord.Selection.InsertBreak(ref sectionStart); count++; } //SAVE THE FINAL DOC oFinalWordDoc.SaveAs(ref oOutputPath, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing); //CLOSE THE FINAL DOC oFinalWordDoc.Close(ref oMissing, ref oMissing, ref oMissing); oFinalWordDoc = null; //now delete the temp file

    File.Delete(oOutputPathTemp.ToString()); return true; }  catch (System.Exception ex)  {    errorString = ex.Message; } 

     

    • Moved by Hila Shemer Sunday, September 16, 2012 10:54 PM not related to apps for Office (From:Developing Apps for Office)
    • Merged by Cindy Meister MVPModerator Monday, September 17, 2012 6:18 AM same question
    Saturday, September 15, 2012 10:07 PM
  • Which version of Word is involved?

    Is this happening "everywhere" or in one place, in particular?

    Since the problem has started appearing has anything been updated or changed on the system where this is running?


    Cindy Meister, VSTO/Word MVP, my blog

    Monday, September 17, 2012 6:16 AM
    Moderator
  • Office 2010 is in use on all of the systems.  We also know that it was working during August.  We run this process at the start of each month and it Failed when attempted on August 31st.

    The basic approach is that we create a document from a template, merge data into it and save it as a "temp" file.  Then the temp file is merged into a document that was created as a holder.  The "temp document is deleted and then we repeat the process until all of the records are merged.  After completion the entire process is restarted for the next region.  Now, If I only have one record, the entire process works correctly and we move on to the next region.  The key is that the region files are saved with a new file name.  If more than one record is involved, we have a failure after writing the first record.  This seems to indicate that the "temp" file still exists even though it has been deleted.  To test this, I am going to add a unique identifier in the file so that I am not reusing the deleted reference and see if that solves the issue.  

    The failure occurs here:

    oWordDoc.SaveAs(ref oOutputPathTemp, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);


    Leon Faircloth

    Monday, September 17, 2012 2:35 PM
  • The more I look at this, it seems that I have some issue with the flow of the documents from creation to destruction.  It may be that the code was just addressing this before. 


    Leon Faircloth

    Monday, September 17, 2012 3:20 PM
  • ...Okay here is what I think is happening

    1) I am creating a word Document  with success

    2) Merging it to another Document  (failing)

    3) Closing the document and setting it to null (Questionalble)

      oWordDoc.SaveAs(ref oOutputPathTemp, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
      oWordDoc.Final = true;
      oWordDoc.Close(ref oFalse, ref oMissing, ref oMissing);
      oWordDoc = null;

    4) Attempting to delete the document for reuse (Failing)

    It seems like the processes that I am going through to release the document are not working  So my issue is with the code listed above.


    Leon Faircloth


    • Edited by fairccl Monday, September 17, 2012 3:58 PM
    Monday, September 17, 2012 3:51 PM
  • Hi Leon,

    Thanks for posting in the MSDN Forum.

    Would you please answer me whether all of the document share one application instance?

    I tried following code on my side, it works fine.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using Word = Microsoft.Office.Interop.Word;
    using log4net;
    
    namespace ConsoleApplication6
    {
        class Program
        {
            [STAThread]
            static void Main(string[] args)
            {
                try
                {
                    OpenFileDialog OFD = new OpenFileDialog();
                    OFD.Filter = "document |*.docx";
                    OFD.ShowDialog();
                    object f1 = OFD.FileName;
                    OFD.ShowDialog();
                    object f2 = OFD.FileName;
                    object f3 = @"C:\Users\v-yangxu\Desktop\RegExpVBA.docx";
                    Word.Application wdApp = new Word.Application();
                    wdApp.Visible = true;
                    Word.Document wdDoc1 = wdApp.Documents.Open(ref f1);
                    Word.Document wdDoc2 = wdApp.Documents.Open(ref f2);
                    string buffer = wdDoc1.Range().Text;
                    ((Word._Document)wdDoc1).Close();
                    wdDoc2.Activate();
                    wdDoc2.Range().Text += buffer;
                    wdDoc2.SaveAs2(ref f3);
                    ((Word._Document)wdDoc2).Close();
                }
                catch (Exception ex)
                {
                    //ILog log = log4net.LogManager.GetLogger(typeof(Program));
                    //log.Fatal(ex);
                }
                Console.ReadKey();
            }
        }
    }

    I hope it can help you.

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, September 18, 2012 7:06 AM
    Moderator
  • Yes it is one application instance.  I have a work around that solved the problem. 

    The system created a temporary file, saved it, merged it to another and then deleted the temp file.  The process was repeated for every record.   I closed the temp file, set it to null and then deleted it.  The delete was failing indicating the document was in use.  I changed to process to create a new temp file for each record merged them, closed the temp documents and then exited the method.  After the method closed I was able to delete the temp files and proceed.  Not the most elegant of solutions but it did fix the problem for now.

    My issue seems to be in closing the document.  I dropped it into a loop to see if the document was just taking a while to release and it never did. I am guessing here, but once I exited the method, I think the garbage collector may have cleaned up the objects.  Don't know for sure.

     oWordDoc.SaveAs(ref oOutputPathTemp, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
      oWordDoc.Final = true;
      oWordDoc.Close(ref oFalse, ref oMissing, ref oMissing);
      oWordDoc = null;


    Leon Faircloth

    Tuesday, September 18, 2012 6:40 PM
  • Hi Leon,

    I'm glad to hear you have solved it. Thanks for sharing it in the forum, it really helpful for other community members who have similar question.

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, September 19, 2012 7:25 AM
    Moderator
  • Hi Leon

    Word is notorious for maintaining file locks, so that could be an issue.

    The other side of the coin is the way COM functions. Unlike .NET, things don't get garbage collected when the app no longer uses them. COM requires the developer to "clean up" pointers to objects. When code goes out of scope (application is ended) then things might get cleaned up, but may be not. This is the reason behind many so-called "memory leaks".

    Without seeing the entirety of your code, so that we can follow object declaration, instantiation and release, it's impossible to say for sure. But your thought is definitely a possibility.

    One thing you can (and should!) do when working with Office COM applications is to explicitly release all object you instantiate - an in the reverse order you intantiate them. It is correct that you do: oWordDoc = null.

    After that, GC is allowed to dispose of oWordDoc - the question is when will it do that. If you have something that's time-critical, as in this case, you should explicitly trigger GC.Collect(); GC.WaitForPendingFinalizers; twice in succession.


    Cindy Meister, VSTO/Word MVP, my blog

    Wednesday, September 19, 2012 9:03 AM
    Moderator
  • Hi Leon,

    I think Cindy's analysis will better to mark as answer. Please feel free to unmark it and post you thought here.

    @Cindy,

    Thanks for you great work.

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Monday, September 24, 2012 6:58 AM
    Moderator