Search code examples
c#excelms-officeoffice-interopexcel-interop

How to access an already opened Excel file in C#?


I have an excel workbook opened via double-clicking it in windows explorer but cannot access it in code

Excel.Application xlApp = (Application)Marshal.GetActiveObject("Excel.Application");
Excel.Workbooks xlBooks = xlApp.Workbooks;

xlBooks.Count equals 0, why isn't it referencing my opened workbook?

EDIT

Here are the various scenarios and what is happening:

Scenario 1: If the file is not already open

  • Code opens workbook, I am happy.

Scenario 2: If the file is initially opened from code and I close and reopen the app

  • Code references file just fine xlBooks.Count equals 1, I am happy.

Scenario 3: If the file is initially opened not from code, and via double-clicking it in explorer

  • Code opens another instance of the file xlBooks.Count equals 0, I am in a rage!

Here is the entire code as it stands right now

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

public class ExcelService : IExcelService
{
    const string _filePath = @"C:\Somewhere";
    const string _fileName = @"TestFile.xlsb";
    string _fileNameAndPath = Path.Combine(_filePath, _fileName);

    Application xlApp;
    Workbooks xlBooks;
    Workbook xlBook;
    Worksheet xlSheet;

    public ExcelService()
    {
        try
        {
            xlApp = (Application)Marshal.GetActiveObject("Excel.Application");
            xlBooks = xlApp.Workbooks;

            var numBooks = xlBooks.Count;
            Log.Info("Number of workbooks: {0}".FormatWith(numBooks));
            if (numBooks > 0)
            {
                xlBook = xlBooks[1];
                Log.Info("Using already opened workbook");
            }
            else
            {
                xlBook = xlBooks.Open(_fileNameAndPath);
                Log.Info("Opening workbook: {0}".FormatWith(_fileNameAndPath));
            }

            xlSheet = (Worksheet)xlBook.Worksheets[1];

            // test reading a named range
            string value = xlSheet.Range["TEST"].Value.ToString();
            Log.Info(@"TEST: {0}".FormatWith(value));

            xlApp.Visible = true;
        }
        catch (Exception e)
        {
            Log.Error(e.Message);
        }
    }

    ~ExcelService()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();

        try
        {
            Marshal.FinalReleaseComObject(xlSheet);
        }
        catch { }

        try
        {
            Marshal.FinalReleaseComObject(xlBook);
        }
        catch { }

        try
        {
            Marshal.FinalReleaseComObject(xlBooks);
        }
        catch { }

        try
        {
            Marshal.FinalReleaseComObject(xlApp);
        }
        catch { }
    }
}

Solution

  • If all your workbooks are opened in the same Excel instance (you can check this by checking if you can switch from one to the other using Alt-tab). You can simply refer to the other using Workbooks("[FileName]"). So, for example :

    Dim wb as Workbook //for C#, type Excel.Workbook wb = null;
    Set wb = Workbooks("MyDuperWorkbook.xlsx") //for C#, type wb = Excel.Workbooks["MyDuperWorkbook.xlsx"];
    wb.Sheets(1).Cells(1,1).Value = "Wahou!"