C# Excel互操作 - 如何测试互操作对象是否仍在工作并执行任务?

7
我正在遍历数百个Excel文件的目录,并尝试逐个刷新Excel文件。但我一直收到这个错误,指示刷新操作仍在文件A上运行,例如FileB正在尝试开始一个刷新操作。循环速度太快了,我必须等待先前对文件A的刷新操作完成后才能开始刷新文件B。
未处理的异常:System.Runtime.InteropServices.COMException: 消息过滤器指示应用程序正在忙碌中。(HRESULT 为 0x8001010A (RPC_E_SERVERCALL_RETRYLATER)的异常) 在 Microsoft.Office.Interop.Excel._Workbook.RefreshAll(),在 RefreshExcelFiles.Program.RefreshFile(String fileName)
这是我的代码。如何在开始处理循环中的新文件之前等待刷新操作完成?或者,在wb对象上的marshal释放操作完成之前等待开始处理新文件?
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);
        }
    }
}

这是在线程中调用的吗? - B L
@glace 不,它没有在线程中调用。我认为这就是操作失败的原因 - 多个线程试图访问Interop Excel对象,因此Interop对象返回“嘿,我很忙”的消息,应用程序将出现错误。通过使用消息过滤器,它允许我继续尝试Interop对象,直到当前操作完成并跳过该错误。 - Frekster
1个回答

8
我找到了解决我的问题的答案。我需要 实现 IMessageFilter RetryRejectedCall。关于使用 IMessageFilter::RetryRejectedCall 的 C# 和 VB.NET 代码示例,请 参阅此博客文章(wayback machine 存档链接)
如果您没有自己注册MessageFilter(通过调用CoRegisterMessageFilter),则会得到默认的行为,即在被拒绝时失败调用。.Net将失败的HRESULT转换为异常。为了处理在调用时可能出现服务器繁忙的情况,您需要在客户端代码中实现IMessageFilter::RetryRejectedCall,并注册消息过滤器。在大多数情况下,您只需等待几秒钟,然后重试调用,通常这将足够的时间让Word完成它正在做的工作,以便处理您的调用。

1
我无法在VS 2012控制台应用程序中运行此代码。我不得不使用VS 2012窗体应用程序。我不确定是否完全必要,但由于博客上的评论之一提到这里的代码将无法在.NET 4.0 / 4.5上工作,因此我将项目框架设置为.NET 3.5。 - Frekster
1
@Frekster 我也遇到了同样的问题,我以为最新的VS2017也能在.Net Core控制台应用程序中处理Excel interops!天哪 - 现在被迫使用旧代码。 - Fandango68

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接