【发布时间】:2014-11-26 16:47:33
【问题描述】:
我一直在努力找出分布在大约 40 个最终用户中的 PPT 插件中的错误原因。
问题:功能区状态丢失/功能区UI对象丢失。
对于某些用户,最终Rib 对象变为Nothing。
用户向我保证,他们没有收到任何运行时错误或脚本错误(来自我们也通过此插件调用的 COM 对象)。如果用户点击End,则会出现未处理的错误,预计会导致状态丢失。
没有一个用户能够可靠地重现导致观察到的故障的场景。这使得故障排除非常困难。我希望有一些明显的东西是我遗漏的,或者是我没有预料到的。
我目前如何处理丢失或 RibbonUI
为了解决这个问题,我将指向功能区的对象指针存储在 三个 位置,这对我来说似乎有点矫枉过正,但显然还不够:
- 一个名为
cbRibbon的类对象有一个属性.RibbonUI被赋值;Set cbRibbon.RibbonUI = Rib在功能区的onLoad回调过程中。所以我们有一个对象本身的byRef副本。如果功能区什么都不是,理论上我可以Set rib = cbRibbon.RibbonUI并且这有效,除非cbRibbon对象也超出范围。 -
cbRibbon对象的属性.Pointer被分配:cbRibbon.Pointer = ObjPtr(Rib)。 - 名为“RibbonPointer”的
CustomDocumentProperty也用于存储对对象指针的引用。 (注意:这种情况即使在状态丢失之后仍然存在)
所以你可以看到我已经对此进行了一些思考,试图复制存储此指针的方式,就像将它存储在 Excel 中的隐藏工作表/范围中一样。
其他信息
我可以从强大的客户端日志记录中看到,此错误似乎通常发生但并非总是在以下过程中发生,该过程用于刷新/使功能区及其控件无效。
只要我需要动态刷新功能区或其部分控件,就会调用此过程:
Call RefreshRibbon(id)
错误似乎(有时,我不能强调这一点:错误不能按需复制)发生在完全刷新期间,称为:
Call RefreshRibbon("")
这是进行失效的程序:
Sub RefreshRibbon(id As String)
If Rib Is Nothing Then
If RibbonError(id) Then GoTo ErrorExit
End If
Select Case id
Case vbNullString, "", "RibbonUI"
Call Logger.LogEvent("RefreshRibbon: Rib.Invalidate", Array("RibbonUI", _
"Ribbon:" & CStr(Not Rib Is Nothing), _
"Pointer:" & ObjPtr(Rib)))
Rib.Invalidate
Case Else
Call Logger.LogEvent("RefreshRibbon: Rib.InvalidateControl", Array(id, _
"Ribbon:" & CStr(Not Rib Is Nothing), _
"Pointer:" & ObjPtr(Rib)))
Rib.InvalidateControl id
End Select
Exit Sub
ErrorExit:
End Sub
如您所见,我在此过程中做的第一件事是测试Rib 对象的Nothing-ness。如果计算结果为 True,则 RibbonUI 对象已经丢失。
然后错误函数尝试重新实例化功能区:首先从cbRibbon.RibbonUI,然后从cbRibbon.Pointer,如果这两个都失败,然后从CustomDocumentProperties("RibbonPointer") 值。如果这些都没有成功,那么我们会显示一个致命错误,并提示用户关闭 PowerPoint 应用程序。如果其中任何一项成功,则功能区将以编程方式重新加载,一切都会继续工作。
这是该过程的代码。请注意,它调用了我没有包含代码的其他几个过程。这些是辅助函数或记录器函数。 .GetPointer 方法实际上是调用 WinAPI 的CopyMemory 函数从其指针值重新加载对象。
Function RibbonError(id As String) As Boolean
'Checks for state loss of the ribbon
Dim ret As Boolean
If id = vbNullString Then id = "RibbonUI"
Call Logger.LogEvent("RibbonError", Array("Checking for Error with Ribbon" & vbCrLf & _
"id: " & id, _
"Pointer: " & ObjPtr(Rib), _
"cbPointer: " & cbRibbon.Pointer))
If Not Rib Is Nothing Then
GoTo EarlyExit
End If
On Error Resume Next
'Attempt to restore from class object:
Set Rib = cbRibbon.ribbonUI
'Attempt to restore from Pointer reference if that fails:
If Rib Is Nothing Then
'Call Logger.LogEvent("Attempt to Restore from cbRibbon", Array(cbRibbon.Pointer))
If Not CLng(cbRibbon.Pointer) = 0 Then
Set Rib = cbRibbon.GetRibbon(cbRibbon.Pointer)
End If
End If
'Attempt to restore from CDP
If Rib Is Nothing Then
'Call Logger.LogEvent("Attempt to Restore from CDP", Array(MyDoc.CustomDocumentProperties("RibbonPointer")))
If HasCustomProperty("RibbonPointer") Then
cbRibbon.Pointer = CLng(MyDoc.CustomDocumentProperties("RibbonPointer"))
Set Rib = cbRibbon.GetRibbon(cbRibbon.Pointer)
End If
End If
On Error GoTo 0
If Rib Is Nothing Then
Debug.Print "Pointer value was: " & cbRibbon.Pointer
'Since we can't restore from an invalid pointer, erase this in the CDP
' a value of "0" will set Rib = Nothing, anything else will crash the appliation
Call SetCustomProperty("RibbonPointer", "0")
Else
'Reload the restored ribbon:
Call RibbonOnLoad(Rib)
Call SetCustomProperty("RibbonPointer", ObjPtr(Rib))
cbRibbon.Pointer = ObjPtr(Rib)
End If
'Make sure the ribbon exists or was able to be restored
ret = (Rib Is Nothing)
If ret Then
'Inform the user
MsgBox "A fatal error has been encountered. Please save & restart the presentation", vbCritical, Application.Name
'Log the event to file
Call Logger.LogEvent("RibbonError", Array("FATAL ERROR"))
Call ReleaseTrap
End If
EarlyExit:
RibbonError = ret
End Function
所有这些在理论上都运行良好,实际上我可以直接 kill 运行时(通过调用 End 语句或其他方式)并且这些过程按预期重置功能区。
那么,我错过了什么?
【问题讨论】:
-
查看我自己在 Access 2010 中处理功能区的方式,我发现我正在使用
CopyMemoryAPI 调用来设置功能区对象。像这样的东西:Private Function GetRibbon(lngRibPtr As Long) As Object: Dim objRibbon As Object: CopyMemory objRibbon, lngRibPtr, 4: Set GetRibbon = objRibbon: Set objRibbon = Nothing: End Function也许对你有帮助? -
@Bobort 是的,我实际上在内部使用那个 WinAPI 调用(在这个调用的
GetRibbon方法中:Set Rib = cbRibbon.GetRibbon(cbRibbon.Pointer))。这里的问题不是“如何从指针恢复功能区”,而是 A)导致功能区状态丢失的原因和 B)我可以在 PowerPoint 中可靠地保存指针值的位置(最好这不涉及写入磁盘或更改注册表,虽然我可以两者都做,但如果可能的话,我宁愿不做)。
标签: vba powerpoint ribbonx