Eliminating use of '2 dots' when using Excel Interop Com Objects - C#

流过昼夜 提交于 2019-11-27 08:32:36

问题


I am having trouble releasing Excel Interop Com Objects which is causing my c# application to crash when I attempt to save and then close an Excel workbook created via Excel Interop. I feel the issue is that in some cases I am using '2 dots' with excel interop COM objects which from what I've read is not allowed. I have eliminated 2 dots from most lines of code, but I am having troulbe figuring out a way to recreate the following lines of code so that they only use one dot. If anyone has any suggestions I would greatly appreciate it.

workbook = (Excel.Workbook)app.Workbooks.Open(startForm.excelFileLocation,);

workbook = (Excel.Workbook)app.Workbooks.Add(1);

workSheet_range.Font.Color = System.Drawing.Color.FloralWhite.ToArgb();

workSheet_range.Font.Bold = font;

workSheet_range.Interior.Color = System.Drawing.Color.Red.ToArgb();

回答1:


Using two dots is not "disallowed" but it certainly can have performance impacts, especially when running in a tight loop.

Each "dot" is a COM call to the Excel library, which can be significantly slower than normal CLR object access. In general, you want to reduce the number of COM calls to as few as possible.

Reducing from two dots to one by splitting into two lines is not going to have any impact unless you reuse the same variable. For example, changing

workSheet_range.Interior.Color = System.Drawing.Color.Red.ToArgb();

to

var interior = workSheet_range.Interior;
interior.Color = System.Drawing.Color.Red.ToArgb();

will have ZERO impact on performance, and may even be "optimized" back to the original single-liner if you don't re-use the interior variable.

However, changing

var font = workSheet_range.Font;
font.Color = System.Drawing.Color.FloralWhite.ToArgb();
font.Bold = font;

will have one fewer call to workSheet_range.Font so you'll see an incremental benefit.

BOTTOM LINE

I wouldn't be too concerned about changing every two-dot call but instead use a good profiling tool to determine where your code is spending the most time, then tackle that area first.




回答2:


Verify the return type of each instruction and break them down separately.

Your first line starts with "app.Workbooks" which returns an object of type Workbooks. Then the Open instruction returns a Workbook:

workbooks = app.Workbooks;
workbook = workbooks.Open(startForm.excelFileLocation);

You can then split the 2nd like this:

workbook = workbooks.add(1);

It's ok to use multiple dots if you're not "dotting" the actual InterOp objects.

Here's a full sample:

Using Excel = Microsoft.Office.Interop.Excel;
public void Read()
{
    Excel.Application xlApp = new Excel.Application();
    Excel.Workbooks xlWorkBooks = xlApp.Workbooks;
    Excel.Workbook xlWorkBook = xlWorkBooks.Open(sourceFile);
    Excel.Worksheet xlWorkSheet = xlWorkBook.Worksheets[ 1 ];

    Excel.Range range = xlWorkSheet.UsedRange;
    range = range.Cells;
    Array myValues = ( Array )range.Value;    //now holds all the data in the sheet

    //The following is to ensure the EXCEL.EXE instance is released...
    //If you edit this code, know that using 2 dots (ex: range.Cells.Value) can create weird stuff!
    xlWorkBook.Close(false);
    xlWorkBooks.Close();
    xlApp.Quit();

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

    xlWorkSheet = null;
    xlWorkBooks = null;
    xlWorkBook = null;
    xlApp = null;
}

private static void releaseObject( object obj )
{
try
{
    System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
    obj = null;
}
catch (Exception ex)
{
    obj = null;
    Console.WriteLine("Unable to release the Object " + ex.ToString());
}
}



回答3:


Summarising all the info here.

  1. Dont use two dots when assigning.
  2. Use the AutoReleaseComObject class.
  3. Use the ReleaseObject method (that uses a while loop) described in Microsoft KB: Office application does not quit after automation from .NET client.
  4. Use GC.Collect and GC.WaitForPendingFinalizers.
  5. Don't be surprised if the Excel Process is kept alive when debugging, always test if the process is kept alive by just running the application.

eg:

using Microsoft.Office.Interop.Excel;
...
var missing = Type.Missing;
using (AutoReleaseComObject<Microsoft.Office.Interop.Excel.Application> excelApplicationWrapper = new AutoReleaseComObject<Microsoft.Office.Interop.Excel.Application>(new Microsoft.Office.Interop.Excel.Application()))
{
    var excelApplicationWrapperComObject = excelApplicationWrapper.ComObject;
    excelApplicationWrapperComObject.Visible = true;

    var excelApplicationWrapperComObjectWkBooks = excelApplicationWrapperComObject.Workbooks;
    try
    {
        using (AutoReleaseComObject<Workbook> workbookWrapper = new AutoReleaseComObject<Workbook>(excelApplicationWrapperComObjectWkBooks.Open(@"C:\Temp\ExcelMoveChart.xlsx", false, false, missing, missing, missing, true, missing, missing, true, missing, missing, missing, missing, missing)))
        {
            var workbookComObject = workbookWrapper.ComObject;
            Worksheet sheetSource = workbookComObject.Sheets["Sheet1"];
            ChartObject chartObj = (ChartObject)sheetSource.ChartObjects("Chart 3");
            Chart chart = chartObj.Chart;
            chart.Location(XlChartLocation.xlLocationAsObject, "Sheet2");

            ReleaseObject(chart);
            ReleaseObject(chartObj);
            ReleaseObject(sheetSource);

            workbookComObject.Close(false);
        }
    }
    finally
    {
        excelApplicationWrapperComObjectWkBooks.Close();
        ReleaseObject(excelApplicationWrapperComObjectWkBooks);

        excelApplicationWrapper.ComObject.Application.Quit();
        excelApplicationWrapper.ComObject.Quit();
        ReleaseObject(excelApplicationWrapper.ComObject.Application);
        ReleaseObject(excelApplicationWrapper.ComObject);

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();    
    }
}

private static void ReleaseObject(object obj)
{
    try
    {
        while (System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) > 0);
        obj = null;
    }
    catch (Exception ex)
    {
        obj = null;
        Console.WriteLine("Unable to release the Object " + ex.ToString());
    }
}

I know Releasing all the Objects, using GC.Collect and not using two dots when assigning seems over the top but at least when I quit the instance of Excel the process is freed, I don't have to programmatically Kill the Excel Process!!




回答4:


I would recommend using a library like NetOffice which will take care of releasing all resources for you (so you don't have to worry about all this COM Interop calls) and as a bonus, will give you intellisense.

You can install it via NuGet or download the assemblies from the site.



来源:https://stackoverflow.com/questions/13069153/eliminating-use-of-2-dots-when-using-excel-interop-com-objects-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!