我正在编写一个循环遍历Excel工作簿中所有工作表的程序,并将每个工作表保存为自己的工作簿。事实证明它比我想象的要复杂一些,因为Sheet.Copy
方法会创建一个奇怪的对象(请参阅此处的MSDN讨论,我相信这是相关的:http://msdn.microsoft.com/en-us/library/ms178779.aspx)。
无论如何,我发现another Stack Overflow post让我到达了我的位置,这基本上是完整的,在程序完成后留下的一个挂起的EXCEL.EXE进程之外(检查更新还有一个问题来了)起来,但我认为它们是相关的。)
这是我的代码:
Imports System.Data
Imports System.IO
Imports Microsoft.Office.Interop
Imports Office = Microsoft.Office.Core
Imports xlNS = Microsoft.Office.Interop.Excel
Imports System.Runtime.InteropServices
Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'Get information from text boxes
Dim InputLocation As String
Dim OutputLocation As String
InputLocation = InputLoc.Text & "\" & FileName.Text
If OutputLoc.Text = "" Then
OutputLocation = InputLoc.Text
Else
OutputLocation = OutputLoc.Text
End If
'Make file to save files in
' Get date and time in filename as well
Dim TLDateTime As String
Dim TLDay As String
Dim TLMonth As Integer
Dim TLYear As Integer
Dim TLHour As Integer
Dim TLMinute As Integer
Dim TLDate As String
Dim TLTime As String
Dim TLSecond As Integer
TLDay = DateTime.Now.Day
TLMonth = DateTime.Now.Month
TLYear = DateTime.Now.Year
TLHour = DateTime.Now.Hour
TLMinute = DateTime.Now.Minute
TLSecond = DateTime.Now.Second
Dim MyDate As New DateTime(TLYear, TLMonth, TLDay, TLHour, TLMinute, TLSecond)
Dim MyString As String = MyDate.ToString("MMMddyyyy_HHmmss")
TLDate = TLMonth.ToString + TLDay.ToString + TLYear.ToString
TLTime = TLHour.ToString + TLMinute.ToString
TLDateTime = TLDate + "_" + TLTime
Try
Directory.CreateDirectory(OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime)
OutputLocation = OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime
Catch
MsgBox("Trying to create a file that exists, please delete it. If the file does not exist check to make sure your output location exists")
End Try
'Open up excel file with information in it
Dim xlApp1 As Excel.Application
Dim locs As Excel.Workbook
Dim exportsheet As Excel.Worksheet
xlApp1 = New Excel.Application
xlApp1.Visible = True
xlApp1.Application.DisplayAlerts = False
locs = xlApp1.Workbooks.Open(InputLocation)
'locsws = locs.ActiveSheet
Dim wkshtcount = locs.Worksheets.Count - 1
Dim fileNames As New ArrayList
For counter = 1 To wkshtcount + 1
'identify and copy sheet to move
exportsheet = CType(locs.Worksheets(counter), Excel.Worksheet)
fileNames.Add(exportsheet.Name)
exportsheet.Copy(Type.Missing, Type.Missing)
exportsheet = xlApp1.Workbooks("Book" & counter).Sheets(1)
exportsheet.SaveAs(Filename:=OutputLocation & "\" & fileNames(counter - 1) & ".xlsx")
'close excel and release com objects
System.Runtime.InteropServices.Marshal.ReleaseComObject(exportsheet)
exportsheet = Nothing
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1.ActiveWorkbook)
xlApp1.ActiveWorkbook.Close(False)
Next
'close excel and release com objects
locs.Close(False)
System.Runtime.InteropServices.Marshal.ReleaseComObject(locs)
locs = Nothing
xlApp1.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1)
xlApp1 = Nothing
End Sub
End Class
现在我认为问题来自循环结束,我尝试关闭导出文件及其创建的新工作表:
'close excel and release com objects
System.Runtime.InteropServices.Marshal.ReleaseComObject(exportsheet)
exportsheet = Nothing
xlApp1.Workbooks(fileNames(counter - 1)).Close(False)
我无法弄清楚如何为已创建的新工作表释放ComObject
。我一直在尝试各种各样的事情,但是当我这样做时它总是抛出一个COM错误,并且如果我试图将它定义为什么(就像我对exportheet一样)是说它只是默认只读,所以我可以'做到了。看起来它应该像以下一样简单:
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1.Workbooks(fileNames(counter - 1)))
但事实并非如此。我尝试了一些这方面的变种,我认为它与上面的MSDN链接有关,但我不能完全理解该怎么做。因此代码适用于我的目的,除了在完成后留下一个EXCEL.EXE。
就测试文件而言,我只使用3张Excel文件并在每个文件上放置一些信息并更改工作表名称,因此很容易看出它是否正常工作。
任何想法都会受到赞赏,谢谢。
更新:我刚刚从我的主Excel应用程序关闭了可见性,但仍会弹出一些东西,这让我相信我使用Copy
的方式是创建一个新的Excel应用程序,但我不是真的确定如何引用它。如果有人知道如何关闭那里的能见度,那将非常感激。
最后更新:关于一些可怜的灵魂遇到我遇到的同一问题的机会,第一次更新应解决它,但同样重要的是要注意excel.exe将挂起,直到你关闭应用程序。我将自动化代码报告为Windows窗体应用程序(因此同事可以提供文件位置等),并且在您关闭程序弹出窗口之前,将会运行excel.exe进程。也许垃圾收集在关闭应用程序窗口之前不会运行,或者由于其他原因它只是挂起到excel.exe的实例。
答案 0 :(得分:3)
下面是适用于我的代码(请注意我发布对象的顺序,这很重要)
xlWorkBook.Close()
xlApp.Quit()
ReleaseObject(xlWorkSheet)
ReleaseObject(xlWorkBook)
ReleaseObject(xlApp)
Private Sub ReleaseObject(ByVal obj As Object)
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
obj = Nothing
Catch ex As Exception
obj = Nothing
Finally
GC.Collect()
End Try
End Sub
答案 1 :(得分:2)
如果您正在创建未引用的COM对象,则可能需要执行以下操作:
GC.Collect()
GC.WaitForPendingFinalizers()
摘自此MSDN论坛帖子: http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/cb5f7948-c229-483e-846b-a1cfbbcd86ca/
答案 2 :(得分:1)
好的,这可以构成一个非常简单的工作表。如果您有任何疑问,请告诉我。
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
'Get information from text boxes
Dim InputLocation As String
Dim OutputLocation As String
InputLocation = InputLoc.Text & "\" & Filename.Text
If OutputLoc.Text = "" Then
OutputLocation = InputLoc.Text
Else
OutputLocation = OutputLoc.Text
End If
'Make file to save files in
' Get date and time in filename as well
Dim TLDateTime As String
Dim TLDay As String
Dim TLMonth As Integer
Dim TLYear As Integer
Dim TLHour As Integer
Dim TLMinute As Integer
Dim TLDate As String
Dim TLTime As String
Dim TLSecond As Integer
TLDay = DateTime.Now.Day
TLMonth = DateTime.Now.Month
TLYear = DateTime.Now.Year
TLHour = DateTime.Now.Hour
TLMinute = DateTime.Now.Minute
TLSecond = DateTime.Now.Second
Dim MyDate As New DateTime(TLYear, TLMonth, TLDay, TLHour, TLMinute, TLSecond)
Dim MyString As String = MyDate.ToString("MMMddyyyy_HHmmss")
TLDate = TLMonth.ToString + TLDay.ToString + TLYear.ToString
TLTime = TLHour.ToString + TLMinute.ToString
TLDateTime = TLDate + "_" + TLTime
Try
Directory.CreateDirectory(OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime)
OutputLocation = OutputLocation & "\" & "Field Sales Report Graphs " & TLDateTime
Catch
MsgBox("Trying to create a file that exists, please delete it. If the file does not exist check to make sure your output location exists")
End Try
'Open up excel file with information in it
Dim xlApp1 As Excel.Application = New Excel.Application
xlApp1.Visible = True
xlApp1.Application.DisplayAlerts = False
Dim locs As Excel.Workbook
locs = xlApp1.Workbooks.Open(InputLocation)
Dim fileNames As New ArrayList
Dim counter As Integer = 1
For Each ws As Excel.Worksheet In locs.Worksheets
fileNames.Add(ws.Name)
ws = xlApp1.Workbooks(counter).Sheets(1)
ws.SaveAs(Filename:=OutputLocation & "\" & fileNames(counter - 1) & ".xlsx")
'close excel and release com objects
System.Runtime.InteropServices.Marshal.ReleaseComObject(ws)
xlApp1.Workbooks(counter).Close(False)
counter += 1
Next
System.Runtime.InteropServices.Marshal.ReleaseComObject(locs)
locs = Nothing
xlApp1.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1)
xlApp1 = Nothing
End Sub
答案 3 :(得分:1)
我最终遇到的解决方案是添加
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp1.ActiveWorkbook)
xlApp1.ActiveWorkbook.Close()
循环。我在另一个堆栈溢出帖子的The first answer的评论中找到了这个答案。基本上,我遇到的问题是worksheet.copy方法创建一个没有引用的工作簿对象,但事实证明它可以通过引用活动表来引用。如果你想用它做更多的事情而不是像我一样把它踢出门,我想你可以创建一个新的工作簿对象并分配它或者作为我链接的帖子建议,你也可以将它保存为某些东西。对于我的目的,在保存我想要的工作表之后释放它就可以了,这样就删除了我的excel.exe挂起过程。
如果你想要一个更加优雅的代码选项,你应该查看ron tornambe帖子,他为每个循环做了我设法不能正确而不是我创造的那些不稳定的东西。基本上你会想在我的代码中使用他的循环,你会设置它。感谢堆栈溢出。