2

I am trying to get the Columns data form an Excel file in lists by this way :

private void Form1_Load(object sender, EventArgs e)
        {
            Excel.Application xlApp = new Excel.Application();
            Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(@"D:/test.xlsx");
            Excel.Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Excel.Range xlRange = xlWorksheet.UsedRange;

            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            List<string> FirstColumnValues = new List<string>();
            List<string> SecondColumnValues = new List<string>();

            for (int row=1; row <= rowCount; row++)
            {
                for (int col = 1; col <= colCount; col++)
                {
                    switch (col)
                    {
                        case 1:
                            FirstColumnValues.Add(xlRange.Cells[row, col].Value2.ToString());
                            break;
                        case 2:
                            SecondColumnValues.Add(xlRange.Cells[row, col].Value2.ToString());
                            break;
                    }
                }
            }

            if (FirstColumnValues.Count != 0 && SecondColumnValues.Count != 0)
            {
                xlWorkbook.Close();
                xlApp.Quit();
                MessageBox.Show("Completed");
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);
                Marshal.ReleaseComObject(xlWorkbook);
                Marshal.ReleaseComObject(xlApp);
                xlApp = null;
            }
        }

The problem is, that the process EXCEL.EXE is not closing even I have tried all the things to close it properly. I know there are many questions posted here about closing excel process properly. But I am not a professional and I have tried almost everything what I can do. Still no luck.

So, Can anybody please tell me what is wrong with this code? And how the process can be closed once when all the data is stored in the lists?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
ygssoni
  • 7,219
  • 2
  • 23
  • 24

5 Answers5

6

I know this question has already been answered, but I thought I would share what I found trying to solve the same problem. Hopefully someone will find this useful. This is in vb.net, but I'm sure it can be translated.

Dim proc As System.Diagnostics.Process
        For Each proc In System.Diagnostics.Process.GetProcessesByName("EXCEL")
            If proc.MainWindowTitle.ToString = "" Then
                proc.Kill()
            End If
        Next

I found that when I was opening Excel files through my app the window title was null, so instead of closing every running Excel process I just closed the ones with no title.

Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
Lift
  • 546
  • 2
  • 4
  • 24
1

I've been there before. Here's an article that really helped me sort it out:

http://devcity.net/Articles/239/1/article.aspx

Excel seems to be very stubborn about terminating the process. You will more than likely end up killing the process using System.Diagnostics.Process.

Steven Hunt
  • 2,321
  • 19
  • 18
  • Thanks for the reply, yeah I have used the `System.Diagnostics.Process` way to close the running process. But it will close all of the excel process in the process lists. Is there a method to close the only process started by `new Excel.Application()`. ?? – ygssoni Sep 21 '12 at 12:19
  • If you don't mind keeping track of running processes, you could get a list of the excel processes before you start running COM, and then afterwards. That would give you the process associated with your COM object without getting too messy. You just have to make sure that you don't start 2 or more COM instances on separate threads, otherwise that technique won't work. – Steven Hunt Sep 21 '12 at 12:21
  • 1
    Ohk, sounds good.If I don't get any direct or short method, I will try it for sure. Thanks! :) – ygssoni Sep 21 '12 at 12:25
1

Perhaps, you might consider using try{}catch{}finally{}, and trying Marshal.FinalReleaseComObject.

Not sure if you have seen this post or not, it might give you some insight as well.

Community
  • 1
  • 1
woodykiddy
  • 6,074
  • 16
  • 59
  • 100
1

The simplest way to ensure your running Excel instance eventually terminates (i.e. when your application exits) is to wrap the top level object in a try/finally block.

Excel.Application xlApp;  
try{  
   xlApp = new Excel.Application();  
   ...do some work...  
}  
finally{  
   xlApp.DisableAlerts=True;  
   xlApp.Quit();  
   xlApp.DisableAlerts=False;  
   Marshal.ReleaseComObject(xlApp);  
}  

//If you don't mind having the Excel process kept alive for a while,  
//you don't even have to call ReleaseComObject() on the intermediate objects.  
//The runtime will eventually free the underlying COM objects.  

//If you want to clean up right away, your bookkeeping needs to be more thorough.  
//Every time you access a method or property that returns a runtime callable wrapper  
//(System.__ComObject), you'll need to assign them to a variable  
//and make sure Marshal.ReleaseComObject() is called.  

// More thorough bookkeeping...    
Excel.Application xlApp;  
try{  
   xlApp = new Excel.Application();  

   Excel.Workbooks xlWorkbooks = xlApp.Workbooks;  
   Excel.Workbook xlWorkbook = xlWorkbooks.Open(@"D:/test.xlsx");  
   Excel.Sheets xlSheets = xlWorkbook.Sheets;  
   Excel.Worksheet xlWorksheet = xlSheets[1];  
   Excel.Range xlRange = xlWorksheet.UsedRange;  

   ...inside the loop...  
   Excel.Range xlCell = xlRange.Cells[row, col];  
   FirstColumnValues.Add(xlCell.Value2.ToString());  
   Marshal.ReleaseComObject(xlCell);  

   ...inside the loop...  
   Excel.Range xlCell = xlRange.Cells[row, col];  
   SecondColumnValues.Add(xlCell.Value2.ToString());  
   Marshal.ReleaseComObject(xlCell);  

   ...do more work...  

   ...clean up...  
   Marshal.ReleaseComObject(xlRange);  
   Marshal.ReleaseComObject(xlWorksheet);  
   Marshal.ReleaseComObject(xlSheets);  
   Marshal.ReleaseComObject(xlWorkbook);  
   Marshal.ReleaseComObject(xlWorkbooks);  
}  
finally{  
   xlApp.DisableAlerts=True;  
   xlApp.Quit();  
   xlApp.DisableAlerts=False;  
   Marshal.ReleaseComObject(xlApp);  
}  
bricklayer137
  • 374
  • 2
  • 10
0

If you are going to terminate the process because you can't find the left over variables that haven't been cleaned up (basically any variable you've set using two dots), then at least kill the correct Excel instance:

[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int lpdwProcessId);

...

if (xlApp != null)
{
  GetWindowThreadProcessId(new IntPtr(xlApp.Hwnd), ref excelProcessId);

  Process ExcelProc = Process.GetProcessById(excelProcessId);
  if (ExcelProc != null)
  {
    ExcelProc.Kill();
  }

I don't advocate killing processes, you should clean up properly with VSTO Contrib or etc RCW library helper, eg:

using (var xlApp = new Microsoft.Office.Interop.Excel.Application().WithComCleanup())
  
halfer
  • 19,824
  • 17
  • 99
  • 186
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321