代码运行时退出Interop控制的Excel

本文关键字:Excel 控制 Interop 运行时 退出 代码 | 更新日期: 2023-09-27 17:51:13

我用c#为Excel写了一个包装器,以便允许在批处理过程中运行Excel工作簿(在JobScheduler下)。我的问题是……

如果代码运行时间太长或需要通过作业调度程序终止,则包装器需要处理此终止事件。为此,我添加了

SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);

放入代码中。在ConsoleCtrlCheck方法中,我调用了一个通用的退出例程(用于正常或终止情况)。该路由按照MS的建议执行以下操作。

关闭和释放工作簿
关闭并释放Excel
垃圾收集

,然后使用被调用的Excel方法的返回代码(入口点)退出。这很有效。

但是,如果VBA代码仍在运行,互操作对象将不会响应工作簿关闭或应用程序退出调用。这可能是因为一切都进展缓慢,或者因为已经发出了模态对话框。为了解决这个问题,我在这个通用例程的开头添加了以下内容…

  • 创建新的线程来运行KillExcel方法
  • killlexcel方法休眠一段指定的时间(因此正常退出代码有机会工作),然后杀死进程
  • 如果正常代码工作,则在线程上调用Abort
private static void Cleanup()
{
    // Give this X seconds then terminate
    mKillThread = new Thread(KillExcel);
    mKillThread.IsBackground = false;
    mKillThread.Start();
    ...
    // close the workbooks and excel application
    // Marshal.FinalReleaseComObject etc
    GC.Collect();
    GC.WaitForPendingFinalizers();
    // Not sure if necessary but makes sure Process gone
    try
    {
        Process p = Process.GetProcessById(mExcelPid);
        p.WaitForExit();
    }
    catch(ArgumentException)
    {}
    mKillThread.Abort();
}
private static void KillExcel()
{
    Thread.Sleep(Settings.Default.KillWaitMilliSeconds);
    if (mLog.IsInfoEnabled)
        mLog.Info(string.Format("Waited {0} seconds, killing Excel process [{1}]",Settings.Default.KillWaitMilliSeconds/1000, mExcelPid));
    try
    {
        Process p = Process.GetProcessById(mExcelPid);
        if (!p.HasExited)
            p.Kill();
    }
    catch(ArgumentException)
    {
    }
}

我的问题是,是否有更好的方法来解决这个问题,或者这是为了确保在作业终止事件上删除excel进程所必须的方式?

代码运行时退出Interop控制的Excel

你所做的几乎是完全确保Excel退出的标准方法。考虑到涉及到COM互操作,而且excel有一个留下孤立实例的坏习惯,你所遵循的方法几乎是万无一失的。

然而,如果你可以访问vba代码并控制它,那么你可以做一件基本的事情来确保vba代码允许其他代码运行。

DoEvents

根据我的经验,在运行大量计算之前和之后放置一行代码通常可以让excel正确处理日常事件。虽然这通常被推荐用于与UI相关的操作,但它也适用于让工作表/应用程序事件正常工作。

是否有机会使用第三方库(如EPPLUS)替换VBA逻辑?那会快很多....