运行 完成之前 COM 对象上的例程?
Run a routine on COM object before finalize?
在下面定义的 class 中,我使用 public 属性 来保存一个 Excel.Application
对象实例(这是我对单例的尝试 class).
每当 Finalize
例程运行并调用 RestoreApp()
时,它会在第一次尝试访问该 Application
对象的成员时抛出一个 InvalidComObjectException
(粘贴在下面) .
我写了这个简单的测试例程试图弄清楚发生了什么,我验证了我可以访问这个 属性 before the Finalize
方法被调用,但当 RetoreApp()
方法尝试访问相同的 属性 时,我总是得到 InvalidComObjectException
。
MSDN reference for Object Lifetime 说:
Before releasing objects, the CLR automatically calls the Finalize
method for objects that define a Sub Finalize procedure. The Finalize
method can contain code that needs to execute just before an object is
destroyed...
但是,也许 COM 对象是一种特殊情况,在 Finalize 方法 运行 之前对象被部分销毁(RCW 与底层 COM 对象断开连接)?我不知道——这似乎就是正在发生的事情。
谁能告诉我如何在我的对象与 COM 对象断开连接之前确保 ResoreApp()
过程是 运行?
测试:
Sub Main()
Dim ExcelTest As New MyExcel
Debug.Print(ExcelTest.Excel Is Nothing)
Debug.Print(ExcelTest.Excel.DisplayStatusBar)
End Sub
异常:
{"COM object that has been separated from its underlying RCW cannot be
used."}
_className: Nothing
_COMPlusExceptionCode: -532462766
_data: Nothing
_dynamicMethods: Nothing
_exceptionMethod: Nothing
_exceptionMethodString: Nothing
_helpURL: Nothing
_HResult: -2146233049
_innerException: Nothing
_ipForWatsonBuckets: 0
_message: "COM object that has been separated from its underlying RCW cannot be used."
_remoteStackIndex: 0
_remoteStackTraceString: Nothing
_safeSerializationManager: {System.Runtime.Serialization.SafeSerializationManager}
_source: Nothing
_stackTrace: {SByte()}
_stackTraceString: Nothing
_watsonBuckets: Nothing
_xcode: -532462766
_xptrs: 0
Data: {System.Collections.ListDictionaryInternal}
HelpLink: Nothing
HResult: -2146233049
InnerException: Nothing
IPForWatsonBuckets: 0
IsTransient: False
Message: "COM object that has been separated from its underlying RCW cannot be used."
RemoteStackTrace: Nothing
s_EDILock: {Object}
Source: "mscorlib"
StackTrace: " at System.StubHelpers.StubHelpers.StubRegisterRCW(Object pThis) at
Microsoft.Office.Interop.Excel.ApplicationClass.get_DisplayStatusBar()"
TargetSite: {Void StubRegisterRCW(System.Object)}
WatsonBuckets: Nothing
Class:
Imports Microsoft.Office
Imports Microsoft.Office.Interop.Excel
Imports System.Windows.Forms
Public Class MyExcel
Dim CreateNew As Boolean
Dim Prepped As Boolean
Dim Prepping As Boolean
Dim pExcel As Microsoft.Office.Interop.Excel.Application
Dim pStatBar As Boolean
Dim pScreenUpdates As Boolean
Dim pEventsEnabled As Boolean
Dim pAlerts As Boolean
Private Property ScreenUpdates As Boolean
Get
Return pScreenUpdates
End Get
Set(value As Boolean)
pScreenUpdates = value
End Set
End Property
Private Property EventsEnabled As Boolean
Get
Return pEventsEnabled
End Get
Set(value As Boolean)
pEventsEnabled = value
End Set
End Property
Private Property StatBar As Boolean
Get
Return pStatBar
End Get
Set(value As Boolean)
pStatBar = value
End Set
End Property
Private Property AlertsDisplay As Boolean
Get
Return pAlerts
End Get
Set(value As Boolean)
pAlerts = value
End Set
End Property
Public ReadOnly Property Excel() As Microsoft.Office.Interop.Excel.Application
Get
Try
If Not CreateNew Then
If pExcel Is Nothing Then pExcel = GetObject([Class]:="Excel.Application")
End If
If pExcel Is Nothing Then pExcel = CreateObject("Excel.Application")
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
Return pExcel
End Get
End Property
Public Sub New(Optional ByVal NewExcel As Boolean = True)
CreateNew = NewExcel
PrepApp()
End Sub
Private Sub PrepApp()
'Set prepping to true to avoid creating a stack overflow
Try
With Me.Excel
ScreenUpdates = .ScreenUpdating
EventsEnabled = .EnableEvents
StatBar = .DisplayStatusBar
AlertsDisplay = .DisplayAlerts
.DisplayStatusBar = True
.ScreenUpdating = False
.EnableEvents = False
.DisplayAlerts = False
End With
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
Private Sub RestoreApp()
'Restores the original values for screen updating, event triggers, and status bar display
Try
With Me.Excel
.DisplayStatusBar = StatBar
.ScreenUpdating = ScreenUpdates
.EnableEvents = EventsEnabled
.DisplayAlerts = AlertsDisplay
End With
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
Protected Overrides Sub Finalize()
RestoreApp()
MyBase.Finalize()
End Sub
End Class
VB.Net 终结器是不确定的,因此不允许安全访问 COM 创建的成员。
您可以通过实现 IDisposable 接口来使用确定性处理器:
https://msdn.microsoft.com/en-us/library/s9bwddyx(v=vs.90).aspx
Sub Main()
Dim xl As New MyExcel
...
xl.Dispose()
End Sub
另一种方法是在终结器中使用新的 COM 实例来恢复您的设置:
Private Sub RestoreApp()
Dim xl As Microsoft.Office.Interop.Excel.Application = pExcel
If Me.CreateNew Then
xl = CreateObject("Excel.Application")
Else
xl = GetObject(, "Excel.Application")
End If
xl.DisplayStatusBar = StatBar
xl.ScreenUpdating = ScreenUpdates
xl.EnableEvents = EventsEnabled
xl.DisplayAlerts = AlertsDisplay
xl.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(xl)
End Sub
在下面定义的 class 中,我使用 public 属性 来保存一个 Excel.Application
对象实例(这是我对单例的尝试 class).
每当 Finalize
例程运行并调用 RestoreApp()
时,它会在第一次尝试访问该 Application
对象的成员时抛出一个 InvalidComObjectException
(粘贴在下面) .
我写了这个简单的测试例程试图弄清楚发生了什么,我验证了我可以访问这个 属性 before the Finalize
方法被调用,但当 RetoreApp()
方法尝试访问相同的 属性 时,我总是得到 InvalidComObjectException
。
MSDN reference for Object Lifetime 说:
Before releasing objects, the CLR automatically calls the Finalize method for objects that define a Sub Finalize procedure. The Finalize method can contain code that needs to execute just before an object is destroyed...
但是,也许 COM 对象是一种特殊情况,在 Finalize 方法 运行 之前对象被部分销毁(RCW 与底层 COM 对象断开连接)?我不知道——这似乎就是正在发生的事情。
谁能告诉我如何在我的对象与 COM 对象断开连接之前确保 ResoreApp()
过程是 运行?
测试:
Sub Main()
Dim ExcelTest As New MyExcel
Debug.Print(ExcelTest.Excel Is Nothing)
Debug.Print(ExcelTest.Excel.DisplayStatusBar)
End Sub
异常:
{"COM object that has been separated from its underlying RCW cannot be used."} _className: Nothing
_COMPlusExceptionCode: -532462766
_data: Nothing
_dynamicMethods: Nothing
_exceptionMethod: Nothing
_exceptionMethodString: Nothing
_helpURL: Nothing
_HResult: -2146233049
_innerException: Nothing
_ipForWatsonBuckets: 0
_message: "COM object that has been separated from its underlying RCW cannot be used."
_remoteStackIndex: 0
_remoteStackTraceString: Nothing
_safeSerializationManager: {System.Runtime.Serialization.SafeSerializationManager}
_source: Nothing
_stackTrace: {SByte()}
_stackTraceString: Nothing
_watsonBuckets: Nothing
_xcode: -532462766
_xptrs: 0
Data: {System.Collections.ListDictionaryInternal}
HelpLink: Nothing
HResult: -2146233049
InnerException: Nothing
IPForWatsonBuckets: 0
IsTransient: False
Message: "COM object that has been separated from its underlying RCW cannot be used."
RemoteStackTrace: Nothing
s_EDILock: {Object}
Source: "mscorlib"
StackTrace: " at System.StubHelpers.StubHelpers.StubRegisterRCW(Object pThis) at
Microsoft.Office.Interop.Excel.ApplicationClass.get_DisplayStatusBar()"
TargetSite: {Void StubRegisterRCW(System.Object)}
WatsonBuckets: Nothing
Class:
Imports Microsoft.Office
Imports Microsoft.Office.Interop.Excel
Imports System.Windows.Forms
Public Class MyExcel
Dim CreateNew As Boolean
Dim Prepped As Boolean
Dim Prepping As Boolean
Dim pExcel As Microsoft.Office.Interop.Excel.Application
Dim pStatBar As Boolean
Dim pScreenUpdates As Boolean
Dim pEventsEnabled As Boolean
Dim pAlerts As Boolean
Private Property ScreenUpdates As Boolean
Get
Return pScreenUpdates
End Get
Set(value As Boolean)
pScreenUpdates = value
End Set
End Property
Private Property EventsEnabled As Boolean
Get
Return pEventsEnabled
End Get
Set(value As Boolean)
pEventsEnabled = value
End Set
End Property
Private Property StatBar As Boolean
Get
Return pStatBar
End Get
Set(value As Boolean)
pStatBar = value
End Set
End Property
Private Property AlertsDisplay As Boolean
Get
Return pAlerts
End Get
Set(value As Boolean)
pAlerts = value
End Set
End Property
Public ReadOnly Property Excel() As Microsoft.Office.Interop.Excel.Application
Get
Try
If Not CreateNew Then
If pExcel Is Nothing Then pExcel = GetObject([Class]:="Excel.Application")
End If
If pExcel Is Nothing Then pExcel = CreateObject("Excel.Application")
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
Return pExcel
End Get
End Property
Public Sub New(Optional ByVal NewExcel As Boolean = True)
CreateNew = NewExcel
PrepApp()
End Sub
Private Sub PrepApp()
'Set prepping to true to avoid creating a stack overflow
Try
With Me.Excel
ScreenUpdates = .ScreenUpdating
EventsEnabled = .EnableEvents
StatBar = .DisplayStatusBar
AlertsDisplay = .DisplayAlerts
.DisplayStatusBar = True
.ScreenUpdating = False
.EnableEvents = False
.DisplayAlerts = False
End With
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
Private Sub RestoreApp()
'Restores the original values for screen updating, event triggers, and status bar display
Try
With Me.Excel
.DisplayStatusBar = StatBar
.ScreenUpdating = ScreenUpdates
.EnableEvents = EventsEnabled
.DisplayAlerts = AlertsDisplay
End With
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
Protected Overrides Sub Finalize()
RestoreApp()
MyBase.Finalize()
End Sub
End Class
VB.Net 终结器是不确定的,因此不允许安全访问 COM 创建的成员。 您可以通过实现 IDisposable 接口来使用确定性处理器: https://msdn.microsoft.com/en-us/library/s9bwddyx(v=vs.90).aspx
Sub Main()
Dim xl As New MyExcel
...
xl.Dispose()
End Sub
另一种方法是在终结器中使用新的 COM 实例来恢复您的设置:
Private Sub RestoreApp()
Dim xl As Microsoft.Office.Interop.Excel.Application = pExcel
If Me.CreateNew Then
xl = CreateObject("Excel.Application")
Else
xl = GetObject(, "Excel.Application")
End If
xl.DisplayStatusBar = StatBar
xl.ScreenUpdating = ScreenUpdates
xl.EnableEvents = EventsEnabled
xl.DisplayAlerts = AlertsDisplay
xl.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(xl)
End Sub