7

I am looping through a directory of several hundered excel files and trying to refresh the excel files one at a time. I keep getting this error which indicates that the refresh operation is still running on File A, for example, and FileB is trying to start a refresh operation. The loop is to fast and somehow I have to wait for the prior refresh operation on File A to complete before starting to refresh File B.

Unhandled Exception: System.Runtime.InteropServices.COMException: The message filter indicated that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER)) at Microsoft.Office.Interop.Excel._Workbook.RefreshAll() at RefreshExcelFiles.Program.RefreshFile(String fileName)

Here is my code. How can I wait for the refresh operation to complete before starting to process a new file in the loop? Or, wait for the marshal release operation on the wb object to complete before starting on a new file?

using System;
using System.Configuration;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace RefreshExcelFiles
{
    internal class Program
    {
        private static string _fileDirectory;
        private static Application _excelApp;

        private static void Main(string[] args)
        {
            _fileDirectory = ConfigurationManager.AppSettings["FileDirectory"];
            _excelApp = new Application();
            _excelApp.DisplayAlerts = false;

            Console.WriteLine("starting to refresh files");
            int i = 0;
            int total = Directory.GetFiles(_fileDirectory).Length;

            foreach (string file in Directory.GetFiles(_fileDirectory))
            {
                i += 1;
                Console.WriteLine("File " + file + " " + i.ToString() + "/" + total.ToString());
                RefreshFile(file);
            }

            _excelApp.Quit();
            Marshal.FinalReleaseComObject(_excelApp);

            Console.WriteLine("press any key to exit");
            Console.ReadLine();
        }

        private static void RefreshFile(string fileName)
        {
            _Workbook wb = _excelApp.Workbooks.Open(fileName, false);
            wb.RefreshAll();

            wb.Save();
            wb.Close(Type.Missing, Type.Missing, Type.Missing);

            Marshal.FinalReleaseComObject(wb);
        }
    }
}
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Frekster
  • 1,138
  • 1
  • 14
  • 32
  • Is this invoked in a thread? – B L Jan 22 '13 at 20:59
  • @glace no, it was not invoked in a thread. I think this was why the operation was failing - multiple threads were trying to hit the interop excel object and so the interop object was returning a "hey, I'm busy man" message and the app would just error out. By using the message filter it allowed me to keep trying the interop object until the current operation was complete and skip that error. – Frekster Jan 25 '13 at 14:13

1 Answers1

8

I found an answer to my problem. I needed to implement IMessageFilter RetryRejectedCall. For a C# and VB.NET code sample of using IMessageFilter::RetryRejectedCall, see this blog post (wayback machine archived link).

If you don't register a MessageFilter yourself (by calling CoRegisterMessageFilter), you will get default behavior which will be to fail the call if it gets rejected.  .Net converts the failure HRESULT to an exception.  To deal with the possibility of the server being busy when you try to call, you need to implement IMessageFilter::RetryRejectedCall in your client code and also register the message filter.  In most cases, you will just need to wait for a few seconds and then retry the call--generally that will be sufficient time to enable Word to finish whatever it is doing so it can handle your call.

jakegrant
  • 18
  • 5
Frekster
  • 1,138
  • 1
  • 14
  • 32
  • 1
    I was unable to get [this code](http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/70ef972b-51b6-4ece-a4af-d6b4e111eea5/) to work in a vs 2012 console application. I had to use a vs 2012 win forms app. I'm not sure if it was 100% necessary, but I set the project framework to .net 3.5 since one of the comments on the blog mentioned that the [code here](http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/70ef972b-51b6-4ece-a4af-d6b4e111eea5/) would not work on .net 4.0/4.5. – Frekster Jan 25 '13 at 14:09
  • 1
    @Frekster I had to do the same and I thought the latest VS2017 would handle Excel interops in a console app in .Net Core too! Blimy - forced to use legacy code now – Fandango68 Oct 09 '18 at 04:14