2

I'm trying to get C# to examine whatever workbook the user has selected and find any sheets which would contain stock data. Concretely this would mean looking at a range of cells (say r<6, c<10) for "Close", "close" or "CLOSE".

The following code shows the point at which the user has selected an .xls file.

I'm not sure how to loop through the sheets in the workbook to look for the desired text.

I'm assuming it involves creating a collection of sheets and assigning it to those in the current workbook, but my attempts so far haven't worked.

private void button1_Click(object sender, System.EventArgs e)
{
  try
  {
    OpenFileDialog dlg = new OpenFileDialog();
    dlg.Filter = "Excel Files (*.xls)|*.XLS";

    if (dlg.ShowDialog() == DialogResult.OK)
    {

       // MessageBox.Show(dlg.FileName, "My Application", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);
       Excel.Application xlApp = new Excel.ApplicationClass();
       xlApp.Visible = true;
       Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(dlg.FileName,
                0, false, 5, "", "", false, Excel.XlPlatform.xlWindows, "",
                true, false, 0, true, false, false);

     }

  }
  catch (Exception theException)
  {
    String errorMessage;
    errorMessage = "Error: ";
    errorMessage = String.Concat(errorMessage, theException.Message);
    errorMessage = String.Concat(errorMessage, " Line: ");
    errorMessage = String.Concat(errorMessage, theException.Source);

    MessageBox.Show(errorMessage, "Error");
  }
}

Thanks for any ideas.

Jeff

Doug Porter
  • 7,721
  • 4
  • 40
  • 55

4 Answers4

2

Always take extra care to clean up when using the Interop libraries. Otherwise, you're likely to end up with a couple dozen EXCEL.EXE processes running in the background while you debug (or when a user hits an error).

private static bool IsStockDataWorkbook(string fileName)
{
    Excel.Application application = null;
    Excel.Workbook workbook = null;
    try
    {
        application = new Excel.ApplicationClass();
        application.Visible = true;
        workbook = application.Workbooks.Open(fileName, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);

        foreach (Excel.Worksheet sheet in workbook.Worksheets)
        {
            if (IsStockWorksheet(sheet))
            {
                return true;
            }
        }

        return false;
    }
    finally
    {
        if (workbook != null)
        {
            workbook.Close(false, Missing.Value, Missing.Value);
        }
        if (application != null)
        {
            application.Quit();
        }
    }
}
private static bool IsStockWorksheet(Excel.Worksheet workSheet)
{
    Excel.Range testRange = workSheet.get_Range("C10", Missing.Value);
    string value = testRange.get_Value(Missing.Value).ToString();

    return value.Equals("close", StringComparison.InvariantCultureIgnoreCase);
}
Tom Mayfield
  • 6,235
  • 2
  • 32
  • 43
  • All those "Missing.Value"s are why people prefer VB.NET for Office interop, and why C# 4 has the dynamic keyword. – Tom Mayfield Jul 03 '09 at 04:01
  • Yes, it is true that you need to clean up; I didn't mention that in my sloppy answer below. My understanding is that Marshal.FinalReleaseComObject(object) should be called for every variable assigned to a COM object. I think that also means than you cannot use a foreach loop as shown here. – Jay Jul 03 '09 at 04:05
  • Crikey. And that "application.Workbooks.Open()" causes errors. Just got done reading through http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects-in-c/159419 – Tom Mayfield Jul 03 '09 at 04:14
  • Thanks, Actually in a previous version of the question (which for some reason never got posted) I mentioned that cleanup was omitted for the sake of brevity. But point well taken, not getting everything sewn up can create some nasty problems. J –  Jul 03 '09 at 04:37
  • 1
    " And that "application.Workbooks.Open()" causes errors" - as well as several other objects that aren't released - in the above code Workbook.Sheets, each sheet referenced in the for loop, and the ranges returned by each call to worksheet.get_Range. It's extremely difficult to get this right. I recommend writing a wrapper class for common simple scenarios, which implements IDisposable and does not expose any Excel objects to the caller. The wrapper is responsible for releasing every Excel reference it creates, at latest during Dispose. – Joe Jul 03 '09 at 06:34
  • Hi, It's partly for this reason that I like to stay out of the realm of interop as much as possible. Essentially I just want to grab the information and close up excel entirely. –  Jul 03 '09 at 11:16
2

You'll need to assign objSheets to something, most likely:

Excel.Sheets objSheets = xlWorkbook.Sheets;

Your foreach statement should look more like this (with no prior declaration of the ws variable):

foreach(Excel.Worksheet ws in objSheets)
{
     rng = ws.get_Range(ws.Cells[1,1], ws.Cells[5,9]);
}

Obviously, you'll want to do something more substantial in that loop.

Jay
  • 56,361
  • 10
  • 99
  • 123
1

This is an easy one :) use the sheets collection in the workbook object.

Foredecker
  • 7,395
  • 4
  • 29
  • 30
0
Workbooks workbooks = xlApp.Workbooks;
foreach(Workbook wb in workbooks)
{
    Worksheets worksheets = wb.Worksheets;
    foreach(Worksheet ws in worksheets)
    {
        Range range = ws.get_Range(ws.Cells[1,1], ws.Cells[5,9]);
        Range match = range.Find("close", ws.Cells[1,1],
            xlFindLookIn.xlValues, xlLookAt.xlPart,
            xlSearchOrder.xlByColumns, xlSearchDirection.xlNext,
            false, false, false); //that first false means ignore case
        // do something with your match here
        // this will only return the first match; to return all
        // you'll need to run the match in a while loop
    }
}
Jay
  • 56,361
  • 10
  • 99
  • 123
  • Thanks, Jay. My problem seems to be in getting the collection of worksheets to work properly. For some reason it gets hung up on the foreach statement. Everything I've tried shows up in red. Am I missing a namespace or reference perhaps? I'll keep looking at this. Thanks, Jeff –  Jul 03 '09 at 04:41