简体   繁体   English

C# 防止用户在 excel 互操作中丢失数据

[英]C# Prevent users from losing data in excel interop

I am writing an application in C# that changes data in an excel worksheet.我正在 C# 中编写一个应用程序,该应用程序更改 excel 工作表中的数据。 VBA isn't an option because the version of office that is installed on the clients desktop is Microsoft Office 2010 Starter Edition which doesn't support VBA (none of the starter editions do). VBA 不是一个选项,因为安装在客户端桌面上的 office 版本是 Microsoft Office 2010 Starter Edition,它不支持 VBA(没有一个 starter 版本支持)。 The application is using the excel interop library.该应用程序正在使用 excel 互操作库。

When I start the application it checks to see if the excel workbook that is to be modified is open and if it is open it notifies the user and then quits.当我启动应用程序时,它会检查要修改的 excel 工作簿是否打开,如果打开,它会通知用户然后退出。 This part is working as expected.这部分按预期工作。 The check isn't working if the user opens the excel file for some reason after starting the application and then trying to save their work from inside the application.如果用户在启动应用程序后由于某种原因打开 excel 文件,然后尝试从应用程序内部保存他们的工作,则检查不起作用。 In that case any modifications from the application are lost without any error notification.在这种情况下,应用程序的任何修改都会丢失,而不会发出任何错误通知。 If you need to see more of the code to answer the entire project is in GitHub .如果您需要查看更多代码来回答整个项目在GitHub中。

I've tried changing CheckExcelWorkBookOpen from a static class to a class that gets instantiated every time it is used, just in case the list of open workbooks was being stored in the excel interop library, this did not help. I've tried changing CheckExcelWorkBookOpen from a static class to a class that gets instantiated every time it is used, just in case the list of open workbooks was being stored in the excel interop library, this did not help.

The code that works in the application start up is:在应用程序启动中工作的代码是:

CheckExcelWorkBookOpen testOpen = new CheckExcelWorkBookOpen();
testOpen.TestAndThrowIfOpen(Preferences.ExcelWorkBookFullFileSpec);

The code is also called any time the application attempts to open the file either for input or output, this doesn't work:每当应用程序尝试打开文件以进行输入或 output 时,也会调用该代码,这不起作用:

    private void StartExcelOpenWorkbook()
    {
        if (xlApp != null)
        {
            return;
        }

        CheckExcelWorkBookOpen testOpen = new CheckExcelWorkBookOpen();
        testOpen.TestAndThrowIfOpen(WorkbookName);
        xlApp = new Excel.Application();
        xlApp.Visible = false;
        xlApp.DisplayAlerts = false;

        xlWorkbook = xlApp.Workbooks.Open(WorkbookName);
    }

Current Code当前代码

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

namespace TenantRosterAutomation
{
    public class CheckExcelWorkBookOpen
    {
        // Check if there is any instance of excel open using the workbook.
        public static bool IsOpen(string workBook)
        {
            Excel.Application TestOnly = null;
            bool isOpened = true;
            // There are 2 possible exceptions here, GetActiveObject will throw
            // an exception if no instance of excel is running, and
            // workbooks.get_Item throws an exception if the sheetname isn't found.
            // Both of these exceptions indicate that the workbook isn't open.
            try
            {
                TestOnly = (Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
                int lastSlash = workBook.LastIndexOf('\\');
                string fileNameOnly = workBook.Substring(lastSlash + 1);
                TestOnly.Workbooks.get_Item(fileNameOnly);
                TestOnly = null;
            }
            catch (Exception)
            {
                isOpened = false;
                if (TestOnly != null)
                {
                    TestOnly = null;
                }
            }
            return isOpened;
        }

        // Common error message to use when the excel file is op in another app.
        public string ReportOpen()
        {
            string alreadyOpen = "The excel workbook " +
                Globals.Preferences.ExcelWorkBookFullFileSpec +
                    " is alread open in another application. \n" +
                    "Please save your changes in the other application and close the " +
                    "workbook and then try this operation again or restart this application.";

            return alreadyOpen;
        }

        public void TestAndThrowIfOpen(string workBook)
        {
            if (IsOpen(workBook))
            {
                AlreadyOpenInExcelException alreadOpen =
                    new AlreadyOpenInExcelException(ReportOpen());
                throw alreadOpen;
            }
        }
    }
}

I got the above code to work by ensuring that any excel process started by the application was killed after the task was complete.通过确保应用程序启动的任何 excel 进程在任务完成后被终止,我得到了上面的代码。 The following code is added to my ExcelInterface module.以下代码已添加到我的 ExcelInterface 模块中。 The Dispose(bool) function already existed but did not kill the process: Dispose(bool) function 已经存在但没有终止进程:

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                if (xlWorkbook != null)
                {
                    xlWorkbook.Close();
                    xlWorkbook = null;
                }

                if (xlApp != null)
                {
                    xlApp.Quit();
                    xlApp = null;
                    Process xlProcess = Process.GetProcessById(ExcelProcessId);
                    if (xlProcess != null)
                    {
                        xlProcess.Kill();
                    }
                }
            }
            disposed = true;
        }
    }

    [DllImport("user32.dll")]
    static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
    private int GetExcelProcessID(Excel.Application excelApp)
    {
        int processId;
        GetWindowThreadProcessId(excelApp.Hwnd, out processId);
        return processId;
    }

    private void StartExcelOpenWorkbook()
    {
        if (xlApp != null)
        {
            return;
        }

        CheckExcelWorkBookOpen testOpen = new CheckExcelWorkBookOpen();
        testOpen.TestAndThrowIfOpen(WorkbookName);
        xlApp = new Excel.Application();
        xlApp.Visible = false;
        xlApp.DisplayAlerts = false;

        xlWorkbook = xlApp.Workbooks.Open(WorkbookName);

        ExcelProcessId = GetExcelProcessID(xlApp);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM