我有一个带有DataGridView的Windows窗体应用程序,其中“浏览”按钮允许用户选择现有的Excel文件并将数据导入数据网格视图。
我使用Interop.Excel
并在btnBrowse_Click
事件中声明了Excel.Application
,Workbook
和Worksheet
的新实例,我读了选定的Excel文件中的工作表,将工作表列表放入列表并将其绑定到用户选择要加载的工作表的ComboBox
。
接下来,当用户在ComboBox
中选择所需的工作表时,应用程序会将所选工作表中的数据加载到DataGridView
。
就导入而言,一切正常,但btnBrowse_Click
事件不会结束它启动的EXCEL流程,当用户选择所需的工作表时,我再次重新读取Excel文件为了提取数据(这次来自cbxSelectSheet ComboBox_SelectedIndexChanged
事件),这将创建一个新的EXCEL进程,直到SelectedIndexChanded事件完成,有两个EXCEL进程正在运行。但是当这个事件完成时,它会结束第一个和第二个EXCEL过程。
这是我首先点击的浏览按钮的代码。
using Excel = Microsoft.Office.Interop.Excel;
private void btnBrowse_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog folderBrowserDialog1 = new OpenFileDialog();
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
resetImportData();
txtFilePath.Clear();
FileInfo file = new FileInfo(folderBrowserDialog1.FileName);
txtFilePath.Text = file.ToString();
Excel.Application xlApp = new Excel.Application();
Excel.Workbook excelBook = xlApp.Workbooks.Open(file.ToString());
List<String> sheets = new List<String>();
sheets.Add("<Select a Sheet>");
foreach (Excel.Worksheet wSheet in excelBook.Worksheets)
{
sheets.Add(wSheet.Name);
}
// Bind the sheet list to the cbxSheetSelect
cbxSheetSelect.DataSource = sheets;
cbxSheetSelect.Select();
excelBook.Close(false);
releaseObject(xlApp);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
最后我调用一个单独的函数来释放xlApp,这是代码。
private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Exception Occurred while releasing object " + ex.ToString());
}
finally
{
GC.Collect();
}
}
接下来,当用户在cbxSelectSheet
组合框上进行选择时会触发以下事件。
private void cbxSheetSelect_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
Excel.Application xlApp = new Excel.Application();
Excel.Workbook excelBook = xlApp.Workbooks.Open(txtFilePath.Text);
Excel.Worksheet sheet = new Excel.Worksheet();
// Find the worksheet selected in cbxSheetSelect
foreach (Excel.Worksheet tmpSheet in excelBook.Worksheets)
{
if (tmpSheet.Name == cbxSheetSelect.SelectedValue.ToString())
{
sheet = tmpSheet;
break;
}
}
// Check for null cells in Row 1 and add first row to _dtItemHeader and _dtQtyHeader tables
foreach (Excel.Range c in sheet.get_Range("A1:Z1").Cells)
{
if (c.Value != null)
{
DataRow drItem = _dtItemHeader.NewRow();
drItem["Address"] = c.Address;
drItem["Name"] = c.Value;
_dtItemHeader.Rows.Add(drItem);
DataRow drQty = _dtQtyHeader.NewRow();
drQty["Address"] = c.Address;
drQty["Name"] = c.Value;
_dtQtyHeader.Rows.Add(drQty);
}
}
excelBook.Close(false);
releaseObject(xlApp);
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
这次执行releaseObject(xlApp)
时,两个EXCEL进程都结束了。
在旁注中,如果我单击浏览按钮,选择一个excel文件但不在我的ComboBox上进行选择,EXCEL进程将一直运行,直到我关闭应用程序。这个表单是在应用程序的三个深处,关闭表单并没有结束EXCEL进程,我必须关闭应用程序。
为什么当我的excelBook.Close()
和releaseObject()
事件相同时,btwBrowse_Click事件没有结束EXCEL流程?
此外,每隔一段时间没有任何模式,如果我在加载Excel数据后尝试发送新电子邮件,我的Outlook 2010将会崩溃。
此Outlook崩溃仅在我添加了从excel文件导入数据的能力后才开始发生,它发生在我尝试过该应用程序的其他工作站上,但似乎并不存在是任何模式。我有一个自定义编写的Outlook加载项,但这个加载项是用VB.NET编写的,与Excel无关。
~~~~编辑~~~~
我根据Scott Chamberlain的评论更新了我的申请,我的结果好坏参与。
这是我现在使用GC.Collect()
private void getExcelFileInfo()
{
Excel.Application xlApp = null;
Excel.Workbook xlBook = null;
Excel.Worksheet xlSheet = null;
try
{
// Declare new excel app, workbook and sheet
xlApp = new Excel.Application();
xlBook = xlApp.Workbooks.Open(txtFilePath.Text);
xlSheet = new Excel.Worksheet();
// Collect sheets in xlBook and add them to _sheets public list
foreach (Excel.Worksheet tmpSheet in xlBook.Worksheets)
_sheets.Add(tmpSheet.Name);
if (_sheets.Count > 0)
{
cbxSheetSelect.Enabled = true;
cbxSheetSelect.DataSource = _sheets;
cbxSheetSelect.SelectedIndex = 0;
}
else
{
cbxSheetSelect.Enabled = false;
cbxSheetSelect.DataSource = null;
}
}
catch (Exception ex)
{
frmMessage msgFrm = new frmMessage("An Error has occurred...", null, ex.Message + "\n" + "\n" + "Error Code: 357", "Error");
msgFrm.ShowDialog();
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
releaseObject(xlSheet);
xlBook.Close(false);
releaseObject(xlBook);
xlApp.Quit();
releaseObject(xlApp);
}
}
这种方法适用于某些工作站,但不适用于其他工作站。我在4个不同的工作站上测试了这个,它们都有相同版本的办公室。在我和工程工作站上,它完美地工作并立即结束了EXCEL流程。
但是在虚拟服务器会话(Windows Server 2008 R2)上,在表单应用程序关闭之前,它没有发布EXCEL进程。我尝试过的第四个工作站是我们的CFO工作站,我得到了与虚拟RD会话相同的结果,在表单应用程序关闭之前它没有发布EXCEL进程。
在虚拟RD会话中,我的用户没有本地管理员权限,但在我尝试的其他三个工作站上,用户都拥有本地管理员权限,包括CFO,它没有结束流程
我在这一点上有点绝望,任何想法或想法都会受到欢迎。
~~~~~第二次编辑~~~~~
此外,似乎在使用上述方法时,我的Outlook和其他一些应用程序(如Syteline ERP)不断崩溃,我不确定它是GC.Collect()
还是releaseObject()
开始导致它,但我的Outlook和ERP肯定没有崩溃,直到我将这两个添加到我的表单应用程序。崩溃是随机的,我无法复制导致它的确切问题,但到目前为止它只发生在用户使用我的表单应用程序上的导入或导出功能之后,两者都使用Interop.Excel
我尝试将其更改为:
xlBook.Close(false);
xlApp.Quit();
GC.Collect();
GC.WaitForPendingFinalizers();
但是,在表单应用程序关闭之前,这不会终止EXEC.exe进程。
~~~~~最终编辑~~~~~
在链接的重复帖子中找到答案。我在第一次阅读时错过了,所以我会在每个工作站上添加最终为我工作的代码。
基本上我将执行Excel读取的代码移动到它自己的函数中,然后我从btnBrowse_Click
事件中调用了该函数。 Hans Passant在链接答案中指出的技巧是我在哪里放GC.Collect()
命令。
在我的getExcelFileInfo()
活动中,我关闭并退出xlBook
和xlApp
,但我将GC.Collect()
命令放入finally {}
我的btnBrowse_Click
部分1}}事件。
private void btnBrowse_Click(object sender, EventArgs e)
{
try
{
getExcelFileInfo();
}
catch (Exception ex)
{
frmMessage msgFrm = new frmMessage("An Error has occurred...", null, ex.Message + "\n" + "\n" + "Error Code: 269", "Error");
msgFrm.ShowDialog();
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
private void getExcelFileInfo()
{
Excel.Application xlApp = null;
Excel.Workbook xlBook = null;
Excel.Worksheet xlSheet = null;
try
{
xlApp = new Excel.Application();
xlBook = xlApp.Workbooks.Open(txtFilePath.Text);
xlSheet = new Excel.Worksheet();
// Read the excel file
}
catch (Exception ex)
{
frmMessage msgFrm = new frmMessage("An Error has occurred...", null, ex.Message + "\n" + "\n" + "Error Code: 357", "Error");
msgFrm.ShowDialog();
}
finally
{
xlBook.Close(false);
xlApp.Quit();
}
}