命令:
DoCmd.Close acForm, lstrParentName, acSaveNo
在组合框包装类(,即,一个将组合框对象分配给声明为WithEvents的组合框变量的类模块(中,Access一直处于崩溃状态,我不知道为什么。
上下文:
- 表单绑定到表单包装类(clsFrm(,该类将其控件绑定到控件特定的控件包装类(,例如、clsCbo(
- clsCbo接收绑定组合框的事件,包括OnDblClick
- clsCbo绑定了一个辅助组合框包装器,用于封装功能相关的代码
- clsCbo.mCbo_DblClick((调用一个导航过程(nSPer(((,该过程打开另一个表单并用
DoCmd.Close acForm, lstrParentName, acSaveNo
关闭当前表单,这会导致Access硬崩溃/失败/非自愿关闭
令人困惑的是:
- 导航过程运行时没有出现错误,并进入其退出过程
- 源/原始窗体关闭,其所有窗体和控件包装器都无错误地命中了Class_Terminate((过程
- 目标表单包装器达到其绑定方法,并且开始加载子表单
- 通过点击窗体窗口的"关闭"按钮手动关闭窗体;X〃;右上角的按钮不会造成任何困难
但是,Access仍然崩溃。尽管如此:
- 注释掉DoCmd.Close命令,没有问题
- 跳过绑定辅助组合框包装,没有问题
有什么想法吗?
子((如下:
Private Sub nSuper()
' Method to navigate from cboSuper~ID to the entity form.
' Dependencies:
' - mstrPath declared in Class_Initialize()
' - stdObjectTests.IsSubform()
' Dependants:
' - mCbo_DblClick
On Error GoTo ErrorHandler
200 Dim cboMe As ComboBox
210 Dim frmMe As Access.Form
220 Dim intPK As Long
230 Dim strDest As String
240 Dim strEnt As String
250 Dim strFld As String
260 Dim strPK As String
270 Dim strRS As String
280 Dim strWHERE As String
' Derive the filter criterion from the control:
290 Set cboMe = mCbo
310 intPK = cboMe.Value
' Derive Entity from Form.RecordSource:
320 Set frmMe = mParent
350 strRS = frmMe.RecordSource
370 strEnt = Right(strRS, Len(strRS) - 3)
' Compose the destination Form.Name from Entity:
400 If strEnt = "Party" Then
410 strDest = "frmFirm"
420 Else
430 strDest = "frm" & strEnt
440 End If
' Compose the filter:
460 strPK = strEnt & "ID"
480 strFld = "tbl" & strEnt & "." & strPK
500 strWHERE = strFld & " = " & intPK
510 If strDest = frmMe.Name Then
' Re-filter the current form if it is the entity form:
520 frmMe.Filter = strWHERE
530 frmMe.FilterOn = True
540 Else
' Otherwise, open the entity form:
550 DoCmd.OpenForm strDest, acNormal, , strWHERE
' Close the current form or its parent:
' - Either may crash Access:
560 If IsSubform(frmMe) Then
570 DoCmd.Close acForm, frmMe.Parent.Name, acSaveNo
580 Else
590 DoCmd.Close acForm, frmMe.Name, acSaveNo
600 End If
610 End If
ExitProcedure:
913 Close lintFF
914 Set lFso = Nothing
Set cboMe = Nothing
Set frmMe = Nothing
Exit Sub
ErrorHandler:
890 Resume ExitProcedure
End Sub 'nSuper()
今天早些时候出现了一个解决方案。
这可能是Access.Form
对象或垃圾收集/IUnknown
中的编码错误或错误。
问题的根源是让主组合框包装器类将控件绑定到辅助包装器类(该类的存在是为了封装功能相关的代码(。然后,做DoCmd.Close acForm, lstrParentName, acSaveNo
硬崩溃访问。未绑定辅助类时没有发生故障。
在Form.Close
上,对象引用都在Termination事件过程中被明确清除,但很明显,辅助类的控制引用持续了足够长的时间,以至于组合框可能已经过时了。当项目继续存在于Controls
集合中时,Form
对象如何关闭我想不通,但我跑题了。结果是硬崩溃访问,尽管如此:
- 所有对辅助类的引用都被清除了(所以垃圾回收在任何情况下都应该清除其对象引用,同时销毁对象(
- 其CCD_ 8过程被触发并且其组合框引用在那里被明确清除
当然,请注意,对象默认在VBA中传递ByRef
(,即,通过引用(。
解决方案是将Form.Close
放入辅助类中,并在那里清除类的组合框引用,因为在Class_Terminate()
中执行此操作需要等待垃圾收集通过其他对象级联,这为时已晚,无法防止Access失败注意尝试从主类中的任何位置清除辅助类的组合框引用也会失败。
具体:
-
在这两个类中,声明一个
Form
对象变量WithEvents
并将组合框的父对象分配给它。是的,特别奇怪的是,如果没有主类也绑定父对象,辅助类就不能接收其绑定对象的父对象的事件,因为所有对象都是通过引用传递的,而且不管怎样,该对象的parent属性都应该是不可变的,但实际情况就是这样 - 在主类中,除了分配Form对象变量之外,什么都不需要。在主类中下沉
Form.Close
是不必要的,甚至是不有效的 - 在辅助类中接收
Form.Close
,并在那里清除其组合框变量
我的实验测试课程如下:
主要类别:
'wecCboTestPrimary
Option Compare Database
Option Explicit
Private WithEvents mCbo As ComboBox
Private WithEvents mParent As Access.Form
Private mwecCboTestSecondary As New wecCboTestSecondary
Private mstrName As String
Private Sub Class_Initialize()
mstrName = "wecCboTestPrimary"
Debug.Print mstrName & ".Class_Initialize()"
End Sub
Private Sub Class_Terminate()
Debug.Print vbTab & mstrName & ".Class_Terminate()"
' Fails to avoid an Access hard crash:
' Set mwecCboTestSecondary.BoundCbo = Nothing
Set mwecCboTestSecondary = Nothing
Set mParent = Nothing
Set mCbo = Nothing
Debug.Print vbTab & mstrName & ".Class_Terminate().End"
End Sub
Public Property Get BoundCbo() As ComboBox
Debug.Print mstrName & ".PPG BoundCbo()"
Set BoundCbo = mCbo
End Property
Public Property Set BoundCbo(lCbo As ComboBox)
Debug.Print mstrName & ".PPS BoundCbo()"
' Bind the control:
Set mCbo = lCbo
' Sink its events:
mCbo.OnDblClick = "[Event Procedure]"
' Bind the control's parent:
Set mParent = lCbo.Parent
' Sinking parent events is unnecessary to avoid an Access hard crash:
'mParent.OnClose = "[Event Procedure]"
' Bind the control to the secondary combo box wrapper class:
Set mwecCboTestSecondary.BoundCbo = lCbo
Debug.Print mstrName & ".PPS BoundCbo()" & vbTab & mCbo.Name
End Property
Private Sub mJustCloseMe()
Debug.Print vbCrLf & vbTab & mstrName & ".JustCloseMe()" & vbTab & mCbo.Name & vbCrLf
Dim lstrParentName As String
lstrParentName = mCbo.Parent.Name
'Hard crashes Access absent Goldilocks:
DoCmd.Close acForm, lstrParentName, acSaveNo
End Sub
Private Sub mCbo_DblClick(Cancel As Integer)
Debug.Print mstrName & ".mCbo_DblClick()" & vbTab & mCbo.Name
mJustCloseMe
End Sub
'Private Sub mParent_Close()
'Fails to print:
'Debug.Print mstrName & ".mParent_Close()" & vbTab & mCbo.Name
'Fails to avoid an Access hard crash (any or in combination):
'Set mwecCboTestSecondary.BoundCbo = Nothing
'Set mwecCboTestSecondary = Nothing
'Set mParent = Nothing
'End Sub
'End wecCboTestPrimary
第二类:
'wecCboTestSecondary
Option Compare Database
Option Explicit
Private WithEvents mCbo As ComboBox
Private WithEvents mParent As Access.Form
Private mstrName As String
Private Sub Class_Initialize()
mstrName = "wecCboTestSecondary"
Debug.Print mstrName & ".Class_Initialize()"
End Sub
Private Sub Class_Terminate()
Debug.Print vbTab & vbTab & mstrName & ".Class_Terminate()"
' Fails to avoid an Access hard crash:
' Set mCbo = Nothing
Set mParent = Nothing
Debug.Print vbTab & vbTab & mstrName & ".Class_Terminate().End"
End Sub
Public Property Get BoundCbo() As ComboBox
Debug.Print mstrName & ".PPG BoundCbo()"
Set BoundCbo = mCbo
End Property
Public Property Set BoundCbo(lCbo As ComboBox)
Debug.Print mstrName & ".PPS BoundCbo()"
' Bind the control:
Set mCbo = lCbo
' Bind the control's parent:
Set mParent = lCbo.Parent
' Sink the parent's events:
mParent.OnClose = "[Event Procedure]"
Debug.Print mstrName & ".PPS BoundCbo()" & vbTab & mCbo.Name
End Property
Private Sub mParent_Close()
Debug.Print mstrName & ".mParent_Close()"
' Goldilocks:
Set mCbo = Nothing
End Sub
'End wecCboTestSecondary