Great Access Tools – Access Crash Reporter

I’m starting a series of posts to promote some great tools to be aware of as an Access developer

Today, I’d like to introduce you to TheSmileyCoder’s Access Crash Reporter.

Any developer can attest that errors will occur and quite often users do not jot down the details of exactly what happened exactly thus making it very difficult to troubleshoot at times.  The Access Crash Reporter is a tool through which you can collect error data so you don’t have to solely rely on the users account of the problem.

This free tool gathers a tremendous amount of information:

  • Error Source
    • Object
    • Control
    • Parent Object (if applicable)
  • User
  • OS Information
  • Access Information
  • and so much more!

about errors when they occur and can, amongst other things:

  • Log errors to a tables
  • Generate E-mails to notify the contact of your choosing when error occur supplying them with all the details, a Screenshot of the application and of the object involved

TheSmileyCoder has a 7 minute video on his site, or view it directly on YouTube by using the following link: Crash Report Tool – Demo, Setup and Instructions which briefly demonstrates the tool and the resulting error collection, e-mail, …

If you don’t have an error handling system yet, consider taking this one for a test drive. If you are new to the subject of Error Handling, you may also like to review my article MS Access – VBA – Error Handling.

If you do have a error handling system, consider taking this one for a test drive.  It truly adds some nice functionalities and makes error reporting much more user-friendly.

Note
I have no affiliation with above mentioned website, developer, product, company … The above is an independent, unsolicited post based on my personal opinion.

Update 2024-05-04: Since TheSmileyCoder’s site no longer exists, I have updated the links to point to an archived version of his site.

Yii2 By Example by Fabrizio Calderelli

I recently used a trial subscription to Packt Publishing and decided to try a number of their books, one of which was Yii2 By Example by Fabrizio Calderelli.

The general concept of this book is excellent.  Fabrizio takes you from basic introduction (installing composer, starting new basic and advanced projects, installing widgets, …) and slowly builds upon each concept chapter to chapter while building a hotel style room reservation system.  You can tell the author clearly knows the subject very well.

Sadly, however, where the book falls apart, especially for any novice developer just trying to get their feet wet with Yii2, is the fact that there are a slew of

  • Mistakes and omissions
    • PHP syntax errors
      • Missing ; at the end of PHP commands
      • Extra parenthesis
      • Yii coding errors
        • Missing use statements in Yii code
        • Missing Namespace statements
        • Incorrect variable names so that they don’t get properly passed between the controller, model and views
    • SQL syntax errors
      • Extra commas
      • Missing clause (without which the commands won’t execute)
  • Images not reflecting what is written in the text
  • Etc.

throughout the book making actually completing the course material impossible.

There are sections where you are told to use a view, but it was never created!  Then in the next section they state “This is how the file … changes” and then you can sometimes backward engineer the file and finish the previous section.  Although I have managed to get around most problems to complete the book, sometimes spending hours to figure out that the author forgot to include a use statement, etc… I still have not managed to complete chapter 6.  Lucky for me, chapter 7 did not continue what was being done in Chapter 6, otherwise that would have been the end of the book for me.

What amazes me, and shows the lack of quality checking by PACKT and the author, is the simple fact that copying the code into a PHP editor instantaneously flags many syntax errors.  So, many of the issues would have easily been spotted just with a simple copy/paste.

All of the above clearly demonstrates that PACKT and the author never truly followed through the book’s content from start to finish themselves to ensure that it was bug free.

I do like the book on it’s whole, but because of the quantity of errors and the target audience, I can’t rate the book any higher than a 4 out of 10!  Had PACKT, or the author, actually done a minimal amount of Quality Assurance this book would be worthy of 9 or 10 without hesitation!!!

News/Info From the MS Access Dev team!

It’s very rare, actually this is the first time, that I am authorized to share some news of upcoming changes/upgrades to MS Access.  Usually, this stuff is all kept under wraps and I find out at the same time as the general population, that is to say when they roll out.

Large Number (BigInt) is rolling out

The Large Number data type stores a non-monetary, numeric value and is compatible with the SQL_BIGINT data type in ODBC. This data type efficiently calculates large numbers.

We expect customers will start seeing this new data type towards mid-March.

Update 2017-03-06

New in Access 2016—Large Number (BigInt) support

dBase file support to Access 2016 (MSI)

Listening to our customers, we will deliver dBASE file support to customers running single license installs of Access 2016.

This will be available with Windows update in the coming couple of months.

What’s New Panel

We are aligning Access with the rest of Office apps and integrating a new feature that is used is used to help introduce users to new features after the user downloads a monthly update to their app.

The expected flow for users is that they should see the What’s New Panel the first time they launch an app after they download an update. Users will also be able to access the What’s New Panel manually by going into File -> Account.

ACE 2016 Redist

In the next month or so we will release a new ACE 2016 Redist that will be available for download from Microsoft download center. It can be installed sxs with Office C2R, and will also include the ability to connect to dBASE.

Access 2016 Runtime C2R

We heard a lot from customers (especially Enterprises) that not having Access 2016 RT C2R is a major pain point for them (as they couldn’t install Access 2016 RT MSI sxs with their O365 subscription).

Next month we’ll make an Access 2016 RT C2R version available for O365 subscribers.

Help Articles

We have been getting great support from Jeff Conrad’s team on creating new Help content and updating existing content for new features.

To name just a few –

 

What’s New in Access 2016 article

As you all know, the team has been working hard on releasing new features, reinstating Access as an Office first class citizen, making it more accessible and more.

The ’What’s new in Access 2016’ article has been updated with all the new changes that we’ve introduced since September 2015.

In addition it has been dusted off, and given a new shiny coat with chrome accents.

Check it out here and feel free to share J

 

dBASE file support

A content gap that we’ve had a for a decade, has been filled by creating and publishing two new support articles for dBASE file support:

Note that when viewed within the Help Viewer, you only see the content relevant to your version. If you view the articles on the site, you’ll see info for all Access versions.

 

Large Number (BigInt) support

We are creating new support content (and updating relevant existing articles) to be published with Large Number when it’s rolling out.

VBA – Early Binding and Late Binding – Part 2

In my first post on the subject, VBA – Early Binding and Late Binding, I went over the basics, but I thought I’d push forward a little more to help demonstrate how to implement “The Best of Both World”.

As I explained, in my first article, the best of both worlds consists of developing using early binding, but deploying using late binding. The issue becomes needing to change the procedure declarations between each technique. Thankfully, Conditional Compilation Directives (CCDs) can solve this dilemma very easily.
 

What are Conditional Compilation Directives?

Conditional Compilation Directives, in this instance, is an approach in which we can get our VBA program to compile one sequence, or another, depending on a condition of our choosing.

In our case, this directly equate to compiling one set of declarations when we want Early Binding, and compiling another set of declarations when we want Late Binding.
 

How is This Done?

Actually, using Conditional Compilation Directives is surprisingly easy, once you are aware of it and it can be very useful far beyond the Early/Late Binding issue.

The Basic Concept of Conditional Compilation is:

    ' #Const EarlyBind = True 'Use Early Binding, Req. Reference Library
    #Const EarlyBind = False    'Use Late Binding
    #If EarlyBind = True Then
        'Early Binding Declarations
        Dim oOutlook          As Outlook.Application
        Dim oOutlookMsg       As Outlook.MailItem
    #Else
        'Late Binding Declaration/Constants
        Dim oOutlook          As Object
        Dim oOutlookMsg       As Object
        Const olMailItem = 0
    #End If

In the above we declare a Constant EarlyBind and depending on its value our program will choose to use one set of declaration or another. So when we’d be developing our program, we set our constant to

#Const EarlyBind = True

and include the necessary Reference Library, in the example above Microsoft Outlook XX.X Object Library to benefit from Intellisence during development and access to help, object browser, … .

On the other hand, when deploying our program to the client/end-users, we would remove the reference library and switch the constant to

#Const EarlyBind = False

to eliminate any versioning issues.

The Best of Both Worlds, you see!
 

Be Aware

Use of the Hashtag

Notice that in the example provide above both the IF and the Const are preceeded by a #. This is critical! This is what changes regular code into compiler directives and without it your code will not work as intended.

Declaration of the Compiler Constant

You may be tempted to simply include the #Const … directly in your procedure, but as a general rule you’d be much better served to declare it at a module level so it applies throughout your entire module rather than each procedure being independent from one another. This will make development easier as you will have but one value to alter instead of needing to do so for every procedure.

If you do go the route of declaring #Const in each procedure in the same module, you will need to ensure they all have unique names or else you will get compilation errors:

Compile error:
Duplicate definition

 

What Else Can Conditional Compilation Do For Me?

Beyond the aspect of Early and Late Binding for which Conditional Compilation Directives (CCD) can be very useful, CCDs are also extremely useful now that we are facing different bitnesses of our Office installations requiring different API declarations. As such, instead of trying to juggle different sets of APIs declarations, it can simply be easier to use CCDs to build a single declaration which will work in all scenarios, something like

    #If VBA7 And Win64 Then
        'x64 Declarations
    #Else
        'x32 Declarations
    #End If

A Concrete Example

An extremely common API is the apiGetUserName which we often use in the function fOSUserName to get the current user’s OS Username. The above function works great in x32 version of Office, but crashes in x64. So let’s put to use our new knowledge of CCDs to build a universal API call that will work in both scenarios.

#If VBA7 And Win64 Then
    'x64 Declarations
    Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
#Else
    'x32 Declaration
    Declare PtrSafe Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
#End If

Function fOSUserName() As String
    ' Returns the network login name
    Dim lngLen As Long, lngX  As Long
    Dim strUserName           As String
    strUserName = String$(254, 0)
    lngLen = 255
    lngX = GetUserName(strUserName, lngLen)
    If (lngX > 0) Then
        fOSUserName = Left$(strUserName, lngLen - 1)
    Else
        fOSUserName = vbNullString
    End If
End Function

You’ll also notice in the above example, we are not defining a #Const. VBA7, Win64 and others are defined by the system at runtime and this is what permits us to build such CCDs to make our code adaptive.

You can find a full list of all the available Compiler constants at:

 

A Global Compiler Constant

Sadly, we cannot use keywords like Public, Private, … with such constants. When we declare a constant it has a module level scope (even when created in a procedure).

#Const statement in code          Private to the module in which they are declaredMicrosoft

Thus, you must re-declare it in every module in which you need to use it. This is why I will try hard to group all my WMI procedures in one module, my WIA in another, … so I can have such a constant at the top that I switch and all my procedures switch.

If you truly want a project level (global) constant, there is one way to do so and that it to define the constant in the Project Properties dialog Conditional Compilation Arguments text box.

In the VBA Editor (VBE):

  1. From the main toolbars, click on Tools
  2. Click on Database Properties …
  3. On the General tab, enter the constant and its value in the Conditional Compilation Arguments text box. If you want to input multiple values simply separate them by a colon (:).

So, say we wanted to take my ‘#Const EarlyBind = True’ example and make it apply project wide, we would simply enter:

EarlyBind = -1

and if we wanted to enter a couple constants, we would enter:

WMI_EarlyBind = 0 : WIA_EarlyBind = -1
Important Difference!
Unlike the Module level syntax, the Conditional Compilation Arguments text box does not appear to support values of True/False. Instead, you must enter the numeric equivalent (as was shown in the examples above).

True => -1
False => 0

The rest of the code, the way we check and use constants in your procedures … remains unchanged.

Should you ever wish to automate these values in some way, note that you can read the current value of the Conditional Compilation Arguments by doing:

Debug.Print Application.GetOption("Conditional Compilation Arguments")

and you can update the value by doing:

Call Application.SetOption("Conditional Compilation Arguments", "Your New Value ...")

Overriding Project Level Constants

If you declare a Project level constant and also declare it in a procedure, the procedure level declaration will reign supreme. This can be very useful during development/testing.

I’d like to thank both AHeyne and John (see the comments section below) as a recent discussion on the matter helped enlighten me so I could expand this article to include this latest section.
 

Useful References

Generate an HTML Table String from a Query’s Recordset

HTML Document Icon

I was trying to help out in an MSDN thread in which the user was trying to include an HTML table from their query.

Below was my solution to export a query, or table, as a HTML Table string which then can be used in e-mails, to generate standalone HTML files, …

Continue reading

MS Access VBA – Set Form’s GridX and GridY Properties

One thing I find when taking over projects from various sources, especially when the database has already been through the hands of several developers is that many default properties can be all different depending on the object types and who create/worked on them. As such, one can spend a lot of time trying to open each form and clean things up. Things like:

  • Date Picker
  • Record Locking
  • Background Colors
  • Alternate Background Colors (for continuous forms)
  • GridX and GridY

As such, I created a very straightforward function that you can run to apply a standard GridX and GridY value to all your forms in once shot.

'---------------------------------------------------------------------------------------
' Procedure : SetFrmsGridProp
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Loops through all the database forms to set the GridX and GridY properties
'               great way to quickly set a common value to all your forms
' 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: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' iGridX    : New GridX value to set
' iGridY    : New GridY value to set
'
' Usage:
' ~~~~~~
' Call SetFrmsGridProp(20, 20)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2015                    Initial Release
' 2         2018-09-20              Updated Copyright
'---------------------------------------------------------------------------------------
Public Sub SetFrmsGridProp(iGridX As Integer, iGridY As Integer)
    On Error GoTo Error_Handler
    Dim db                    As Object
    Dim obj                   As AccessObject
    Dim frm                   As Access.Form
    Dim i                     As Long
    
    Set db = Application.CurrentProject
    For Each obj In db.AllForms
        i = i + 1
        DoCmd.OpenForm obj.Name, acDesign
        Set frm = Forms(obj.Name).Form
        frm.GridX = iGridX
        frm.GridY = iGridY
        DoCmd.Close acForm, obj.Name, acSaveYes
    Next obj

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

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: SetFrmsGridProp" & 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 Sub

Obviously, this could be adapted to be applied to Reports as well with one minor tweak of the code.

VBA – Generate a Random Number Between Two Numbers

While reworking an old demo of mine, I came across a function I had created to generate a Random Number between t2o supplied numbers and thought it could help other, so here it is.

'---------------------------------------------------------------------------------------
' Procedure : GetRndNo
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Generate a random number between 2 inputted values
' 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: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' lLowerVal : smallest number of the interval to get a random number from
' lUpperVal : greatest number of the interval to get a random number from
' bInclVals : Should the smallest and greatest numbers be part of the potential returned
'               values
'
' Usage:
' ~~~~~~
' lRndNo = GetRndNo(-9, 4)
'           Will return a random number between -9 and 4
' lRndNo = GetRndNo(4, -9)
'           Will return a random number between -9 and 4
' lRndNo = GetRndNo(4, -9, False)
'           Will return a random number between -8 and 3
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2012-02-07              Initial Release
' 2         2018-09-20              Updated Copyright
'---------------------------------------------------------------------------------------
Function GetRndNo(ByVal lLowerVal As Long, ByVal lUpperVal As Long, _
                  Optional bInclVals As Boolean = True) As Long
    On Error GoTo Error_Handler
    Dim lTmp                  As Long

    'Swap the lLowerVal and lUpperVal values, if they were inversed in the originating
    '   function call
    If lLowerVal > lUpperVal Then
        lTmp = lLowerVal
        lLowerVal = lUpperVal
        lUpperVal = lTmp
    End If

    'Adjust the boundary values should the user specify to exclude them from the 
    '   possible returned values
    If bInclVals = False Then
        lLowerVal = lLowerVal + 1
        lUpperVal = lUpperVal - 1
    End If

    'Calculate our random number!
    Randomize
    GetRndNo = Int((lUpperVal - lLowerVal + 1) * Rnd + lLowerVal)

Error_Handler_Exit:
    On Error Resume Next
    Exit Function

Error_Handler:
    'LogError Err.Number, Err.Description, sModName & "\GetRndNo", , True, Erl
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: GetRndNo" & 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

VBA – Generate a Random Date Between Two Dates

Reworking an old demo database, I came across some code to generate a random date between two supplied dates and thought it could be of use to other.

 

'---------------------------------------------------------------------------------------
' Procedure : GetRndDate
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Get a random date between 2 dates
' 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: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' dtStartDate : Minimum Date value
' dtEndDate   : Maximum Date value
'
' Usage:
' ~~~~~~
' dtRnd = GetRndDate(#12/01/2002#, #01/05/2015#)
'           Will return a random date between #12/01/2002# and #01/05/2015#
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2012-02-07              Initial Release
' 2         2017-02-09              Added check that Start is < to End
'                                   Added Comments to code
' 3         2018-09-20              Updated Copyright
'---------------------------------------------------------------------------------------
Function GetRndDate(dtStartDate As Date, dtEndDate As Date) As Date
    On Error GoTo Error_Handler
    Dim dtTmp                 As Date

    'Swap the dates if dtStartDate is after dtEndDate
    If dtStartDate > dtEndDate Then
        dtTmp = dtStartDate
        dtStartDate = dtEndDate
        dtEndDate = dtTmp
    End If
    
    Randomize
    GetRndDate = DateAdd("d", Int((DateDiff("d", dtStartDate, dtEndDate) + 1) * Rnd), dtStartDate)

Error_Handler_Exit:
    On Error Resume Next
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: GetRndDate" & 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

Internet Explorer Save Tabs

This little demo demonstrates how you can iterate through all your open Internet Explorer windows/tabs and save the information to an Access table.  I find this to be much more powerful than bookmarks, etc.

In Access this information can be grouped, searched, sorted, … Simply put, used much more easily.

This can be a great tools to share useful links between team members. You can build a treasure trove of useful links by topic and then share them with everyone in this manner.

The code, is not necessarily Access centric, beyond the saving to a table, which is a single INSERT into query.  Beyond that, the functionality could easily be integrated into any other VBA programs (Excel, PowerPoint, Word, …).

Although the code was developed on Access 32-bit, it uses no APIs, etc. so it can be ported to 64-bit without any issue.

Continue reading

VBA – Manipulate Images, Resize, Convert and More …

Have you ever need to modify images through your database, or any other Office program (Excel, Word, …) for that matter?

In my last post VBA – Resize Image I demonstrated how you can do basic manipulations using the WIA object, but what about performing more advanced jobs? What about converting images between formats, … ? What about some of the headaches that occur with WIA.

Well, I set out to find a reliable solution that would work across the board and enable a vast array of manipulations and FreeImage was one such answer.

Why FreeImage you may ask, well, there are 3 main reasons (+1 personal reason):

  • It is free
  • It is relatively easy to implement and get up and running
  • Provides lots of functionalities
  • And I simply wanted to explore this option to see what it might offer to the community at large

Continue reading