VBA – WIA – Resize/Scale an Image

I was trying to help someone out in the MSDN forum, pictures change height width, with wanting to resize images from within an MS Access database. As you can see from my initial post, there are a multitude of possible approaches:

After my initial post just offering a few options, I decided to hand the OP a working solution because I know it isn’t always easy to translate some of our suggestions into concrete solutions, so I created the following Function which employs WIA.  As you can see, it is very simple and straightforward.  As always, to avoid requiring any reference libraries, this function uses Late Binding techniques.

'---------------------------------------------------------------------------------------
' Procedure : WIA_ResizeImage
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Resize an image based on Max width and Max height using WIA
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Microsoft Windows Image Acquisition Library vX.X
'
' Windows Image Acquisition (WIA)
'   https://msdn.microsoft.com/en-us/library/windows/desktop/ms630368(v=vs.85).aspx
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sInitialImage : Fully qualified path and filename of the original image to resize
' sResizedImage : Fully qualified path and filename of where to save the resized image
' lMaximumWidth : Maximum allowable image width
' lMaximumHeight: Maximum allowable image height
' bPreserveAspectRatio: Whether the image aspect ratio should be preserved, or not
' bOverwrite    : Whether it should overwrite the output file if it already exists
'
' Usage:
' ~~~~~~
' Call WIA_ResizeImage("C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg", _
'                      "C:\Users\MyUser\Desktop\Chrysanthemum_small.jpg", _
'                      800, 600)
' Call WIA_ResizeImage("C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg", _
'                      "C:\Users\MyUser\Desktop\Chrysanthemum_small.jpg", _
'                      800, 600, False)
' Call WIA_ResizeImage("C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg", _
'                      "C:\Users\MyUser\Desktop\Chrysanthemum_small.jpg", _
'                      800, 600, True, True)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2017-01-18              Initial Release
' 2         2022-12-15              Added bPreserveAspectRatio argument
'                                   Added bOverwrite argument
'---------------------------------------------------------------------------------------
Public Function WIA_ResizeImage(sInitialImage As String, sResizedImage As String, _
                                lMaximumWidth As Long, lMaximumHeight As Long, _
                                Optional bPreserveAspectRatio As Boolean = True, _
                                Optional bOverwrite As Boolean = False) As Boolean
On Error GoTo Error_Handler
    #Const WIA_EarlyBind = False    'True => Early Binding / False => Late Binding
    #If WIA_EarlyBind = True Then
        Dim oIF               As WIA.ImageFile
        Dim oIP               As WIA.ImageProcess

        Set oIF = New WIA.ImageFile
        Set oIP = New WIA.ImageProcess
    #Else
        Dim oIF               As Object
        Dim oIP               As Object

        Set oIF = CreateObject("WIA.ImageFile")
        Set oIP = CreateObject("WIA.ImageProcess")
    #End If

    If Len(Dir(sResizedImage)) > 0 Then
        If bOverwrite = True Then
            Kill sResizedImage
        Else
            Exit Function
        End If
    End If

    oIP.Filters.Add oIP.FilterInfos("Scale").FilterID
    oIP.Filters(1).Properties("MaximumWidth") = lMaximumWidth
    oIP.Filters(1).Properties("MaximumHeight") = lMaximumHeight
    oIP.Filters(1).Properties("PreserveAspectRatio") = bPreserveAspectRatio

    oIF.LoadFile sInitialImage
    Set oIF = oIP.Apply(oIF)
    oIF.SaveFile sResizedImage
    WIA_ResizeImage = True

Error_Handler_Exit:
    On Error Resume Next
    Set oIP = Nothing
    Set oIF = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: WIA_ResizeImage" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

The function will return True when the Resize operation is successful and False in all other cases. So you can validate your calls to the function very easily.

Also note that this function can both reduce or blow up an image. It works fine either way, but obviously, blowing up a picture will automatically imply a lost of quality.

VBA – Add Appointment to Outlook Calendar

Yet again, I was trying to help someone out in an Access forum who was trying to write code to add an appointment to their Outlook Calendar. So I dug up a function I had developed for a project I had worked on many years ago and thought it might help other.

'---------------------------------------------------------------------------------------
' Procedure : CreateAppointment
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Create Appointments in Outlook
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding  -> None required
'             Early Binding -> Microsoft Outlook XX.X Object 
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sSubject              : Subject line
' sBody                 : Body text
' sLocation             : Location of appointment
' dtStart               : Start Date/Time
' dtEnd                 : End Date/Time
' bAllDayEvent          : Is the appointment a full day event True/False
' bDisplayToUser        : Display the newly created appointment item to the end-user True/False
' sCategories           : Name of the Category(ies) to assign to the appointment
' lReminderMinsBefore   : Number of minute prior to the appointment to prompt a reminder
' sOrganizers           : Name/Email of the meeting orgainizer
' sRequiredAttendees    : Name/Email of the required meeting attendees
' sOptionalAttendees    : Name/Email of the optional meeting attendees
' sResources            : Name/Email of the meeting resources
'
' Usage:
' ~~~~~~
' Call CreateAppointment("Project Review", "Initial Delivery Review Metting with the client", _
                         "Main Board room", #01/13/2017 12:00#, #01/13/2017 14:00#, False)
' Call CreateAppointment("Project Review", "Initial Delivery Review Metting with the client", _
                         "Main Board room", #01/13/2017 12:00#, #01/13/2017 14:00#, False, True)
' Call CreateAppointment("Project Review", "Initial Delivery Review Metting with the client", _
                         "Main Board room", #9/6/2018 12:00:00 PM#, #9/6/2018 2:00:00 PM#, False, True, _
                         "Green Category")
' Call CreateAppointment("Project Review", "Initial Delivery Review Metting with the client", _
                         "Main Board room", #09/06/2018 12:00#, #09/06/2018 14:00#, False, True, _
                         "Green Category, Red Category")
' Call CreateAppointment("Project Review", "Initial Delivery Review Metting with the client", _
                         "Main Board room", #09/06/2018 12:00#, #09/06/2018 14:00#, False, True, _
                         "Green Category, Red Category", 60, "someone@somehwere.com", _
                         "someonelese@somewhere.com;anotherperson@somewhere.com", _
                         "yetanotherperson@somedomain.com")
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2011-11-17              Initial Release
' 2         2017-01-16              Added GetObject(), instead of just CreateObject()
'                                   Added sLocation
'                                   Added bDisplayToUser
' 3         2017-02-21              Added Conditional Compiling Directives (CCDs) for
'                                       switching between Early and Late Binding
' 4         2018-09-06              Added sCategories
'                                       updated copyright
' 5         2024-05-08              Added meeting reminder
'                                   Added meeting attendees & resources
' 6         2025-01-31              Fixed #Const EarlyBind declaration for Late Binding
'                                      per Jesse Fuller's comment
'---------------------------------------------------------------------------------------
Public Function CreateAppointment(sSubject As String, _
                                  sBody As String, _
                                  sLocation As String, _
                                  dtStart As Date, _
                                  dtEnd As Date, _
                                  bAllDayEvent As Boolean, _
                                  Optional bDisplayToUser As Boolean = False, _
                                  Optional sCategories As String, _
                                  Optional lReminderMinsBefore As Long = 0, _
                                  Optional sOrganizers As String, _
                                  Optional sRequiredAttendees As String, _
                                  Optional sOptionalAttendees As String, _
                                  Optional sResources As String) As Boolean
    On Error GoTo Error_Handler
    #Const EarlyBind = False    'Use Late Binding, No Reference Library Required
    '    #Const EarlyBind = True    'Use Early Binding, Requires Reference Library
    #If EarlyBind = True Then
        Dim OApp              As Outlook.Application
        Dim OAppt             As Outlook.AppointmentItem
        Dim oRecipient        As Outlook.Recipient
    #Else
        Dim OApp              As Object
        Dim OAppt             As Object
        Dim oRecipient        As Object
        Const olAppointmentItem = 1
        Const olOrganizer = 0
        Const olRequired = 1
        Const olOptional = 2
        Const olResource = 3
    #End If
    Dim aAttendees()          As String
    Dim iCounter              As Long
    Dim bAppOpened            As Boolean

    'Initiate our instance of the oApp object so we can interact with Outlook
    On Error Resume Next
    Set OApp = GetObject(, "Outlook.Application")    'Bind to existing instance of Outlook
    If Err.Number <> 0 Then    'Could not get instance of Outlook, so create a new one
        Err.Clear
        Set OApp = CreateObject("Outlook.Application")
        bAppOpened = False    'Outlook was not already running, we had to start it
    Else
        bAppOpened = True    'Outlook was already running
    End If
    On Error GoTo Error_Handler

    'Start creating our Appointment
    Set OAppt = OApp.CreateItem(olAppointmentItem)
    With OAppt
        '        .To = ""
        .Start = dtStart
        .End = dtEnd
        .AllDayEvent = bAllDayEvent
        .Subject = sSubject
        .Body = sBody
        '.RTFBody = sBody 'If using formatted text
        .Location = sLocation
        If IsMissing(sCategories) = False Then .Categories = sCategories
        '        .ShowCategoriesDialog
        '        MsgBox .Categories
        If bDisplayToUser = True Then .Display

        If lReminderMinsBefore > 0 Then
            .ReminderSet = True
            .ReminderMinutesBeforeStart = lReminderMinsBefore
        Else
            .ReminderSet = False
        End If


        If sOrganizers <> "" Then
            If Right(sOrganizers, 1) = ";" Then sOrganizers = Left(sOrganizers, Len(sOrganizers) - 1)
            aAttendees = Split(sOrganizers, ";")
            For iCounter = 0 To UBound(aAttendees)
                Set oRecipient = .Recipients.Add(aAttendees(iCounter))
                oRecipient.Type = olOrganizer
                DoEvents
            Next iCounter
        End If
        If sRequiredAttendees <> "" Then
            If Right(sRequiredAttendees, 1) = ";" Then sRequiredAttendees = Left(sRequiredAttendees, Len(sRequiredAttendees) - 1)
            aAttendees = Split(sRequiredAttendees, ";")
            For iCounter = 0 To UBound(aAttendees)
                Set oRecipient = .Recipients.Add(aAttendees(iCounter))
                oRecipient.Type = olRequired
                DoEvents
            Next iCounter
        End If
        If sOptionalAttendees <> "" Then
            If Right(sOptionalAttendees, 1) = ";" Then sOptionalAttendees = Left(sOptionalAttendees, Len(sOptionalAttendees) - 1)
            aAttendees = Split(sOptionalAttendees, ";")
            For iCounter = 0 To UBound(aAttendees)
                Set oRecipient = .Recipients.Add(aAttendees(iCounter))
                oRecipient.Type = olOptional
                DoEvents
            Next iCounter
        End If
        If sResources <> "" Then
            If Right(sResources, 1) = ";" Then sResources = Left(sResources, Len(sResources) - 1)
            aAttendees = Split(sResources, ";")
            For iCounter = 0 To UBound(aAttendees)
                Set oRecipient = .Recipients.Add(aAttendees(iCounter))
                oRecipient.Type = olResource
                DoEvents
            Next iCounter
        End If

        .Save
        '        .Send
    End With

    CreateAppointment = True

    'If our code started Outlook, we should close it now that we're done
    If bAppOpened = False Then
        OApp.Quit    'There seems to be a delay in this action taking place, but does eventually take place
    End If

Error_Handler_Exit:
    On Error Resume Next
    If Not OAppt Is Nothing Then Set OAppt = Nothing
    If Not OApp Is Nothing Then Set OApp = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: CreateAppointment" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl), _
           vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

The beauty of the above is that by default (yes, you can change it if you wish) it uses Late Binding and thus you do not need to add any reference libraries to your VBA project. Simply drop this into a standard module and it works. As always, don’t forget about my work around for situations where CreateObject(“Outlook.Application”) does not work: CreateObject(“Outlook.Application”) Does Not Work, Now What?.

Other Resources

Convert Your Access Database Into An Exe

Over the years, I’ve seen the same question come up time and time again:

How can I convert my MS Access database into an exe so I can sell it

Further discussion typically brings up the notion that they wish it to run as it’s stand alone program and not require the end-user to have MS Access installed (like other programs found on the Net).

Now, in a general sense, this is simply not possible.  If this was the original intent of your application, one should have used one of many alternative programming languages which allow for compiling into an exe.

That said, long, long ago, did come across a website in which instructions were provided to package everything up and indeed convert it into an exe.  Let me be perfectly clear, whether or not it worked back then, or continues to work today, it would be illegal as one does not have copyright to repackage the Access application in this manner.  This would be a clear violation of MS’ copyright!

So what can be done then?  No matter what database format you use mdb, mde, accdb, accde, accdr, … the end-user must have Microsoft Access (full version or runtime) installed to run it.  So while you cannot in fact convert your database itself into an exe, you can package it, as an exe, to perform the installation, but the final format, once installed, will remain an MS Access database file (mdb, mde, accdb, accdb, accde, accdr, … ).

If you are wanting to go down the route of creating an installer for your database, then take a look at MS Access Package Solution Wizard is Dead, Now What? as it lists a few installers you could look at.  By packaging the database, you will indeed create an exe that you can distribute/sell and it, in turn, can install the free Microsoft Access runtime (you should check to make sure they don’t already have the full version installed already before blindly installing the runtime), create the necessary folder structure(s), install other dependencies, perform registry operations (create a Trusted Location for the FE for instance) and lastly install your database.

Lastly, if you are at the point of looking to package your database application be sure to properly secure it prior to doing so.  To do so please read Securing Your MS Access Database Front-End.

 

Bye Bye BELL, Hello Anyone Else!

After some 20 years with Bell, we finally ditched them as our service provider this summer and haven’t looked back!!!

Never have I experienced such poor quality of services (years of crackling on phone lines, Internet disruptions/instability), but worst of all is their customer service which is dismal. They make the mistakes and then tell you to calm down.  They mistakenly cancelled all our services on 2 occasions (and the service reps laughed at us), misbilled us and couldn’t fix it promptly (when I say promptly, I mean within a month’s time) and God only knows how much time I’ve spent on the phone with them to resolve their issues over the years.  They’ve made me rewire my entire house, buy a new phone, … and as I always told them it had nothing to do with our setup, it was their setup that was flawed, but their own personnel couldn’t figure it out for months and kept telling us to do this and that because they had no clue!

The only time I ever got any real service was when I sent an e-mail to the president of Bell, promptly got a phone call back from the VP and things were quickly fixed, but one should not need to do so and their contact information is not publicly available (I was able to figure it out by doing some serious digging because I had enough of their customer service run around).

Anyways, the point of this post was not to continue bringing up all there problems (I could go on all day as I have years of issues to elaborate on), …

No, the point of this post is to inform people, especially people living in the Greater Montreal area that there are numerous very viable options to get away from the BIG multinational service providers and start being treated like an actual human being rather than a number.

I Googled to see what was available, compared websites, spoke to friends and family and came up with several options available to us.

One website I found useful was Internet providers in Montreal in which I started looking at companies such as:

  • AEI
  • Bravo Telecom
  • TekSavvy

Then this summer, we made what felt like a leap, but in reality was the easiest thing we ever did. We now get faster, cheaper Internet with almost double to the monthly capacity (we don’t need unlimited) and we also switched over to VOIP telephone so we only pay 40$/year for our phone and get all the bells and whistles and we can call anywhere in Canada and the US at no charge (BELL charges more than that for a single month with their basic plan with which we can’t call outside of Quebec without incurring extra charges)!  The service took 10 minutes to install and I was up and running.  Everything has been running smooth ever since (it must be 4-5 month in now).

All of this to say, if you:

  • Aren’t happy with your current service provider
  • Find you are monthly bill is excessive
  • Etc …

I’d urge you to look around because there truly are good options available to you.

Lastly, one thing I will mention was that when I researched my options, numerous people (and I do mean a lot of people) kept telling me to switch to Videotron. Everyone that mentioned Videotron mentioned how good their customer service was and how they never experienced any issues.  I personally was no longer interested in using one of the Big Telecom companies and went with a smaller local company, but I thought I’d mention it in case it could help any of you.

Disable the MS Access Date Picker

I won’t lie, I hate the built-in MS Access Date Picker. Once again, Microsoft did things as poorly as possible by implementing the most primitive Date Picker possible. One that only moves forwards or backwards one month at a time. No Month selector. No Year selector. No ability to interact with it through VBA!

MS Access Date Picker

Try using the built-in Date Picker to enter someone’s birthdate. For instance, try using it to enter April 1st, 1941. I hope you have some time on your hands as you’re going to have to click on the past month button 907 TIMES!!!

With a great many available Date Pickers to inspire themselves with/from, I still cannot remotely grasp how this one was adopted. We’re supposed to facilitate the end-user’s jobs by simplifying data entry, not making their lives a living nightmare.

Okay, enough with the rant, let’s move forward and see how we can actually help our users! The reality is that the best thing we can do is adopt one of many form based date pickers like one of the numerous ones I listed in my post: https://www.devhut.net/2017/06/17/great-access-tools-calendar-controls/.

That said, we have to first disable the built-in Date Picker from always displaying (possibly overtop of our calendar button). Now, we can open each form individually and then edit the Show Date Picker property of each date control, but this is time consuming and one can easily overlook a control or two. As such, I set out to find a better way than having to manually edit individual form control properties and knew that we could harvest the power of VBA to rescue us from this Date Picker affliction.

As such, I put together the following code which iterates through all the forms, and all the controls within each form to change the property (sadly, there is no global database setting and it has to be set control by control!).

'---------------------------------------------------------------------------------------
' Procedure : disableDatePicker
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Disable the built-in date picker in all the database forms.  This is to
'             avoid issue with using the calendar form that we have chosen to implement.
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
'
' Usage:
' ~~~~~~
'
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2011-Oct-09                 Initial Release
'---------------------------------------------------------------------------------------
Sub DisableDatePicker()
    On Error GoTo Error_Handler
    Dim ctl             As Access.Control
    Dim frm             As AccessObject

    For Each frm In CurrentProject.AllForms
        DoCmd.OpenForm frm.Name, acDesign

        For Each ctl In Forms(frm.Name).Form.Controls
            Select Case ctl.ControlType
                Case acTextBox
                    ctl.ShowDatePicker = 0
            End Select
        Next ctl

        DoCmd.Close acForm, frm.Name, acSaveYes
    Next frm

Error_Handler_Exit:
    On Error Resume Next
    Set ctl = Nothing
    Set frm = Nothing
    Exit Sub

Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: DisableDatePicker" & vbCrLf & _
           "Error Description: " & Err.Description, _
           vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Sub

This is now one of the first procedures that I run on databases that I take over as part of my cleanup routine. Hopefully it can help other too!

VBA – Early Binding and Late Binding

Early Binding and Late Binding

When coding using VBA, as a developer, you have a choice of either using Early Binding or Late Binding.
Below I will attempt to explain in plain English what each technique consists of and go over their Pros and Cons.
Please keep in mind the advice provided below is all relative to Early and Late Binding, nothing more.

Early Binding

Early Binding consists in setting specific Library References within your VBA Project (Tools -> References).

VBA - Tools -> References Menu

VBA - References Dialog

Benefits

The benefits of using Early Binding include:

  • The use of Intellisense
    • Say your add a reference to Excel, then you gain all the Intellisense relating to Excel (properties, Methods, …)
  • The use of constants
    • Say your add a reference to Excel, then you gain the ability to directly use Excel’s application specific constants, things like: xlSolid, xlContinuous, xlCenter, xlByRows, …
  • Use of the Object Browser
  • Help (using F1)
    • You can use F1 to quickly access help relating to any property, method, …, but this will err with Late Binding.

Drawbacks

The MAJOR (or not depending on your user’s setup) drawback to Early Binding is that it can lead to versioning issues when your program is used on multiple versions of MS Office (or whatever program you are automating). Say you develop a 2013 accdb database and set the Microsoft Excel 15.0 Object Library because you perform some Excel automation. Now if you have a user try and open the accdb in 2010, they will be presented with errors at the startup
[Pic] , and the database itself will throws seemingly random errors with code that has nothing to do with Excel to the point of being unusable.
[Pic]?

On the other hand, if a user opens the same accdb in Access 2016, Access, if a newer library is registered, will automatically upgrade the reference and as such, the database will work seamlessly.

So keeping this in mind, this is why you should always perform any development using the oldest version of the program on which your solution will be run. So if you have users running Office 2013, 2016 and 2003, you need to perform your development using Office 2003 if you are using Early Binding!

Early Binding Example

'Function to open a specified Word file
'Req'd: Reference to Microsoft Word XX.X Object Library
Function OpenWordDoc1(sFileName As String)
    Dim oApp                  As Word.Application
    Dim oDoc                  As Word.Document

    On Error Resume Next
    Set oApp = GetObject(, "Word.Application")
    If Err.Number <> 0 Then    'Word isn't running so start it
        Set oApp = CreateObject("Word.Application")
    End If
    On Error GoTo Error_Handler

    Set oDoc = oApp.Documents.Open(sFileName)
    oApp.Visible = True

Error_Handler_Exit:
    On Error Resume Next
    Set oDoc = Nothing
    Set oApp = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: OpenWordDoc" & vbCrLf & _
           "Error Description: " & Err.Description, _
           vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

The above can also be re-written as follows when using Early Binding (although I always use the above approach as there have been Bugs noted when using the New … coding)

'Function to open a specified Word file
'Req'd: Reference to Microsoft Word XX.X Object Library
Function OpenWordDoc2(sFileName As String)
    Dim oApp                  As Word.Application
    Dim oDoc                  As Word.Document

    On Error Resume Next
    Set oApp = GetObject(, "Word.Application")
    If Err.Number <> 0 Then    'Word isn't running so start it
        Set oApp = New Word.Application 'CreateObject("Word.Application")
    End If
    On Error GoTo Error_Handler

    Set oDoc = oApp.Documents.Open(sFileName)
    oApp.Visible = True

Error_Handler_Exit:
    On Error Resume Next
    Set oDoc = Nothing
    Set oApp = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: OpenWordDoc" & vbCrLf & _
           "Error Description: " & Err.Description, _
           vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

Late Binding (Dynamic Binding)

Late Binding on the other hand does not make use of pre-defined Libraries and hence it’s MAJOR benefit and thus does not suffer from versioning issues. Code written in Office 2016 will typically run just fine in Office 2013, 2010, …, 97 (always assuming the library is registered on the PC – You can’t perform say Excel automation if Excel isn’t installed!).

Late binding, or dynamic binding, is a computer programming mechanism in which the method being called upon an object is looked up by name at runtime.

Benefits

The key benefits of Late Binding include:

  • Does not require declaring Reference Libraries
  • Does not suffer from versioning issues

Drawbacks

The main drawbacks of Late Binding include:

  • Loss of Intellisense during development
  • No constants, so you have to declare the constants yourself when writing your code
  • Since the process is all done in real-time (dynamically binding to libraries in real-time), technically speaking, Late Binding will be slower that Early Binding. That said, with today’s computer hardware, the difference is truly not noticeable. So to me, this point is moot.

Late Binding Example

'Function to open a specified Word file
'Req'd: None
Function OpenWordDoc(sFileName As String)
Dim oApp As Object 'Word.Application
Dim oDoc As Object 'Word.Document
 
On Error Resume Next
    Set oApp = GetObject(, "Word.Application")
    If Err.Number <> 0 Then 'Word isn't running so start it
        Set oApp = CreateObject("Word.Application")
    End If
On Error GoTo Error_Handler
 
    Set oDoc = oApp.Documents.Open(sFileName)
    oApp.Visible = True
 
Error_Handler_Exit:
    On Error Resume Next
    Set oDoc = Nothing
    Set oApp = Nothing
    Exit Function
 
Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
            "Error Number: " & Err.Number & vbCrLf & _
            "Error Source: OpenWordDoc" & vbCrLf & _
            "Error Description: " & Err.Description, _
            vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

The Best of Both World

Now that you understand the basic concepts and see the benefit of Early Binding (Intellisense to aid in coding) and the benefit of Late Binding (no versioning issues, thus can be distributed with much greater ease) we can explorer how we can combine both techniques to gain the best of both worlds.

Although the coding examples provided above are very simple ones, they do illustrate just how simple it is, normally, to convert between Early and Late Binding. As such, it is very easy during development to work in Early Binding to ease the coding process and then when you are ready to distribute your solution into production you simply switch to Late Binding by changing a few declarations and hard coding a few constants.

You may also wish to review Late Binding in Microsoft Access in which Tony Toews demonstrates how you can use conditional compiling instruction to accommodate both techniques within the same code

Some thoughts about all of the above

As an experienced developer I have, over time, come to truly value Late Binding for the flexibility it provides and use it in all my more recent code.

That being said, if you are a corporate developer in an organization with a very controlled environment in which you can be guaranteed that everyone is running the same versions of Office (or whatever program you are automating) then you can utilize Early Binding without any issues.

The Story Continues

Be sure to check out part 2 of this article: VBA – Early Binding and Late Binding – Part 2 in which I delve more into how to implement The Best of Both Worlds.

Resources

Using early binding and late binding in Automation
Early vs. Late Binding
Early vs. Late Binding
VBA references and early binding vs late binding

Windows Explorer MS Access Database Right-Click Context Menu

I was part of a discussion, Easy Use Command Switches \decompile \excl \compact \runtime Etc, Access 2010 , regarding Windows Explorer Context Menus for sending/opening accdb files to:

  • Compact
  • Decompile
  • Runtime

Up until now, I simply created a couple of shortcuts with I added to the SendTo Menu as described in the Reusable decompilation method section of the MS Access – Decompile a Database which did work very nicely and is relatively easy to setup.

but each of the above methods does present certain issues:

  • SendTo approach always displays the options regardless of the file extension, so you could technically try sending incorrect files to Access.
  • Jon Smith’s approach:
    • requires pressing the SHIFT key for the options to display (I do not like hidden features!)
    • currently it only works with accdb file extensions. so it currently does not support accdr, accde, accda, mbd, mde, mda, …
    • Is hardcoded for Access 2010 requiring editing the reg files for every version of Access you wish to install it on.

So I set out to try and figure out a better a solution, one that would:

  • only be available for all access files (or whatever extensions I specify)
  • did not require extra key sequences
  • did not require manual editing of setup files

and this is why, last night, I developed the Windows Explorer MS Access Database Right-Click Context Menu Creator.

Continue reading

Enhanced ControlTip for MS Access

I was trying to help in a forum discussion:

Creating A Dynamic Form To Replace Controltiptext?, Access 2013

and finally decided to create a proof of concept of my general idea for an Enhanced ControlTip.

MS Access Enhanced ControlTipOver the years and over the various editions of MS Access I have found ControlTips to be useful, but unreliable.  Under various conditions, they don’t seem to always display, or display promptly and then become frustrating.  So my basic idea was to replace the built-in ControlTip with a dynamic pop-up form which could display my text.

In the attached demo file below I have included three forms, each illustrating a slight twist on the general concept:

  • Display the Custom Control Tip when you move the mouse over each form control (F_Main_MouseMove)
  • Display the Custom Control Tip when each control gets the focus (F_Main_GotFocus)
  • Display the Custom Control Tip when you press a key sequence in each control (F_Main_Shortcut)

Features

By using such an approach we can avoid the native issues experienced with the built-in ControlTip property where it doesn’t display, or there are delays.

But beyond that, this approach offers other advantages, such as:

  • Rich Text so you can format the text being displayed to make it much more appealing and emphasize certain aspects.
  • You can format the underlying form to fit in with your overall database feel.
  • You could add a delay to display the Enhanced ControlTip as you see fit very easily.

At the end of the day, the Enhanced ControlTip is much more customizable and as a developer you can take it wherever you’d like!

Disclaimer/Notes:

If you do not have Microsoft Access, simply download and install the freely available runtime version (this permits running MS Access databases, but not modifying their design):

Microsoft Access 2010 Runtime
Microsoft Access 2013 Runtime
Microsoft Access 2016 Runtime
Microsoft 365 Access Runtime

All code samples, download samples, links, ... on this site are provided 'AS IS'.

In no event will Devhut.net or CARDA Consultants Inc. be liable to the client/end-user or any third party for any damages, including any lost profits, lost savings or other incidental, consequential or special damages arising out of the operation of or inability to operate the software which CARDA Consultants Inc. has provided, even if CARDA Consultants Inc. has been advised of the possibility of such damages.

 

Download

Feel free to download a copy (unlocked accdb) by using the link provided below:

Download “MS Access Enhanced ControlTip” Enhanced_ControlTip_V1.zip – Downloaded 38593 times – 74.89 KB

Taking things Further

The next step would be to allow to possibly implement a WebBrowser Control and use pure HTML instead of RichText because it is more versatile. We could also explore making the pop-up form have a dynamic sizing to adapt to the ControlTip content. We could include a form caption and make it dynamically populated as part of the routine. And much more …

To be continued at a later date.

Update Breaks Office 2010

Just passing along a recent warning from fellow MVP Scott Diamond in which he informed us that update(s ) pushed yesterday (October 11th, 2016) appear to have broken Microsoft Office 2010.

When I opened Excel, I couldn’t see the ribbon tabs. When I opened Access I got the same thing. Also when I tried to link to an external table. The Browse button wouldn’t work and the radio buttons for Import and Link weren’t visible or selectable.

 

I uninstalled the update and everything is back to normal. — Scott Diamond

So if you are experiencing any issue today, or shortly thereafter you may wish to uninstall any recent updates.

From what I could find on October 11, 2016, update for Microsoft Office there were 3 updates released for Office 2010:

Office 2010 MS16-121: Description of the security update for Office 2010: October 11, 2016(KB3118311)
Word 2010 MS16-121: Description of the security update for Word 2010: October 11, 2016(KB3118312)
Office 2010 MS16-120: Description of the security update for Office 2010: October 11, 2016(KB3118317)

Based on the general nature of the issue described by Scott, I would doubt the Word update caused issue with Excel and Access (although it has happened in the past that one application impacts another), so I would focus on determining if my system installed either KB3118311 or KB3118317 and see if the problem(s) go away after uninstalling them.

Access USysRibbons Bug

Software Bug

Last week I experienced some weird behavior while trying to split a database I was working on (which took me a little while to figure out since my db had table relinking code included in it) and then yesterday fellow MVP Jack Leach started a discussion regarding the same problem. So having been confirmed by a 2nd completely independent source, I am dubbing it a bug.

The Bug

Here’s how you can reproduce the issue easily.

  1. Create a new db and create a USysRibbons table to create a custom ribbon from. Set the db to use that ribbon.
  2. Split the db, but place the USysRibbons table in the back-end (yes, this isn’t a best practice, but regardless).
  3. Now, do one of 2 things:
    • Encrypt the back-end
    • Move the back-end file location

The next time you try to open your FE you will be presented with the default Access GUI to select the database you wish to open, as if you didn’t just ask it to open the FE. Even the Shift-Bypass will not work. As such, a relinking table function will not even fire.

MS Access USysRibbons bug

The issue here being that Access’ bug does not give the developer/user any means to rectify it (as stated above, the even the shift-bypass does not work to get the developer back into the database to address the root cause of the problem), does not report any error, …

The Workaround

The solution is to deleted the linked table in the FE prior to encryption or relocating the BE and then recreating the linked table thereafter.

So basically, once you discover the problem you can’t actually address it.  You have to undo the encryption or relocation, so your FE will work again, delete the linked USysRibbons table, then make your changes, then relink the table.

Note

  1. This problem only occurs with the USysRibbons table.  Other tables properly display the

    Could not find file ‘C:\…\…\…\databasename.accdb’

    when you try to access them after relocation or encryption.

  2. I experienced the problem in Access 2013, and I believe Jack was having the issue in Access 2016.

The Bigger Question

The bigger question that should be asked here is why is it that a decade into the ribbon is it soooo fragile and temperamental without error reporting?  If you have flawed XML why can it not report the line causing the error?  Why in the above case, does this cause a fatal application error and not allow the developer any means to correct the situation?  Why has the ribbon stood still for 10 years now?!