Dynamic Ribbon & Runtime Errors

jet46

Registered User.
Local time
Today, 05:43
Joined
Sep 22, 2004
Messages
25
I am working with a dynamic ribbon for the first time in Access 2010 and I noticed that while developing my database if I halt code execution during an error while debugging, it loses the reference to the ribbon and I can no longer change controls on it (visible, enabled, etc...) and have to exit the database and go back in.

Now my question is, theoretically all my possible errors will be handled with proper error handling when I actually release my final version of the DB. Assuming all run time errors are properly handled, what are the chances the ribbon will stop working for some other reason? For example, if an end user leaves the database open for a week straight on their computer, will the reference to the ribbon be lost for any reason? Is there anything else I should be concerned about?

I have searched and searched for a way to reload the ribbon after an unhandled exception but can't find any solution. Should I not even worry about it if I am writing good code that doesn't result in unhandled exceptions?
 
Losing your ribbon reference is a hazard. Do your best to not have it happen. I consider it a weakness in the ribbon model that the reference is so fragile, but there it is.
Cheers,
 
I actually found a way to save the reference to the ribbon even after breaks in code. Well, I can't really take credit, I modified something that someone posted here http://www.mrexcel.com/forum/showthread.php?t=518629 regarding the same issue in Excel.

Basically I open a form and make it invisible and store the reference to the ribbon in a text box. As long as you never close that form, it will never lose the reference and if you lose the reference to the ribbon in your original variable, you can just set it again from the textbox in the form, which is retained through any errors.

My code is:

1. Create a form called "SystemForm" and put 1 unbound text box on it called "RBNPointer"

2. During your OnRibbonLoad callback event, tell it to store the info in your new form (as well as the original object I have always used). My function looks like this:

Code:
Sub OnRibbonLoad(ribbon As IRibbonUI)
    ' Callbackname in XML File "onLoad"

     Set gobjRibbon = ribbon
  
     Dim lngRibPtr As Long 'create a variable to store the UI id.

     lngRibPtr = ObjPtr(ribbon)
    
     If IsOpen("SystemForm") = False Then DoCmd.OpenForm "SystemForm"
     Forms!systemform.visible = False
     Forms!systemform!RBNPointer = lngRibPtr

End Sub
3. In a standard module add the following:

Code:
Public Declare Sub CopyMemory Lib "kernel32" Alias _
    "RtlMoveMemory" (destination As Any, Source As Any, _
    ByVal length As Long)


Function GetRibbon(lngRibPtr As Long) As Object
   Dim objRibbon As Object
   CopyMemory objRibbon, lngRibPtr, 4
   Set GetRibbon = objRibbon
   Set objRibbon = Nothing
End Function

Public Function IsOpen(ByVal strFormName As String) As Boolean 'Used to check if a form is open or not.
On Error GoTo Error1
    Dim i As Integer
    IsOpen = False
    For i = 0 To Forms.count - 1
        If Forms(i).Name = strFormName Then
            IsOpen = True
            Exit Function
        End If
    Next
    Exit Function

Error1:
MsgBox Err.Number & " " & Err.Description
Resume Next

End Function


4. Then whenever you need to run .Invalidate or .InvalidateControl you can use this before doing so:
Code:
If (gobjRibbon Is Nothing) Then  'Checks if the original reference to ribbon was lost
      Set gobjRibbon = GetRibbon(CLng(Forms!systemform!RBNPointer))
End If

gobjribbon.Invalidate

Now I can be sure that no matter what happens, I will always be able to regain control of my dynamic ribbon.
 
The answer is, handle all exceptions (or in VBA terms, errors).

The reference will not be lost if you have error handling code in every (necessary) function/sub.
 
vbaInet,

I agree and my final release versions I create do handle all errors but I am also maybe a little overly cautious in that for whatever yet unkown reason I wouldn't be able to regain control of the ribbon. I guess I just don't like the idea of the possibility of losing control of something and not being able to get it back and having unhappy customers. Now I don't have to worry, even if the situation would probably never happen anyway. Better to be safe than sorry.
 
Of course no one wants an unhappy customer ;)

Perhaps, you can write code to log a failure. Then after a year or so query that table to see if it ever failed :)

Happy developing!
 
First of all, I know that the last reply in this thread was a while ago.
Nevertheless I think my reply has got a small contribution for this thread, here it is:


thanks for the code to retrieve the HandleID!

I placed the HandleID in the persistent access properties and made the ribbon accessible through a property.

My code (VBA module)
Code:
Public Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (destination As Any, source As Any, ByVal length As Long)

Private Const rbHandleProp = "RibbonHandleID"

Private gRibbon As IRibbonUI

Property Get AppRibbon() As IRibbonUI
    If gRibbon Is Nothing Then
        Set gRibbon = GetRibbon(CLng(ReadProjProperty(rbHandleProp)))
    End If
    Set AppRibbon = gRibbon
End Property

Sub CallbackOnLoad(ribbon As IRibbonUI)
    Dim lngRibPtr As Long
    Set gRibbon = ribbon                     'Cache a copy RibbonUI
    lngRibPtr = ObjPtr(ribbon)                  'HandleID van Ribbon
    WriteProjProperty rbHandleProp, lngRibPtr   'Write HandleID
End Sub

Private Function GetRibbon(lngRibPtr As Long) As Object
   Dim objRibbon As Object
   CopyMemory objRibbon, lngRibPtr, 4
   Set GetRibbon = objRibbon
   Set objRibbon = Nothing
End Function

Public Function ReadProjProperty(Key As String) As Variant
On Error Resume Next
      ReadProjProperty = CurrentProject.Properties(Key).value
End Function

Public Sub WriteProjProperty(Key As String, value As Variant)
On Error Resume Next
      CurrentProject.Properties(Key).value = value
      If Err.Number = 2455 Then 'No Key!
          CurrentProject.Properties.Add Key, value
      End If
End Sub
And i always use this code in the forms and other modules for invalidating/activating the ribbon:
Code:
AppRibbon.Invalidate
AppRibbon.Activatetab "MyTab"
 
I actually found a way to save the reference to the ribbon even after breaks in code. Well, I can't really take credit, I modified something that someone posted here [link removed because of some bizarre forum-nazi rule] regarding the same issue in Excel.

...

Now I can be sure that no matter what happens, I will always be able to regain control of my dynamic ribbon.

Naturally your Nanny is going to pipe up and chide you about how ALL ERRORS MUST BE HANDLED (as if you didn't know that).

But what if you're a user-developer, and your workday includes frequent forays into the VBA editor to run routines that are often interrupted during development and testing? That blows the reference, and you are still required to restart Access if you want to use that obnoxious toolbar -- oh, excuse me, "ribbon" -- which you need to do to test your code.

Gawd only knows how many collective minutes I wasted over the past year doing that. But now you've posted a great hack to work around this glaring instance of developmental malpractice -- which Office >= 2007 is rife with (but you know that if you've been to Mr. Excel).

No, you get five Gold Stars (*****) not only for this hack, and for giving credit to your source, but also for your approach, which is, "if I can't control it, I can't distribute it." This makes you, in my book, a Top Gun and distinguishes you from the Chapter-And-Verse-quoting college boys who think perfection is attainable because MSFT says it must be in order for the product to work as advertised.

The real programmers plan for the opposite, because that's reality, and that's how you create apps that are robust.
 
Last edited:
First of all, I know that the last reply in this thread was a while ago.
Nevertheless I think my reply has got a small contribution for this thread, here it is:


thanks for the code to retrieve the HandleID!

I placed the HandleID in the persistent access properties and made the ribbon accessible through a property.

My code (VBA module)
Code:
Public Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (destination As Any, source As Any, ByVal length As Long)

Private Const rbHandleProp = "RibbonHandleID"

Private gRibbon As IRibbonUI

Property Get AppRibbon() As IRibbonUI
    If gRibbon Is Nothing Then
        Set gRibbon = GetRibbon(CLng(ReadProjProperty(rbHandleProp)))
    End If
    Set AppRibbon = gRibbon
End Property

Sub CallbackOnLoad(ribbon As IRibbonUI)
    Dim lngRibPtr As Long
    Set gRibbon = ribbon                     'Cache a copy RibbonUI
    lngRibPtr = ObjPtr(ribbon)                  'HandleID van Ribbon
    WriteProjProperty rbHandleProp, lngRibPtr   'Write HandleID
End Sub

Private Function GetRibbon(lngRibPtr As Long) As Object
   Dim objRibbon As Object
   CopyMemory objRibbon, lngRibPtr, 4
   Set GetRibbon = objRibbon
   Set objRibbon = Nothing
End Function

Public Function ReadProjProperty(Key As String) As Variant
On Error Resume Next
      ReadProjProperty = CurrentProject.Properties(Key).value
End Function

Public Sub WriteProjProperty(Key As String, value As Variant)
On Error Resume Next
      CurrentProject.Properties(Key).value = value
      If Err.Number = 2455 Then 'No Key!
          CurrentProject.Properties.Add Key, value
      End If
End Sub
And i always use this code in the forms and other modules for invalidating/activating the ribbon:
Code:
AppRibbon.Invalidate
AppRibbon.Activatetab "MyTab"

This is a great implementation of pointer restoration, but I recommend that you use tempvars instead of project properties (I use them to attach version numbers to database objects; they never go away). Project properties are persistent outside of runtime, so if your project calls any ribbon actions before the ribbon is fully initialized (believe me, it happens...:rolleyes:) your code could be trying to restore a previous pointer and causing access violations etc.

Tempvars however are non-persistent across access instances, so they're ideal for local pointers etc. So, instead of:
CurrentProject.Properties
Use
Application.TempVars

All the rest of the syntax is the same, and you won't have pesky persistent pointers to clean up in case of a break.

Just my 2 cents...
 

Users who are viewing this thread

Back
Top Bottom