我通过C#试图Excel的自动化。 我按照所有从微软如何去这样的指示,但我仍然在努力放弃最后的参考(S)到Excel它关闭,使GC收集它。
一个代码示例如下。 当我注释掉的代码块中包含类似于行:
Sheet.Cells[iRowCount, 1] = data["fullname"].ToString();
然后将文件保存并退出Excel。 否则,文件保存,但Excel中是左作为进程运行。 这段代码运行它下一次创建一个新的实例,他们最终建立。
任何帮助表示赞赏。 谢谢。
这是我的代码的准系统:
Excel.Application xl = null;
Excel._Workbook wBook = null;
Excel._Worksheet wSheet = null;
Excel.Range range = null;
object m_objOpt = System.Reflection.Missing.Value;
try
{
// open the template
xl = new Excel.Application();
wBook = (Excel._Workbook)xl.Workbooks.Open(excelTemplatePath + _report.ExcelTemplate, false, false, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);
wSheet = (Excel._Worksheet)wBook.ActiveSheet;
int iRowCount = 2;
// enumerate and drop the values straight into the Excel file
while (data.Read())
{
wSheet.Cells[iRowCount, 1] = data["fullname"].ToString();
wSheet.Cells[iRowCount, 2] = data["brand"].ToString();
wSheet.Cells[iRowCount, 3] = data["agency"].ToString();
wSheet.Cells[iRowCount, 4] = data["advertiser"].ToString();
wSheet.Cells[iRowCount, 5] = data["product"].ToString();
wSheet.Cells[iRowCount, 6] = data["comment"].ToString();
wSheet.Cells[iRowCount, 7] = data["brief"].ToString();
wSheet.Cells[iRowCount, 8] = data["responseDate"].ToString();
wSheet.Cells[iRowCount, 9] = data["share"].ToString();
wSheet.Cells[iRowCount, 10] = data["status"].ToString();
wSheet.Cells[iRowCount, 11] = data["startDate"].ToString();
wSheet.Cells[iRowCount, 12] = data["value"].ToString();
iRowCount++;
}
DirectoryInfo saveTo = Directory.CreateDirectory(excelTemplatePath + _report.FolderGuid.ToString() + "\\");
_report.ReportLocation = saveTo.FullName + _report.ExcelTemplate;
wBook.Close(true, _report.ReportLocation, m_objOpt);
wBook = null;
}
catch (Exception ex)
{
LogException.HandleException(ex);
}
finally
{
NAR(wSheet);
if (wBook != null)
wBook.Close(false, m_objOpt, m_objOpt);
NAR(wBook);
xl.Quit();
NAR(xl);
GC.Collect();
}
private void NAR(object o)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(o);
}
catch { }
finally
{
o = null;
}
}
更新
不管我怎么努力,在“干净”的方法或“丑陋”的方法(见下面的答案),则Excel实例仍然只要这条线被击中徘徊:
wSheet.Cells[iRowCount, 1] = data["fullname"].ToString();
如果我评论说,线路输出(和它下面的其他类似的人,很明显)Excel的应用程序正常退出。 只要按照上面一行是注释掉,Excel的支左右。
我想我将不得不检查是否有之前分配XL变量的运行实例和钩成代替。 我忘了说,这是一个窗口服务,但不应该的问题,应该吗?
Answer 1:
UPDATE(2016年11月)
我刚刚读了有说服力的论据由Hans帕桑特,使用GC.Collect
实际上是正确的道路要走。 我不再与Office(谢天谢地)工作,但如果我做了我可能会想给这一次机会 - 这肯定会简化很多的代码(千行)我写试图做的事情“正确的“办法(因为我看到了它,然后)。
我会留下我的后人原来的答复......
正如迈克在他回答说,有一个简单的方法和硬的方式来处理这个问题。 麦克建议使用简单的方法,因为......它更容易。 我不个人认为这是一个好足够的理由,我不相信这是正确的方式。 它之嫌“将其关闭并重新开启”给我。
我有开发.NET中的办公自动化应用的数年经验,而这些COM互操作问题困扰我的头几个星期和几个月,当我第一次跑进问题,这不仅是因为微软即将承认很忸怩有中存在的问题首先,和在当时很好的建议是很难在网上找到。
我有工作,我现在几乎不使用思考它的方式,它的年以来我有一个问题。 它仍然是重要的是要活到所有隐藏的对象,你可能会创造 - 是的,如果你错过了一个,你可能有泄漏,只有变得明显要晚得多。 但它不逊于用过的东西要在坏日子malloc
/ free
。
我不认为有什么东西对自己后清理,当您去,而不是在结束时说。 如果你只启动Excel来填补一些细胞,那么也许它并不重要 - 但如果你打算做一些繁重的,那么这是一个不同的问题。
无论如何,我使用的技术是使用实现一个包装类IDisposable
,并且在其Dispose
方法调用ReleaseComObject
。 这样,我可以使用using
语句,尽快确保对象布置(与COM对象释放),因为我完成它。
最重要的是,它会得到处理/释放,即使我的功能早返回,或也有一个例外,等等,如果它被摆在首位实际创建它只会得到处理/发布-叫我书呆子,但建议试图释放实际上可能没有被创建对象的代码在我看来就像马虎代码。 我有一个类似的反对使用FinalReleaseComObject - 你应该知道你有多少次引发了COM参考的创作,因此应该能够释放它相同的次数。
我的代码的典型片段可能会是这样的(或将它,如果我是用C#v2和可以使用泛型:-)):
using (ComWrapper<Excel.Application> application = new ComWrapper<Excel.Application>(new Excel.Application()))
{
try
{
using (ComWrapper<Excel.Workbooks> workbooks = new ComWrapper<Excel.Workbooks>(application.ComObject.Workbooks))
{
using (ComWrapper<Excel.Workbook> workbook = new ComWrapper<Excel.Workbook>(workbooks.ComObject.Open(...)))
{
using (ComWrapper<Excel.Worksheet> worksheet = new ComWrapper<Excel.Worksheet>(workbook.ComObject.ActiveSheet))
{
FillTheWorksheet(worksheet);
}
// Close the workbook here (see edit 2 below)
}
}
}
finally
{
application.ComObject.Quit();
}
}
现在,我不会假装说是不罗嗦了,并引起对象创建的压痕可能失控的,如果你不分裂的东西成更小的方法。 这个例子是一个最坏情况的东西,因为我们所做的是创建对象。 通常有很多更括号和开销之间的事情要少得多。
请注意,按照上面的例子中我总是会通过“包裹”对象的方法,绝不是赤裸裸的COM对象之间,这将是主叫方处置它(通常有责任using
语句)。 同样,我总是会返回一个包装的对象,从来没有一个裸体之一,它再次将主叫方释放它的责任。 你可以使用不同的协议,但有明确的规则是很重要的,只是因为它是当我们曾经有过做我们自己的内存管理。
所述ComWrapper<T>
这里使用的类有希望需要少量的解释。 它简单地存储要被包装的COM对象的引用,并明确地将其释放(使用ReleaseComObject
)在其Dispose
方法。 该ComObject
方法简单地返回类型化的参考包装的COM对象。
希望这可以帮助!
编辑 :我现在才跟着过来的链接麦克的回答另外一个问题 ,我看到的另一个问题的答案有有一个链接到一个包装类,就像我上面的建议。
此外,关于小李的答案是另外一个问题,我必须说,我是非常接近的“只使用引诱GC.Collect
”的说法。 不过,我主要是吸引到一个错误的前提; 它乍一看好像就没有必要担心在所有的COM引用。 然而,由于迈克说你仍然需要明确地释放你的所有调查范围内的变量相关的COM对象 - 等你所做的一切是减少而不是消除对COM对象管理的需要。 就个人而言,我宁愿干到底。
我还注意到,在许多答案的倾向,写代码,其中的一切都将在方法月底公布,在一大块ReleaseComObject
调用。 这一切都非常好,如果一切按计划进行,但我敦促所有希望写严肃代码考虑,如果该方法有几个出口点(代码将不会被执行,从而COM对象,如果一个异常被抛出会发生什么,或会不会被释放)。 这就是为什么我倾向于使用“包装”和using
秒。 这是罗嗦,但它确实使防弹代码。
EDIT2:我已经更新上面的代码,以指示工作簿要带或不保存变化就关闭。 下面的代码保存更改:
object saveChanges = Excel.XlSaveAction.xlSaveChanges;
workbook.ComObject.Close(saveChanges, Type.Missing, Type.Missing);
......并且不保存更改,只需更改xlSaveChanges
到xlDoNotSaveChanges
。
Answer 2:
正在发生的事情是,你的呼叫:
Sheet.Cells[iRowCount, 1] = data["fullname"].ToString();
在本质上是一样的:
Excel.Range cell = Sheet.Cells[iRowCount, 1];
cell.Value = data["fullname"].ToString();
通过做这种方式,你可以看到你正在创建Excel.Range
对象,然后将值分配给它。 这样也给了我们一个名为参考我们的产品范围变量时, cell
变,允许如果我们希望我们直接将其释放。 所以,你可以清理你的对象的方式有两种:
(1)困难且难看的方法:
while (data.Read())
{
Excel.Range cell = Sheet.Cells[iRowCount, 1];
cell.Value = data["fullname"].ToString();
Marshal.FinalReleaseComObject(cell);
cell = Sheet.Cells[iRowCount, 2];
cell.Value = data["brand"].ToString();
Marshal.FinalReleaseComObject(cell);
cell = Sheet.Cells[iRowCount, 3];
cell.Value = data["agency"].ToString();
Marshal.FinalReleaseComObject(cell);
// etc...
}
在上文中,我们通过一个释放呼叫的每个范围目的是Marshal.FinalReleaseComObject(cell)
,因为我们走。
(2)简单,干净的方式:
留下你的代码完全按照您目前拥有它,然后在年底可以清理如下:
GC.Collect();
GC.WaitForPendingFinalizers();
if (wSheet != null)
{
Marshal.FinalReleaseComObject(wSheet)
}
if (wBook != null)
{
wBook.Close(false, m_objOpt, m_objOpt);
Marshal.FinalReleaseComObject(wBook);
}
xl.Quit();
Marshal.FinalReleaseComObject(xl);
总之,现有的代码是非常接近 。 如果你只需要添加到GC.Collect的()和你的“NAR”调用之前GC.WaitForPendingFinalizers()调用,我认为它应该为你工作。 (总之,无论杰米的代码和艾哈迈德的代码是正确的。杰米的是清洁,但艾哈迈德的代码是一个更简单的“速战速决”为你,因为你将只需要调用添加到)调用GC.Collect的(和GC.WaitForPendingFinalizers (),以现有的代码。)
杰米和Amhad还列出链接到.NET自动化论坛 ,我在参加这里有一对夫妇,我在这里做的相关的帖子(谢谢你们!) StackOverflow的 :
(1) 如何正确清理在C#中的Excel互操作对象
(2) C#自动化PowerPoint中的Excel - PowerPoint不退出
我希望这可以帮助,肖恩·...
麦克风
Answer 3:
您的来电xl.Quit()之前添加以下内容:
GC.Collect();
GC.WaitForPendingFinalizers();
您还可以使用Marshal.FinalReleaseComObject()
在您的NAR方法,而不是ReleaseComObject的。 ReleaseComObject的减1,引用计数而FinalReleaseComObject释放所有引用,这样的计数为0。
所以,你的finally块将如下所示:
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
NAR(wSheet);
if (wBook != null)
wBook.Close(false, m_objOpt, m_objOpt);
NAR(wBook);
xl.Quit();
NAR(xl);
}
更新NAR方法:
private void NAR(object o)
{
try
{
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(o);
}
catch { }
finally
{
o = null;
}
}
一段时间以前,我研究过这一点,并在实例,我发现平时GC相关的电话都是在年底关闭该应用程序后。 然而,有这么提到,它应该在一开始就被称为MVP(迈克·布鲁姆)。 我已经试过两种方式与他们为之奋斗。 我也试了一下没有WaitForPendingFinalizers和它的工作,虽然它不应该伤害任何东西。 因人而异。
这里是由我所提到的MVP(他们在VB,但它没有什么不同)相关链接:
- http://www.xtremevbtalk.com/showthread.php?p=1157109#post1157109
- http://www.xtremevbtalk.com/showthread.php?s=bcdea222412c5cbfa7f02cfaf8f7b33f&p=1156479#post1156479
Answer 4:
正如其他人已经覆盖互操作,我会建议,如果你处理与XLSX扩展Excel文件,你应该使用EPPlus这将使您的Excel噩梦消失。
Answer 5:
我刚刚在这里回答了这个问题:
杀死Excel进程通过它的主窗口的hWnd
Answer 6:
它的4年,因为这被张贴,但我遇到了同样的问题,能解决这个问题。 显然刚刚访问细胞Array创建一个COM对象。 所以,如果你是做:
wSheet = (Excel._Worksheet)wBook.ActiveSheet;
Microsoft.Office.Interop.Excel.Range cells = wSheet.Cells;
int iRowCount = 2;
// enumerate and drop the values straight into the Excel file
while (data.Read())
{
Microsoft.Office.Interop.Excel.Range cell = cells[iRowCount, 1];
cell = data["fullname"].ToString();
Marshal.FinalReleaseComObject(cell);
}
Marshal.FinalReleaseComObject(cells);
然后你清理的其余部分应该解决这个问题。
Answer 7:
我落得这样做来解决类似的问题是获得进程ID和杀死作为最后的手段...
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindowThreadProcessId(int hWnd, out IntPtr lpdwProcessId);
...
objApp = new Excel.Application();
IntPtr processID;
GetWindowThreadProcessId(objApp.Hwnd, out processID);
excel = Process.GetProcessById(processID.ToInt32());
...
objApp.Application.Quit();
Marshal.FinalReleaseComObject(objApp);
_excel.Kill();
Answer 8:
这里的内容我hasn't失败的,但finally块清理Excel的自动化。 我的应用程序留下Excel中打开,所以没有退出呼叫。 将在注释中引用了我的源代码。
finally
{
// Cleanup -- See http://www.xtremevbtalk.com/showthread.php?t=160433
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
// Calls are needed to avoid memory leak
Marshal.FinalReleaseComObject(sheet);
Marshal.FinalReleaseComObject(book);
Marshal.FinalReleaseComObject(excel);
}
Answer 9:
你有没有使用纯.NET解决方案考虑,如的SpreadsheetGear的.NET ? 这里是你的代码可能会喜欢什么样的使用的SpreadsheetGear:
// open the template
using (IWorkbookSet workbookSet = SpreadsheetGear.Factory.GetWorkbookSet())
{
IWorkbook wBook = workbookSet.Workbooks.Open(excelTemplatePath + _report.ExcelTemplate);
IWorksheet wSheet = wBook.ActiveWorksheet;
int iRowCount = 2;
// enumerate and drop the values straight into the Excel file
while (data.Read())
{
wSheet.Cells[iRowCount, 1].Value = data["fullname"].ToString();
wSheet.Cells[iRowCount, 2].Value = data["brand"].ToString();
wSheet.Cells[iRowCount, 3].Value = data["agency"].ToString();
wSheet.Cells[iRowCount, 4].Value = data["advertiser"].ToString();
wSheet.Cells[iRowCount, 5].Value = data["product"].ToString();
wSheet.Cells[iRowCount, 6].Value = data["comment"].ToString();
wSheet.Cells[iRowCount, 7].Value = data["brief"].ToString();
wSheet.Cells[iRowCount, 8].Value = data["responseDate"].ToString();
wSheet.Cells[iRowCount, 9].Value = data["share"].ToString();
wSheet.Cells[iRowCount, 10].Value = data["status"].ToString();
wSheet.Cells[iRowCount, 11].Value = data["startDate"].ToString();
wSheet.Cells[iRowCount, 12].Value = data["value"].ToString();
iRowCount++;
}
DirectoryInfo saveTo = Directory.CreateDirectory(excelTemplatePath + _report.FolderGuid.ToString() + "\\");
_report.ReportLocation = saveTo.FullName + _report.ExcelTemplate;
wBook.SaveAs(_report.ReportLocation, FileFormat.OpenXMLWorkbook);
}
如果你有超过几行更多,你可以在它运行快多少感到震惊。 而你将永远不会有担心的Excel挂实例。
您可以下载免费试用在这里和自己试一试。
免责声明:我自己的SpreadsheetGear LLC
Answer 10:
粘贴代码最简单的方法是通过一个问题 - 这并不意味着我已经回答了我的问题(很遗憾)。 道歉给那些试图帮助我 - 我没能回到这个直到现在。 它仍然有我难倒了......我已经完全隔离了Excel码成一个功能按
private bool GenerateDailyProposalsReport(ScheduledReport report)
{
// start of test
Excel.Application xl = null;
Excel._Workbook wBook = null;
Excel._Worksheet wSheet = null;
Excel.Range xlrange = null;
object m_objOpt = System.Reflection.Missing.Value;
xl = new Excel.Application();
wBook = (Excel._Workbook)xl.Workbooks.Open(@"E:\Development\Romain\APN\SalesLinkReportManager\ExcelTemplates\DailyProposalReport.xls", false, false, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);
wSheet = (Excel._Worksheet)wBook.ActiveSheet;
xlrange = wSheet.Cells[2, 1] as Excel.Range;
// PROBLEM LINE ************
xlrange.Value2 = "fullname";
//**************************
wBook.Close(true, @"c:\temp\DailyProposalReport.xls", m_objOpt);
xl.Quit();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.FinalReleaseComObject(xlrange);
Marshal.FinalReleaseComObject(wSheet);
Marshal.FinalReleaseComObject(wBook);
Marshal.FinalReleaseComObject(xl);
xlrange = null;
wSheet = null;
wBook = null;
xl = null;
// end of test
return true;
}
如果我注释掉的问题线之上,Excel的实例从内存中释放。 既然这样,它没有。 我会很感激有这方面的进一步帮助,因为时间是短暂的,一个最后期限织机(不要他们全部)。
如果您需要了解更多信息请查询。 由于在期待。
附录
这可能会或可能不会揭示出这个越光位的更多信息。 我已经使出了一定时间后杀死进程(权宜之计)(5-10秒,从而获得Excel的时间来完成它的过程)。 我有计划的两份报告 - 创建的第一份报告,并保存到磁盘和Excel进程被终止,然后通过电子邮件发送。 二是创建,保存到磁盘,进程被杀死,但突然在尝试电子邮件时出现错误。 错误是:该进程无法访问该文件“......”等等
所以,即使在Excel的应用程序已被杀害,实际的Excel文件仍被持有的窗口服务。 我不得不杀服务,删除文件...
Answer 11:
我怕我运行的想法在这里肖恩。 :-(
加里可能有一些想法,但尽管他的包装方法是非常坚实的,它实际上并不会帮助你在这种情况下,因为你已经做的一切非常正常。
我会在这里列出的一些想法。 我怎么没看到任何人会真正因为你的神秘线上工作
xlrange.Value2 = "fullname";
似乎没有受到任何的这些想法会受到影响,但在这里有云:
(1)不要使用_Workbook和_Worksheet接口。 使用簿和工作表来代替。 (欲了解更多关于此看到: Excel的互操作:_Worksheet或工作表? )。
(2)任何时间有两个点(“”)在同一行访问Excel对象时,它分解成两行,分配每个对象一个命名的变量。 然后,代码的清理部中,显式释放使用Marshal.FinalReleaseComObject每个变量()。
例如,您的代码在这里:
wBook = (Excel._Workbook)xl.Workbooks.Open(@"E:\Development\Romain\APN\SalesLinkReportManager\ExcelTemplates\DailyProposalReport.xls", false, false, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt, m_objOpt);
可能被打破高达:
Excel.Workbooks wBooks = xl.Workbooks;
wBook = wBooks.Open("@"E:\Development\...\DailyProposalReport.xls", etc...);
再后来,清理部分中,你会:
Marshal.FinalReleaseComObject(xlrange);
Marshal.FinalReleaseComObject(wSheet);
Marshal.FinalReleaseComObject(wBook);
Marshal.FinalReleaseComObject(wBooks); // <-- Added
Marshal.FinalReleaseComObject(xl);
(3)我不知道是怎么回事你Process.Kill方法。 如果在调用Process.Kill之前调用wBook.Close(),然后xl.Quit()(),你应该不会遇到麻烦。 直到Excel中完成关停(尽管它可能仍然挂)Workbook.Close(),直到关闭工作簿不返回执行你,Excel.Quit()将不会返回执行。
调用Process.Kill()后,您可以在一个循环检查Process.HasExited财产,或者更好的,调用将暂停,直到它已经退出了您的Process.WaitForExit()方法。 我猜想,这通常会出现第二下拍好。 这是更好地等待更少的时间和肯定,而不是等待5 - 10秒钟,只能猜测。
(4)你应该尝试,我上面列出的这些清理的想法,但我开始怀疑你可能有可能与Excel中工作的其他工艺,比如外接或防病毒程序的问题。 这些加载项可能导致Excel挂起,如果他们不正确。 如果发生这种情况,它可以是非常困难或不可能得到的Excel释放。 您需要找出问题的程序,然后禁用它。 另一种可能性是,作为Windows服务运行在某种程度上是一个问题。 我不明白为什么会是这样,但我没有经验,通过一个Windows服务自动化Excel,所以我不能说。 如果你的问题都与此有关,然后使用Process.Kill将可能是你在这里唯一的救命稻草。
这是所有我能想到离手的,肖恩。 我希望这有帮助。 让我们知道怎么回事...
迈克 -
Answer 12:
肖恩,
我要与我的变更(下)再次重新发布您的代码。 我避免改变你的代码太多了,所以我还没有添加任何异常处理等,这代码并不健壮。
private bool GenerateDailyProposalsReport(ScheduledReport report)
{
Excel.Application xl = null;
Excel.Workbooks wBooks = null;
Excel.Workbook wBook = null;
Excel.Worksheet wSheet = null;
Excel.Range xlrange = null;
Excel.Range xlcell = null;
xl = new Excel.Application();
wBooks = xl.Workbooks;
wBook = wBooks.Open(@"DailyProposalReport.xls", false, false, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
wSheet = wBook.ActiveSheet;
xlrange = wSheet.Cells;
xlcell = xlrange[2, 1] as Excel.Range;
xlcell.Value2 = "fullname";
Marshal.ReleaseComObject(xlcell);
Marshal.ReleaseComObject(xlrange);
Marshal.ReleaseComObject(wSheet);
wBook.Close(true, @"c:\temp\DailyProposalReport.xls", Type.Missing);
Marshal.ReleaseComObject(wBook);
Marshal.ReleaseComObject(wBooks);
xl.Quit();
Marshal.ReleaseComObject(xl);
return true;
}
注意要点:
该Workbooks
中的方法Application
类创建一个Workbooks
持有至相应的COM对象的引用对象,所以我们必须确保我们随后释放该引用,这就是为什么我添加变量wBooks
,并调用相应的ReleaseComObject
。
同样, Cells
中的方法Worksheet
对象返回一个Range
与另一个COM参考对象,所以我们需要清理一下了。 因此,需要2个独立的Range
变量。
我已经发布了COM引用(使用ReleaseComObject
,只要他们不再需要),我认为这是即使它不是绝对必要的好习惯。 此外,(这可能是迷信)我已经发表的工作簿所拥有的所有对象关闭工作簿之前,和关闭Excel中之前发布的工作簿。
我没有要求GC.Collect
等等,因为它不应该是必要的。 真!
我使用ReleaseComObject的,而不是FinalReleaseComObject,因为它应该是完全足够了。
我不是空-ING使用后的变量; 再次,它没有做任何事情值得的。
这里不相关,但我使用Type.Missing
代替System.Reflection.Missing.Value
为了方便。 在C#V4卷,其中可选参数将被编译器支持!
我已经无法编译或运行这段代码,但我非常有信心它会工作。 祝好运!
Answer 13:
有没有必要使用Excel的COM对象从C#。 您可以使用OleDb的修改表。
http://www.codeproject.com/KB/office/excel_using_oledb.aspx
Answer 14:
我有一个类似的问题。 我删除_worksheet
和_workbook
,一切都很好。
文章来源: c# and excel automation - ending the running instance