Pérdida de complemento de PowerPoint de RibbonUI

He estado luchando por identificar la causa de un error en un complemento PPT que se distribuye entre aproximadamente 40 usuarios finales.

Problema: pérdida del estado de la cinta / pérdida del objeto ribbonUI.

Para algunos usuarios, eventualmenteRib objeto se convierteNothing.

Los usuarios me aseguran que no reciben errores de tiempo de ejecución ni errores de script (del objeto COM que también invocamos a través de este complemento). Un error no manejado, si el usuario golpeaEnd se espera que cause la pérdida del estado.

Ninguno de los usuarios ha podido reproducir de manera confiable el escenario que causa la falla observada. Esto es lo que hace que sea muy difícil solucionar problemas. Espero contra toda esperanza que haya algo obvio que me estoy perdiendo o que no anticipé.

Cómo manejo actualmente la pérdida o RibbonUI

En un intento por combatir esto, guardo el puntero del objeto en la cinta enTRES lugares, esto me parece excesivo, pero aparentemente todavía no es suficiente:

Un objeto de clase llamadocbRibbon tiene una propiedad.RibbonUI cual es asignadoSet cbRibbon.RibbonUI = Rib durante la cintaonLoad procedimiento de devolución de llamada. Entonces tenemos unbyRef copia del objeto en sí. Si la cinta no es nada, teóricamente puedoSet rib = cbRibbon.RibbonUI y esto funciona a menos quecbRibbon El objeto también está fuera de alcance.loscbRibbon objeto tiene propiedad.Pointer que se le asigna:cbRibbon.Pointer = ObjPtr(Rib).A CustomDocumentProperty llamado "RibbonPointer" también se utiliza para almacenar una referencia al puntero del objeto. (Nota: Esto persiste incluso más allá de la pérdida de estado.)

Como puede ver, he pensado un poco en esto en un intento de replicar la forma de almacenar este puntero de la forma en que uno podría almacenarlo en una hoja de cálculo / rango oculto en Excel.

Información Adicional

Puedo ver en el registro robusto del lado del cliente que este error parece suceder generalmente, pero no siempre, durante el siguiente procedimiento, que se utiliza para actualizar / invalidar la cinta y sus controles.

Este procedimiento se llama cada vez que necesito actualizar dinámicamente la cinta o parte de sus controles:

Call RefreshRibbon(id)

El error parece (a veces, no puedo enfatizar esto lo suficiente: el errorno puedo replicarse a pedido) suceden durante una actualización completa, que se llama así:

Call RefreshRibbon("")

Este es el procedimiento que hace la invalidación:

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

Como puede ver, lo primero que hago en este procedimiento es probar elRib objeto paraNothing-ness. Si esto se evalúa comoTrue, entonces el objeto RibbonUI de alguna manera se ha perdido.

La función de error luego intenta reiniciar la cinta:primero decbRibbon.RibbonUI, luego de lacbRibbon.Pointer y si ambos fallan, entonces delCustomDocumentProperties("RibbonPointer") valor. Si ninguno de estos tiene éxito, mostramos un error fatal y se le solicita al usuario que cierre la aplicación de PowerPoint. Si alguno de estos tiene éxito, la cinta se vuelve a cargar mediante programación y todo continúa funcionando.

Aquí está el código para ese procedimiento. Tenga en cuenta que llama a varios otros procedimientos para los que no he incluido el código. Estas son funciones auxiliares o funciones de registro. los.GetPointer El método en realidad invoca el WinAPICopyMemory función para recargar el objeto desde su valor de puntero.

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

Todo esto funciona perfectamente bien en teoría y, de hecho, puedomatar tiempo de ejecución (invocando elEnd declaración o no) y estos procedimientos restablecen la cinta como se esperaba.

Entonces, ¿qué me estoy perdiendo?

Respuestas a la pregunta(2)

Su respuesta a la pregunta