none
How to deal with 'Word Object In Use' type errors RRS feed

  • General discussion

  • In several applications I've written, I've had problems accessing objects belonging to the Word application, because the Word application is using them. This usually manifests in either 'RPC_E_CALL_REJECTED' or 'RPC_E_SERVERCALL_RETRYLATER'. Since I've seen multiple people ask about this in the past, and don't think it's discussed anywhere else, I thought I'd say how I deal with it, and see if anyone has any better ideas.

    First let me say, for anyone with the luxury of dealing purely in '.docx' documents, I heartily recommend OpenXML, which much better gets around this issue.

    For any code where I know there is a high risk of coming up against this error, I surround it with a 'try' block, and in the 'catch' section start another thread, from which I can monitor the object's availability.

    For example, let's say I'm doing a hefty processing job, and while testing the application realise a fatal exception, 'RPC_E_CALL_REJECTED' is brought on by the user playing around in Word (as was the case here). I could couch the processing like so:

    public partial class Form1 : Form
    {
        Processing(Word.Document targetDoc)
        {
            try
            {
                FindAndReplace(targetDoc);
            }
            catch
            {
                DocChecker dc = new DocChecker(targetDoc);
            }
        }
    }

    My monitoring class could look like this:


    public class DocChecker
    {
        private String name;
        private Form1 primaryForm;
        private Word.Document pDoc;
        public CheckingThread workerObject;

        public DocChecker(Form1 form, Word.Document doc)
        {

            primaryForm = form;
            pDoc = doc;     
            CheckThread();
        }

        private void CheckThread()
        {
            workerObject = new CheckingThread();
            workerObject.father = this;
            Thread workerThread = new Thread(workerObject.DoWork);
            workerThread.Start();
        }

        public class CheckingThread
        {
            public void DoWork()
            {
                while (!_shouldStop)
                {
                    try
                    {
                        // Here attempt to access the object you've had the problem with. In this case, the document object
                       if (parent.
    pDoc.Paragraphs.Count > -1))
                       {

                            parent.primaryForm.Process(pDoc);

                            break;

                       }

                    }
                    catch
                    {
                        Thread.Sleep(500); // Wait half a second before retrying
                    }
                }
            }
            public void RequestStop()
            {
                _shouldStop = true;
            }
            private volatile bool _shouldStop;
            public DocChecker parent;
        }
    }

    The thread calls the processing method when the document has been freed up. Of course, in this simple example, the SearchAndReplace would have to require no tracking, so as to be re-entered again in recursive style. If it was some sort of progressive processing, as is usually the case, tracking would have to be done, perhaps with class level variables and/or boolean flags.

    This sort of thing makes it possible to maintain a list box with the names of open Word documents. When a user saves a file, they may change the file name. While the 'save as' dialog is up, you can usually not access that document. I believe doing something similar to the above the only way to maintain such a list, however inelegant it is.

    Thoughts?


    • Edited by JosephFox Saturday, March 3, 2012 9:10 AM Edit: Needed to break out of thread's 'while' loop after signaling, so the garbage collector could dispose of class/thread after processing was done
    Friday, March 2, 2012 10:46 PM