PowerPoint Add-In-Verlust von RibbonUI
Ich habe Probleme, die Ursache eines Fehlers in einem PPT-Add-In zu identifizieren, das auf ungefähr 40 Endbenutzer verteilt ist.
Problem Verlust des Ribbon-Status / Verlust des RibbonUI-Objekts.
Für einige Benutzer kann dasRib
Objekt wirdNothing
.
Benutzer versichern mir, dass sie keine Laufzeit- oder Skriptfehler erhalten (von COM-Objekten, die wir auch über dieses Add-In aufrufen). Ein unbehandelter Fehler, wenn der Benutzer auf @ klickEnd
würde voraussichtlich den Staatsverlust verursachen.
Keiner der Benutzer konnte das Szenario, das den beobachteten Fehler verursacht, zuverlässig reproduzieren. Aus diesem Grund ist die Fehlersuche sehr schwierig. Ich hoffe gegen die Hoffnung, dass es etwas Offensichtliches gibt, das ich vermisse oder das ich nicht erwartet habe.
Wie ich momentan mit Verlust oder RibbonUI umgehe
Um dem entgegenzuwirken, speichere ich den Objektzeiger auf das Menüband inDRE Orte, das scheint mir übertrieben, aber es ist anscheinend immer noch nicht ausreichend:
Ein Klassenobjekt mit dem NamencbRibbon
hat eine Eigenschaft.RibbonUI
welches zugewiesen ist;Set cbRibbon.RibbonUI = Rib
während des BändchensonLoad
Rückrufprozedur. Also haben wir einbyRef
Kopie des Objekts selbst. Wenn das Band nichts ist, kann ich theoretischSet rib = cbRibbon.RibbonUI
und das funktioniert, wenncbRibbon
Objekt liegt ebenfalls außerhalb des Gültigkeitsbereichs.DascbRibbon
Objekt hat Eigenschaft.Pointer
welches zugewiesen ist:cbRibbon.Pointer = ObjPtr(Rib)
.A CustomDocumentProperty
namens "RibbonPointer" wird auch verwendet, um eine Referenz auf den Objektzeiger zu speichern. Hinweis: Dies bleibt auch über den Staatsverlust hinaus bestehen)So können Sie sehen, dass ich einige Überlegungen angestellt habe, um die Art und Weise des Speicherns dieses Zeigers so zu replizieren, wie man ihn in einem verborgenen Arbeitsblatt / Bereich in Excel speichern könnte.
Zusätzliche Informatio
Ich kann anhand der robusten clientseitigen Protokollierung feststellen, dass dieser Fehler normalerweise, jedoch nicht immer, während der folgenden Prozedur auftritt, die zum Aktualisieren / Ungültigmachen des Menübands und seiner Steuerelemente verwendet wird.
Diese Prozedur wird jedes Mal aufgerufen, wenn ich das Menüband oder einen Teil seiner Steuerelemente dynamisch aktualisieren muss:
Call RefreshRibbon(id)
Der Fehler scheint zu (manchmal kann ich das nicht genug betonen: der Fehlerkann nich bei Bedarf repliziert werden) während einer vollständigen Aktualisierung erfolgen, die wie folgt aufgerufen wird:
Call RefreshRibbon("")
Dies ist die Prozedur, die die Ungültigerklärung vornimmt:
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
ie Sie sehen können, ist das allererste, was ich in diesem Verfahren tue, das Testen desRib
Objekt fürNothing
-ness. Wenn dies zu @ ausgewertet wiTrue
, dann ist das RibbonUI-Objekt irgendwie verloren gegangen.
Die Fehlerfunktion versucht dann, das Menüband erneut zu instanziieren:zuers voncbRibbon.RibbonUI
, dann von dercbRibbon.Pointer
und wenn beide fehlschlagen, dann von derCustomDocumentProperties("RibbonPointer")
Wert. Wenn keiner dieser Vorgänge erfolgreich ist, wird ein schwerwiegender Fehler angezeigt und der Benutzer wird aufgefordert, die PowerPoint-Anwendung zu schließen. Wenn einer dieser Vorgänge erfolgreich ist, wird das Menüband programmgesteuert neu geladen und alles funktioniert weiterhin.
Hier ist der Code für diese Prozedur. Beachten Sie, dass es mehrere andere Prozeduren aufruft, für die ich keinen Code eingefügt habe. Dies sind Hilfs- oder Loggerfunktionen. Das.GetPointer
-Methode ruft tatsächlich die WinAPI aufCopyMemory
Funktion zum erneuten Laden des Objekts von seinem Zeigerwert.
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
All dies funktioniert in der Theorie perfekt und in der Tat kann ich direkttöte run-time (durch Aufrufen desEnd
statement oder anders) und diese Prozeduren setzen das Menüband wie erwartet zurück.
Also, was fehle ich?