Microsoft.Office.Interop.Excel真的很慢

时间:2010-10-21 15:28:41

标签: c# excel interop

我使用标准的Microsoft.Office.Interop.Excel将1200 X 800矩阵(indexMatrix)导出到excel文件。该应用程序工作,只是它真的非常慢(即使对于100 x 100矩阵)。我还通过TextWriter导出文本文件,它几乎可以立即工作。有没有办法更快地导出到excel文件?

这是我的代码:

        Excel.Application xlApp=new Excel.Application();
        Excel.Workbook xlWorkBook;
        Excel.Worksheet xlWorkSheet;
        object misValue = System.Reflection.Missing.Value;

        //xlApp = new Excel.ApplicationClass();
        xlWorkBook = xlApp.Workbooks.Add(misValue);

        xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
        for (int i = 0; i < 800; i++)   //h
            for (int j = 0; j < 1200; j++)
                xlWorkSheet.Cells[i+1,j+1] =indexMatrix[i][j];


        xlWorkBook.SaveAs("C:\\a.xls", Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
        xlWorkBook.Close(true, misValue, misValue);
        xlApp.Quit();

        releaseObject(xlWorkSheet);
        releaseObject(xlWorkBook);
        releaseObject(xlApp);

        MessageBox.Show("Excel file created , you can find the file c:\\csharp-Excel.xls");

8 个答案:

答案 0 :(得分:64)

您正在更新单个单元格。那将是非常缓慢的。如果您考虑一下,每次更新单元格时,RPC调用都将被编组到Excel进程。

如果您将二维数组值分配给单个语句中的相同维度的Excel范围(一个跨进程调用)而不是当前的1200 x 800,那么 会更快 = 960,000个跨进程调用。

类似的东西:

// Get dimensions of the 2-d array
int rowCount = indexMatrix.GetLength(0);
int columnCount = indexMatrix.GetLength(1);
// Get an Excel Range of the same dimensions
Excel.Range range = (Excel.Range) xlWorkSheet.Cells[1,1];
range = range.get_Resize(rowCount, columnCount);
// Assign the 2-d array to the Excel Range
range.set_Value(Excel.XlRangeValueDataType.xlRangeValueDefault, indexMatrix);

实际上,为了迂腐,上面的代码中有三个跨进程调用(.Cells,.get_Resize和.set_Value),并且在代码中每次迭代有两次调用(.Cells get和一个隐式的.set_Value )总计1200 x 800 x 2 = 1,920,000。

注意 首次撰写此帖子时,我正在使用的旧版Excel互操作库需要range.get_Resizerange.set_Value。现在,您可以使用@ The1nk评论中所述的range.Resizerange.Value

答案 1 :(得分:15)

Excel互操作永远不会快。您基本上是远程控制Excel应用程序的实例。您可以通过创建CSV文件然后使用Excel互操作将其转换为.xls或.xlsx文件来获得更多成功

答案 2 :(得分:8)

我在阅读一个非常大的ex​​cel文件时遇到了类似的问题,使用interop需要花费2个多小时。

我尝试使用ClosedXml,这个过程不到10秒。 ClosedXml

// To loop 
Sheet.Row(y).Cell(x).Value

另请注意,除非您安装了Excel,否则interop将无法在您的服务器上运行。 ClosedXml不需要安装excel。

答案 3 :(得分:5)

在写入任何数据之前关闭ScreenUpdatingApplication.ScreenUpdating = FALSE然后在代码= TRUE

结尾处开启

答案 4 :(得分:2)

使用Value2使其快速; 在填写数据之前显示excel

答案 5 :(得分:2)

ClosedXML 是一个奇迹,它更快更容易使用。

var workbook = new XLWorkbook();//create the new book

var worksheet = workbook.Worksheets.Add("Computer Install");// Add a sheet
worksheet.Cell(1,1).Value = "PC Name";// (Row, column) write to a cell

workbook.SaveAs(@"LIC documents.xlsx");// Save the book

使用nu Get软件包进行安装。 https://www.nuget.org/packages/ClosedXML

答案 6 :(得分:0)

有三种方法可以做到这一点,其中2种在其他人的答案中提到:

  1. 直接将excel中的范围值设置为2D数组。
  2. 将数据写入CSV文件,然后使用互操作将CSV文件另存为xls或xlsx文件。
  3. 将数据写入CSV文件,然后使用数据连接功能将CSV用作数据源并导入数据。
  4. 上述三种方法都非常快。我可以在大约6秒内写出大小为90000行和100列的数据。

    P.S。然而,他们没有解决格式化边框,字体样式,颜色,单元格合并等数据的问题。

答案 7 :(得分:0)

对不起,我来晚了。我当时在做我的项目,我们不得不使用interop excel。而且我们的数据太大,使用interop excel花费了超过1分钟的时间。我们尝试了其他方法,即将datagridview的所有内容复制到剪贴板,使用interop excel打开excel工作表,然后将内容粘贴到excel。只需不到1秒的时间,即可完美导出我们的数据。

DataGridView转换为字符串:

    var newline = System.Environment.NewLine;
    var tab = "\t";
    var clipboard_string = "";

    foreach (DataGridViewRow row in dgProjeler.Rows)
    {
        for (int i = 0; i < row.Cells.Count; i++)
        {
            if (i == (row.Cells.Count - 1))
                clipboard_string += row.Cells[i].Value + newline;
            else
                clipboard_string += row.Cells[i].Value + tab;
        }
    }

并将字符串复制到剪贴板

        Clipboard.SetText(clipboard_string);

打开工作表,粘贴内容。

        Excel.Application app = new Excel.Application();
        app.Visible = true;
        Excel.Workbook wb = app.Workbooks.Add(1);
        Excel.Worksheet ws = (Excel.Worksheet)wb.Worksheets[1];
        // changing the name of active sheet
        ws.Name = "Exported from gridview";

        ws.Rows.HorizontalAlignment = HorizontalAlignment.Center;
        app.ActiveWindow.Activate();


        ws.Activate();
        ws.Paste();

        ws.Cells.EntireColumn.AutoFit();

它对我来说非常有效,我希望它能对仍然找不到解决方案的人有所帮助。