.net 发布Excel互操作com对象的最佳方法

.net 发布Excel互操作com对象的最佳方法,.net,vb.net,interop,.net,Vb.net,Interop,专家们,请让我知道我能做到这一点的最好方法 我正在开发一个VB.Net应用程序,它使用Microsoft.Office.Interop.Excel对象库在工作簿中创建工作表,并在这些工作表中创建透视表 我的代码如下所示: Dim ExcelApp As New Microsoft.Office.Interop.Excel.Application Dim wbk As Microsoft.Office.Interop.Excel.Workbook = Nothing Dim wksRawData

专家们,请让我知道我能做到这一点的最好方法

我正在开发一个VB.Net应用程序,它使用Microsoft.Office.Interop.Excel对象库在工作簿中创建工作表,并在这些工作表中创建透视表

我的代码如下所示:

Dim ExcelApp As New Microsoft.Office.Interop.Excel.Application
Dim wbk As Microsoft.Office.Interop.Excel.Workbook = Nothing
Dim wksRawData As Microsoft.Office.Interop.Excel.Worksheet = Nothing
Dim wksPvtTbl As Microsoft.Office.Interop.Excel.Worksheet = Nothing
Dim pvtCache As Microsoft.Office.Interop.Excel.PivotCache = Nothing
Dim pvtTables As Microsoft.Office.Interop.Excel.PivotTables = Nothing
Dim pvtTable As Microsoft.Office.Interop.Excel.PivotTable = Nothing
Dim r1 As Microsoft.Office.Interop.Excel.PivotField = Nothing
Dim r2 As Microsoft.Office.Interop.Excel.PivotField = Nothing
Dim df1 As Microsoft.Office.Interop.Excel.PivotField = Nothing

Try

... Create the objects, put in the information

Catch ex As Exception
   MessageBox.Show("There was an error creating the Excel file", "Error Creating File", MessageBoxButtons.OK)
Finally

   ReleaseObject(r1)
   ReleaseObject(r2)
   ReleaseObject(df1)
   ReleaseObject(pvtTable)
   ReleaseObject(pvtTables)
   ReleaseObject(pvtCache)
   ReleaseObject(wksRawData)
   ReleaseObject(wksPvtTbl)
   ReleaseObject(wbk)

   ExcelApp.DisplayAlerts = True
   ExcelApp.Quit()
   ReleaseObject(ExcelApp)

   ExcelApp = Nothing
   wbk = Nothing
   wksRawData = Nothing
   GC.Collect()
End Try
然后,我的发布对象的代码如下所示:

Public Sub ReleaseObject(ByRef Reference As Microsoft.Office.Interop.Excel.Application)
    Dim i As Integer
    If Reference IsNot Nothing Then
        i = System.Runtime.InteropServices.Marshal.ReleaseComObject(Reference)

        While i > 0
            i = System.Runtime.InteropServices.Marshal.ReleaseComObject(Reference)
        End While

        Reference = Nothing
    End If
End Sub

Public Sub ReleaseObject(ByRef Reference As Microsoft.Office.Interop.Excel.Workbook)
    Dim i As Integer
    If Reference IsNot Nothing Then
        i = System.Runtime.InteropServices.Marshal.ReleaseComObject(Reference)

        While i > 0
            i = System.Runtime.InteropServices.Marshal.ReleaseComObject(Reference)
        End While

        Reference = Nothing
    End If
End Sub

... etc
我知道有很多解决这类问题的方法,但我迷失在所有不同的方法中,想知道什么最适合我目前的情况… 这是一种很好的方法吗?如果不是,还有什么更有效的方法


谢谢

我的答案不是最好的,但它很有效:(杀死应用程序使用的Excel进程)


我过去遇到过excel无法正确关闭的问题

以下是我的工作:

  • 请确保先.Close()和.Dispose()保存所有工作簿,然后保存所有应用程序
  • 也封送.ReleaseComObject(),但只封送一次。我从来没有像你那样做过for循环
  • 然后是GC.Collect()和GC.WaitForPendingFinalizers()
  • 通常在开始时将DisplayAlerts和Interactive设置为false。确保没有弹出窗口在后台运行

我从未使用interop创建过pivot表。

不要手动搜索和调用release。如果应用程序发生灾难性崩溃,互操作过程仍可能松散运行

我将通过向操作系统注册互操作过程

这里有一个解决方案:

我共享VB.net代码:

Imports System.Runtime.InteropServices
Namespace Jobs

Public Class Job
    Implements IDisposable

    <DllImport("kernel32.dll", CharSet:=CharSet.Unicode)>', 
    CallingConvention:=CallingConvention.Cdecl)>
    Shared Function CreateJobObject(a As IntPtr, lpName As String) As IntPtr
    End Function

    <DllImport("kernel32.dll")>
    Public Shared Function SetInformationJobObject(hJob As IntPtr, infoType As 
    JobObjectInfoType, lpJobObjectInfo As IntPtr, cbJobObjectInfoLength As UInteger) As Boolean
    End Function

    <DllImport("kernel32.dll", SetLastError:=True)>
    Public Shared Function CloseHandle(Token As IntPtr) As Boolean
    End Function

    <DllImport("kernel32.dll", SetLastError:=True)>
    Public Shared Function AssignProcessToJobObject(job As IntPtr, process As IntPtr) as Boolean
    End Function

    Private m_handle As IntPtr
    Private m_disposed As Boolean = False

    Public Sub New()

        m_handle = CreateJobObject(IntPtr.Zero, Nothing)

        Dim info As JOBOBJECT_BASIC_LIMIT_INFORMATION = New JOBOBJECT_BASIC_LIMIT_INFORMATION()
        info.LimitFlags = &H2000

        Dim extendedInfo = New JOBOBJECT_EXTENDED_LIMIT_INFORMATION()
        extendedInfo.BasicLimitInformation = info

        Dim length As Integer = Marshal.SizeOf(GetType(JOBOBJECT_EXTENDED_LIMIT_INFORMATION))
        Dim extendedInfoPtr As IntPtr = Marshal.AllocHGlobal(length)
        Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, False)

        If (Not SetInformationJobObject(m_handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, length)) Then
            Throw New Exception(String.Format("Unable to set information.  Error: {0}", Marshal.GetLastWin32Error()))
        End If
    End Sub

#Region "IDisposableMembers"

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

#End Region
    Private Sub Dispose(disposing As Boolean)
        If m_disposed Then
            Return
        End If
        If disposing Then
        End If
        Close()
        m_disposed = True
    End Sub

    Public Sub Close()
        CloseHandle(m_handle)
        m_handle = IntPtr.Zero
    End Sub

    Public Function AddProcess(handle As IntPtr) As Boolean
        Return AssignProcessToJobObject(m_handle, handle)
    End Function

End Class

Public Enum JobObjectInfoType
    AssociateCompletionPortInformation = 7
    BasicLimitInformation = 2
    BasicUIRestrictions = 4
    EndOfJobTimeInformation = 6
    ExtendedLimitInformation = 9
    SecurityLimitInformation = 5
    GroupInformation = 11
End Enum

<StructLayout(LayoutKind.Sequential)>
Public Structure SECURITY_ATTRIBUTES
    Public nLength As Integer
    Public lpSecurityDescriptor As IntPtr
    Public bInheritHandle As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Structure JOBOBJECT_BASIC_LIMIT_INFORMATION
    Public PerProcessUserTimeLimit As Int64
    Public PerJobUserTimeLimit As Int64
    Public LimitFlags As Int16
    Public MinimumWorkingSetSize As UInt32
    Public MaximumWorkingSetSize As UInt32
    Public ActiveProcessLimit As Int16
    Public Affinity As Int64
    Public PriorityClass As Int16
    Public SchedulingClass As Int16
End Structure

<StructLayout(LayoutKind.Sequential)>
Structure IO_COUNTERS
    Public ReadOperationCount As UInt64
    Public WriteOperationCount As UInt64
    Public OtherOperationCount As UInt64
    Public ReadTransferCount As UInt64
    Public WriteTransferCount As UInt64
    Public OtherTransferCount As UInt64
End Structure

<StructLayout(LayoutKind.Sequential)>
Structure JOBOBJECT_EXTENDED_LIMIT_INFORMATION
    Public BasicLimitInformation As JOBOBJECT_BASIC_LIMIT_INFORMATION
    Public IoInfo As IO_COUNTERS
    Public ProcessMemoryLimit As UInt32
    Public JobMemoryLimit As UInt32
    Public PeakProcessMemoryUsed As UInt32
    Public PeakJobMemoryUsed As UInt32
End Structure

End Namespace
导入System.Runtime.InteropServices
命名空间作业
公开课工作
实现IDisposable
', 
CallingConvention:=CallingConvention.Cdecl)>
共享函数CreateJobObject(a作为IntPtr,lpName作为字符串)作为IntPtr
端函数
公共共享函数SetInformationJobObject(hJob作为IntPtr,infoType作为
JobObjectInfo类型,lpJobObjectInfo作为IntPtr,CBJobObjectInfo长度作为UInteger)作为布尔值
端函数
公共共享函数CloseHandle(标记为IntPtr)为布尔值
端函数
公共共享函数AssignProcessToJobObject(作业为IntPtr,进程为IntPtr)为布尔值
端函数
作为IntPtr的专用m_句柄
私有m_被处理为布尔值=False
公共分新()
m_handle=CreateJobObject(IntPtr.Zero,Nothing)
作为作业对象\基本\限制\信息的尺寸信息=新作业对象\基本\限制\信息()
info.LimitFlags=&H2000
Dim extendedInfo=新作业对象\扩展\限制\信息()
extendedInfo.BasicLimitInformation=info
作为整数的Dim长度=Marshal.SizeOf(GetType(JOBOBJECT\u EXTENDED\u LIMIT\u INFORMATION))
Dim extendedInfoPtr作为IntPtr=Marshal.AllocHGlobal(长度)
Marshal.StructureToPtr(extendedInfo、extendedInfoPtr、False)
如果(不是SetInformationJobObject(m_句柄,JobObjectInfo类型。ExtendedLimitInformation,extendedInfoPtr,长度)),则
抛出新异常(String.Format(“无法设置信息。错误:{0}”,Marshal.GetLastWin32Error()))
如果结束
端接头
#区域“IDisposablembers”
Public Sub Dispose()实现IDisposable.Dispose
处置(真实)
总干事(Me)
端接头
#末端区域
私有子处置(作为布尔值处置)
如果你愿意的话
返回
如果结束
如果是这样的话
如果结束
关闭()
m_=True
端接头
公开分户结算()
闭合手柄(m_手柄)
m_handle=IntPtr.Zero
端接头
公共函数AddProcess(句柄为IntPtr)为布尔值
返回AssignProcessToJobObject(m_句柄,句柄)
端函数
末级
公共枚举JobObjectInfo类型
AssociateCompletionPortInformation=7
基本信息=2
基本限制=4
EndOfJobTimeInformation=6
ExtendedLimitInformation=9
SecurityLimitInformation=5
组信息=11
结束枚举
公共结构安全属性
公共长度为整数
公共lpSecurityDescriptor作为IntPtr
作为整数的公共bInheritHandle
端部结构
结构作业对象\基本\限制\信息
公共PerProcessUserTimeLimit作为Int64
公共PerJobUserTimeLimit As Int64
公共标志为Int16
公共最小工作集尺寸为UInt32
公共最大工作集大小为UInt32
公共ActiveProcessLimit作为Int16
公共亲和力为Int64
公共优先级类As Int16
公共调度类为Int16
端部结构
结构IO_计数器
公共读取操作计数为UInt64
公共写入操作计数为UInt64
公共其他操作计数为UInt64
公共ReadTransferCount为UInt64
公共WriteTransferCount为UInt64
公共OtherTransferCount为UInt64
端部结构
结构作业对象\u扩展\u限制\u信息
公共基本信息作为作业对象\基本\限制\信息
作为IO_计数器的公共IoInfo
公共进程MemoryLimit作为UInt32
公共工作记忆限制为UInt32
公共峰值过程被存储为UInt32
公共峰值工作记忆为UInt32
端部结构
结束命名空间
在您计划实施的课程中:

<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function GetWindowThreadProcessId(hWnd As IntPtr, ByRef lpdwProcessId As UInteger) As UInteger
End Function


'Implements
Dim oExcelApp = New Microsoft.Office.Interop.Excel.Application
Dim job As Jobs.Job = New Jobs.Job()
Dim pid As UInteger = 0
GetWindowThreadProcessId(New IntPtr(oExcelApp.Hwnd), pid)
job.AddProcess(Process.GetProcessById(pid).Handle)

oExcelApp.Workbooks.Open(RutaArchivoExcel)

'Code work code here

oExcelApp.Workbooks(1).Close()
oExcelApp.Quit()

'Then Dispose correctly the excel app and windows distroy appropiately the process
job.Dispose()

公共共享函数GetWindowThreadProcessId(hWnd作为IntPtr,ByRef lpdwProcessId作为UInteger)作为UInteger
端函数
"工具",
Dim oExcelApp=新的Microsoft.Office.Interop.Excel.Application
将作业设置为作业。作业=新作业。作业()
尺寸pid为UInteger=0
GetWindowThreadProcessId(新的IntPtr(oExcelApp.Hwnd),pid)
job.AddProcess(Process.GetProcessById(pid.Handle)
oExcelApp.Workbooks.Open(RutaArchivoExcel)
'在这里输入工作代码
oExcelApp.Workbook(1.Close)()
oExcelApp.Quit()
'然后正确地处理excel应用程序和windows发行版并适当地分配该进程
job.Dispose()

我用Microsoft Excel 2016对此进行了测试。

我已确保所有对象都是通过使用后期绑定进行单独声明、使用和发布的,并小心地用“一点”声明每个对象。确保