C# Excel自动化:缺少关闭事件

C# Excel自动化:缺少关闭事件,c#,excel,events,interop,C#,Excel,Events,Interop,又一个大家好 我正在通过C#中的互操作实现Excel自动化,我希望在工作簿关闭时收到通知。但是,工作簿上没有关闭事件,应用程序上也没有退出事件 以前有人这样做过吗如何编写一段代码来对关闭的工作簿做出反应(仅当工作簿确实关闭时才执行)?理想情况下,这应该在关闭工作簿后发生,因此我可以依靠文件来反映所有更改 到目前为止我发现的详细信息: 存在BeforeClose()事件,但如果存在未保存的更改,则在询问用户是否保存更改之前会引发此事件,因此目前我可以处理该事件,我没有最终文件,也无法释放COM对象

又一个大家好

我正在通过C#中的互操作实现Excel自动化,我希望在工作簿关闭时收到通知。但是,工作簿上没有关闭事件,应用程序上也没有退出事件

以前有人这样做过吗如何编写一段代码来对关闭的工作簿做出反应(仅当工作簿确实关闭时才执行)?理想情况下,这应该在关闭工作簿后发生,因此我可以依靠文件来反映所有更改

到目前为止我发现的详细信息:

存在BeforeClose()事件,但如果存在未保存的更改,则在询问用户是否保存更改之前会引发此事件,因此目前我可以处理该事件,我没有最终文件,也无法释放COM对象,这两件事都是我需要做的。我甚至不知道工作簿是否真的会关闭,因为用户可能会选择中止关闭


然后是BeforeSave()事件。因此,如果用户选择“是”保存未保存的更改,那么BeforeSave()将在BeforeClose()之后执行。但是,如果用户选择“中止”,然后点击“文件->保存”,则执行完全相同的事件顺序。此外,如果用户选择“否”,则根本不会执行BeforeSave()。只要用户不单击这些选项中的任何一个,同样适用。

您可以同时使用这两个事件吗?在BeforeClose()上设置一个标志,然后在BeforeSave()上查看是否设置了该标志。不过,如果触发了BeforeClose()而未触发BeforeSave(),则需要一种重置方法。我不确定是否还有其他办法可以帮上忙


编辑:看起来您已经用“事件的执行顺序完全相同”来说明这一点。但是,如果你能找到一种方法来重置它(另一个“取消”事件?),它可能会工作。

我使用类似轮询的方法创建了一个黑客程序,它可以工作:

给定要观察的工作簿,我创建了一个线程,定期尝试在工作簿集合中查找该工作簿

(DisposableCom类是我当前的解决方案。)

Excel.Application app=wbWorkbook.Application;
字符串sWorkbookName=wbWorkbook.Name;
线程=新线程(新线程开始(
代表()
{
bool bOpened=错误;
Excel.Workbooks wbsWorkbooks=app.Workbooks;
使用(新的DisposableCom(wbsWorkbooks))
{
while(true)
{
睡眠(1000);
if(wbsWorkbooks.ContainsWorkbookProperly(sWorkbookName))
bOpened=真;
其他的
如果(已打开)
//工作簿已打开,因此已关闭。
打破
其他的
{
//工作簿未完全打开,请不要执行任何操作
}
}
//工作簿已关闭
在工作簿关闭()后运行代码以删除;
}
}));
检查WorkbooksThread.Start();
“ContainsworkBookProperty”扩展方法如下所示:

public static bool ContainsWorkbookProperly(this Excel.Workbooks excelWbs,
    string sWorkbookName)
{
    Excel.Workbook wbTemp = null;
    try
        wbTemp = excelWbs.Item(sWorkbookName);
    catch (Exception)
    {
        // ignore
    }

    if (wbTemp != null)
    {
        new DisposableCom<Excel.Workbook>(wbTemp).Dispose();
        return true;
    }

    return false;
}
public static bool正确地包含了SWORKBOOK(此Excel.Workbooks excelWbs,
弦剑书)
{
Excel.Workbook wbTemp=null;
尝试
wbTemp=excelWbs.项目(剑书名称);
捕获(例外)
{
//忽略
}
如果(wbTemp!=null)
{
新的DisposableCom(wbTemp.Dispose();
返回true;
}
返回false;
}

不过,如果有更简单或更好的解决方案,我还是会感兴趣。

这不是我的代码,但这对我来说很有用:

复制粘贴:

using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace Helpers.Vsto
{
    public sealed class WorkbookClosedMonitor
    {
        internal class CloseRequestInfo
        {
            public CloseRequestInfo(string name, int count)
            {
                this.WorkbookName = name;
                this.WorkbookCount = count;
            }

            public string WorkbookName { get; set; }

            public int WorkbookCount { get; set; }
        }

        public WorkbookClosedMonitor(Excel.Application application)
        {
            if (application == null)
            {
                throw new ArgumentNullException("application");
            }

            this.Application = application;

            this.Application.WorkbookActivate += Application_WorkbookActivate;
            this.Application.WorkbookBeforeClose += Application_WorkbookBeforeClose;
            this.Application.WorkbookDeactivate += Application_WorkbookDeactivate;
        }

        public event EventHandler<WorkbookClosedEventArgs> WorkbookClosed;

        public Excel.Application Application { get; private set; }

        private CloseRequestInfo PendingRequest { get; set; }

        private void Application_WorkbookDeactivate(Excel.Workbook wb)
        {
            if (this.Application.Workbooks.Count == 1)
            {
                // With only one workbook available deactivating means it will be closed
                this.PendingRequest = null;

                this.OnWorkbookClosed(new WorkbookClosedEventArgs(wb.Name));
            }
        }

        private void Application_WorkbookBeforeClose(Excel.Workbook wb, ref bool cancel)
        {
            if (!cancel)
            {
                this.PendingRequest = new CloseRequestInfo(
                    wb.Name, 
                    this.Application.Workbooks.Count);
            }
        }

        private void Application_WorkbookActivate(Excel.Workbook wb)
        {
            // A workbook was closed if a request is pending and the workbook count decreased
            bool wasWorkbookClosed = true
                && this.PendingRequest != null
                && this.Application.Workbooks.Count < this.PendingRequest.WorkbookCount;

            if (wasWorkbookClosed)
            {
                var args = new WorkbookClosedEventArgs(this.PendingRequest.WorkbookName);

                this.PendingRequest = null;

                this.OnWorkbookClosed(args);
            }
            else
            {
                this.PendingRequest = null;
            }
        }

        private void OnWorkbookClosed(WorkbookClosedEventArgs e)
        {
            var handler = this.WorkbookClosed;

            if (handler != null)
            {
                handler(this, e);
            }
        }
    }

    public sealed class WorkbookClosedEventArgs : EventArgs
    {
        internal WorkbookClosedEventArgs(string name)
        {
            this.Name = name;
        }

        public string Name { get; private set; }
    }
}
使用系统;
使用Excel=Microsoft.Office.Interop.Excel;
命名空间帮助程序.Vsto
{
公共密封类工作簿关闭监视器
{
内部类CloseRequestInfo
{
public CloseRequestInfo(字符串名称、整数计数)
{
this.WorkbookName=名称;
this.WorkbookCount=计数;
}
公共字符串工作簿名{get;set;}
公共int工作簿计数{get;set;}
}
公用工作簿关闭监视器(Excel.Application)
{
if(应用程序==null)
{
抛出新的ArgumentNullException(“应用程序”);
}
这个应用程序=应用程序;
this.Application.WorkbookActivate+=应用程序\u WorkbookActivate;
this.Application.WorkbookBeforeClose+=应用程序\u WorkbookBeforeClose;
this.Application.WorkbookDeactivate+=应用程序\u WorkbookDeactivate;
}
公共事件事件处理程序工作簿已关闭;
public Excel.Application应用程序{get;private set;}
私有CloseRequestInfo PendingRequest{get;set;}
私有作废应用程序\u工作簿停用(Excel.Workbook wb)
{
if(this.Application.Workbooks.Count==1)
{
//只有一个工作簿可用,停用意味着它将被关闭
this.PendingRequest=null;
this.OnWorkbookClosed(新的WorkbookClosedEventArgs(wb.Name));
}
}
关闭前专用作废应用程序\u工作簿(Excel.Workbook wb,ref bool cancel)
{
如果(!取消)
{
this.PendingRequest=新建CloseRequestInfo(
世界银行名称,
这是(应用程序、工作簿、计数);
}
}
专用作废应用程序\u工作簿激活(Excel.Workbook wb)
{
//如果请求挂起且工作簿计数减少,则工作簿已关闭
bool wasWorkbookClosed=true
&&this.PendingRequest!=null
&&this.Application.Workbooks.Countusing System;
using Excel = Microsoft.Office.Interop.Excel;

namespace Helpers.Vsto
{
    public sealed class WorkbookClosedMonitor
    {
        internal class CloseRequestInfo
        {
            public CloseRequestInfo(string name, int count)
            {
                this.WorkbookName = name;
                this.WorkbookCount = count;
            }

            public string WorkbookName { get; set; }

            public int WorkbookCount { get; set; }
        }

        public WorkbookClosedMonitor(Excel.Application application)
        {
            if (application == null)
            {
                throw new ArgumentNullException("application");
            }

            this.Application = application;

            this.Application.WorkbookActivate += Application_WorkbookActivate;
            this.Application.WorkbookBeforeClose += Application_WorkbookBeforeClose;
            this.Application.WorkbookDeactivate += Application_WorkbookDeactivate;
        }

        public event EventHandler<WorkbookClosedEventArgs> WorkbookClosed;

        public Excel.Application Application { get; private set; }

        private CloseRequestInfo PendingRequest { get; set; }

        private void Application_WorkbookDeactivate(Excel.Workbook wb)
        {
            if (this.Application.Workbooks.Count == 1)
            {
                // With only one workbook available deactivating means it will be closed
                this.PendingRequest = null;

                this.OnWorkbookClosed(new WorkbookClosedEventArgs(wb.Name));
            }
        }

        private void Application_WorkbookBeforeClose(Excel.Workbook wb, ref bool cancel)
        {
            if (!cancel)
            {
                this.PendingRequest = new CloseRequestInfo(
                    wb.Name, 
                    this.Application.Workbooks.Count);
            }
        }

        private void Application_WorkbookActivate(Excel.Workbook wb)
        {
            // A workbook was closed if a request is pending and the workbook count decreased
            bool wasWorkbookClosed = true
                && this.PendingRequest != null
                && this.Application.Workbooks.Count < this.PendingRequest.WorkbookCount;

            if (wasWorkbookClosed)
            {
                var args = new WorkbookClosedEventArgs(this.PendingRequest.WorkbookName);

                this.PendingRequest = null;

                this.OnWorkbookClosed(args);
            }
            else
            {
                this.PendingRequest = null;
            }
        }

        private void OnWorkbookClosed(WorkbookClosedEventArgs e)
        {
            var handler = this.WorkbookClosed;

            if (handler != null)
            {
                handler(this, e);
            }
        }
    }

    public sealed class WorkbookClosedEventArgs : EventArgs
    {
        internal WorkbookClosedEventArgs(string name)
        {
            this.Name = name;
        }

        public string Name { get; private set; }
    }
}
// put a syncContext instance somewhere you can reach it
static SynchronizationContext syncContext = SynchronizationContext.Current ?? new System.Windows.Forms.WindowsFormsSynchronizationContext();

// subscribe to workbook deactivate
workbook.Deactivate += workbook_Deactivate;


[DebuggerHidden]
private void workbook_Deactivate()
{
    // here, the workbook is still alive, but we can schedule
    // an action via the SyncContext which will execute 
    // right after the deactivate event is completed. At that 
    // point, the workbook instance (RCW) will no longer be usable
    // meaning that the workbook has been closed

    syncContext.Post(x =>
    {
        try
        {
            // will throw if workbook is gone
            workbook.Path.ToString();
        }
        catch
        {
            // handle workbook closed
        }
    }, null);
}