Quantcast
Channel: MS Access General Information | DEVelopers HUT
Viewing all 89 articles
Browse latest View live

Great Access Tools – Find and Replace

$
0
0
Ever wanted to change a field name but have no clue what objects use it? Historically developers turned towards tools like: Speed Ferret (last updated for Access 2002 and I don’t think the company is issuing licenses anymore) Rick Fisher’s Find and Replace (he no longer seems to respond to purchases and does not issue…

Continue reading


Export\Salvage Your AWA Data

$
0
0
With Microsoft’s recent announcement of them pulling the plug on Acess Web Apps (AWAs), see: http://www.devhut.net/2017/03/27/steer-clear-of-access-web-applications-awa/ the question has become about how one can salvage the data from an AWA.  What means can we employ to export the tables so we don’t loose all our data. Thankfully, George Hepworth has put together a very valuable…

Continue reading

Setting Up an MS Access Database

$
0
0
One of the most important question we see in the forums is regarding setting up a production database. So, today, I thought I’d take a stab at properly covering the subject from A to Z, and everything in between. Splitting the Database Right off the bat, Microsoft starts developers off on the wrong foot by…

Continue reading

Access x32 vs x64 Compatibility

$
0
0
A more and more common question in Access, and Office forums in general, relates to using Access x32 (32 bit) vs using Access x64 (64 bit).  How they play together … etc. Now some people erroneously believe that since their machine is running a 64 bit version of Windows that Office is automatically 64 bit…

Continue reading

Access Best Practices and Troubleshooting Steps

$
0
0

I thought I’d make a quick post to go over some basic Access development best practices and Troubleshooting steps that any developer should be aware of.

MS Access Development Best Practices

So you want to start developing an MS Access database!  The following is based on my personal experience developing databases for over 15 years now.  That said, a few (2 or 3) of the items below are debatable, but most are steadfast rules.

General

  • Implement everything I covered in my article entitled Setting Up an MS Access Database
    • Split your database (I’d suggest you do this early on in development)
    • Add a Persistent Connection between your front-end and back-end at the startup of your application
    • Always develop using the oldest version of Access that will be used to run your database
    • etc…
  • Make sure to always develop on a local copy of the database. Never develop on a network copy
  • Save regularly
  • Backup, backup … did I mention backup!
  • Deploy an ACCDE version of your database to your end-users rather than the source ACCDB
  • I recommend setting up a table(s) in both the FE and BE to keep track of your database version history and the changes that occurred in each version

Access Settings and Properties

  • You should not use the Compact on Close property
  • Always disable the Track name AutoCorrect info property

Tables

  • Set all your tables’ Subdatasheet Name to [None]
  • Never use Lookups
  • Never use the Attachments data type
  • Never use the Hyperlink data type
  • Never use special characters when naming fields (this includes avoiding the use of spaces).  You are best to use CamelCase or another well known technique
  • Never use any Reserved Words when naming fields
  • Do not use Input Masks
  • Take the time to properly index your table fields
  • Ensure to set the Required property for your table fields where required
  • Take the time to setup Relationships between your tables

Forms/Reports

  • Never use special characters when naming forms/reports and their controls (this includes avoiding the use of spaces).  You are best to use CamelCase or another well known technique
  • Never use any Reserved Words when naming forms/reports and controls
  • Give your controls meaningful names, do not use the default names that Access creates (ie: command1, text1, …)!
  • Do not use Input Masks
  • Avoid the use of any and all ActiveX Controls
  • If you’re looking to embed images within, please take the time to resize the image files prior to using them.  There is no point using a full resolution image 3442x2559px if you’re just going to use it as a 32x32px icon on a button!  This leads to database bloat and impacts database/object load times …
  • When reusing images within multiple objects, say a logo, remember to either set the Picture Type to Shared, or my preferred method, is to create a form/report and insert the image and then insert the form/report as a subform/subreport wherever the image is required.  This also mean I only have one object to edit to update all my dependent objects and this technique is backwards compatible with mdb format database unlike the Picture Type approach which is only available in accdb database! (and yes people still use mdb databases!)

VBA

  • Always include the Option Explicit declaration at the top of all of your VBA modules
  • Compile regularly and save
  • Do not make VBA changes to forms/Reports unless in Design mode
  • Never use any Reserved Words when naming variables

MS Access Troubleshooting Steps

Below are some basic troubleshooting steps that you can try when attempting figure out issues with a database.

  • Perform a Compact and Repair
  • Restart Access
  • Shutdown and restart your PC, not hibernate or sleep, but rather a proper shutdown.
  • If you use VBA code
    • Ensure there are no MISSING references (Tools -> References…)
    • Decompile, Compact and Repair, Compile, Compact and Repair
  • Create a new blank db and then import everything into it
  • Update Access/Office to ensure you have all the latest fixes/patched/…
    • Do not rely on Windows Update as it does not do this!
  • Perform an Office Repair through the Windows Control Panel
  • (This is only for extreme cases) Perform a complete uninstall of office using Option 2 from Uninstall Office 2016, Office 2013, or Office 365 from a PC and then reinstall it.
    • Backup everything first

dBASE Support Restored to Access 2016 MSI

Access Database File Compatibility Table

$
0
0

Access has always maintained an exceptional backwards compatibility between version, but as we move forward and new versions are released some of the older versions are being left behind.  Now I’m not going defend why this is happening, but I did want to try and create a simple list to easily know what version is compatible with which version.  This is what I’ve put together thus far.

Version Compatible Up To
Access 2.0 Access 2003
Access 95 Access 2007
Access 97 Access 2010
Access 2000 Access 2016
Access XP (2002) Access 2016
Access 2003 Access 2016

So for example, if you had an Access 2.0 mdb and wanted to upgrade it to be able to run it on Access 2016 you’d need to use Access 2003 to upgrade the file format to Access 2002-2003 mdb format and then you could use Access 2016 to open the mdb and/or convert it.

Some Useful References:

Access 2.0 -> Import an Access 2.0 database into an Access 2007 file
Access 95 -> Import an Access 95 database into an Access 2007 file
Access 97 -> Convert a database to the .accdb file format
Access 2000, 2002 & 2003 -> Convert a database to the .accdb file format

MS Access – Find Macros Using a Search Term

$
0
0

To continue my original post, entitled MS Access – Find Embedded Macros, regarding identifying where Embedded Macro were being used within an Access database, I also develop the following procedure that enable one to search through all Embedded Macros and Standard Macros for a search term. The search term can be anything, object names, commands, segments of words, …

I originally was using this to identify where Forms were being called since I needed to rename them.

'---------------------------------------------------------------------------------------
' Procedure : FindTermInMacros
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Search through Form and Report Embedded Macros and standard Macros for
'             a given term
'             The search results are printed to the immediate window
' 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: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sSearchTerm   The term to look form
'
' Usage:
' ~~~~~~
' Call FindTermInMacros("Form1")
' Call FindTermInMacros("SetTempVar")
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2017-01-07              Initial Release
' 2         2017-05-22              Added search of standard Macros
'---------------------------------------------------------------------------------------
Public Function FindTermInMacros(sSearchTerm As String)
    On Error GoTo Error_Handler
    Dim oFrm                  As Object
    Dim frm                   As Access.Form
    Dim oRpt                  As Object
    Dim rpt                   As Access.Report
    Dim ctl                   As Access.Control
    Dim oMcr                  As Object
    Dim prp                   As DAO.Property
    Dim sFile                 As String
    Dim sMcr                  As String
    Dim intChannel            As Integer
    Dim sLine                 As String

    Access.Application.Echo False
    Debug.Print "Search Results for the Term '" & sSearchTerm & "'"
    Debug.Print "Object Type", "Object Name", "Control Name", "Event Name"
    Debug.Print String(80, "-")

    'Search Forms
    For Each oFrm In Application.CurrentProject.AllForms
        DoCmd.OpenForm oFrm.Name, acDesign
        Set frm = Forms(oFrm.Name).Form
        With frm
            'Form Properties
            For Each prp In .Properties
                If InStr(prp.Name, "EmMacro") > 0 Then
                    If Len(prp.value) > 0 Then
                        'Search for the Search Term we are looking for
                        If InStr(prp.value, sSearchTerm) > 0 Then
                            Debug.Print "Form:", frm.Name, , Replace(prp.Name, "EmMacro", "")  ', prp.Value
                        End If
                    End If
                End If
            Next prp
            'Form Control Properties
            For Each ctl In frm.Controls
                For Each prp In ctl.Properties
                    If InStr(prp.Name, "EMMacro") > 0 Then
                        If Len(prp.value) > 0 Then
                            If InStr(prp.value, sSearchTerm) > 0 Then
                                Debug.Print "Form", frm.Name, ctl.Name, Replace(prp.Name, "EmMacro", "")
                            End If
                        End If
                    End If
                Next prp
            Next ctl
        End With
        DoCmd.Close acForm, oFrm.Name, acSaveNo
    Next oFrm

    'Search Reports
    For Each oRpt In Application.CurrentProject.AllReports
        DoCmd.OpenReport oRpt.Name, acDesign
        Set rpt = Reports(oRpt.Name).Report
        With rpt
            'Report Properties
            For Each prp In .Properties
                If InStr(prp.Name, "EmMacro") > 0 Then
                    If Len(prp.value) > 0 Then
                        'Search for the Search Term we are looking for
                        If InStr(prp.value, sSearchTerm) > 0 Then
                            Debug.Print "Report:", rpt.Name, , Replace(prp.Name, "EmMacro", "")  ', prp.Value
                        End If
                    End If
                End If
            Next prp
            'Report Control Properties
            For Each ctl In rpt.Controls
                For Each prp In ctl.Properties
                    If InStr(prp.Name, "EMMacro") > 0 Then
                        If Len(prp.value) > 0 Then
                            If InStr(prp.value, sSearchTerm) > 0 Then
                                Debug.Print "Form", frm.Name, ctl.Name, Replace(prp.Name, "EmMacro", "")
                            End If
                        End If
                    End If
                Next prp
            Next ctl
        End With
        DoCmd.Close acReport, oRpt.Name, acSaveNo
    Next oRpt

    'Search Standard Macros
    'There appears to be no way to simply read/access a macro's commands through VBA, so
    'we have to export the object to a text file and then read and search the resulting
    'file.  It's just the way it is, thank MS for not giving us any mean to interact with
    'macros!
    For Each oMcr In Application.CurrentProject.AllMacros
        sFile = Access.Application.CurrentProject.path & "\Macro_" & oMcr.Name & ".txt"
        'Export the Macro to a Text file so we can review it
        Access.Application.SaveAsText acMacro, oMcr.Name, sFile
        'Read the text file
        sMcr = ""
        intChannel = FreeFile
        Open sFile For Input Access Read As #intChannel
        Do Until EOF(intChannel)
            Line Input #intChannel, sLine
            If Trim(sLine) Like "Comment =""_AXL: 0 Then
            Debug.Print "Macro:", oMcr.Name
        End If
    Next oMcr

    Debug.Print String(80, "-")
    Debug.Print "Search Completed"

Error_Handler_Exit:
    On Error Resume Next
    Access.Application.Echo True
    If Not oMcr Is Nothing Then Set oMcr = Nothing
    If Not prp Is Nothing Then Set prp = Nothing
    If Not ctl Is Nothing Then Set ctl = Nothing
    If Not rpt Is Nothing Then Set rpt = Nothing
    If Not oRpt Is Nothing Then Set oRpt = Nothing
    If Not frm Is Nothing Then Set frm = Nothing
    If Not oFrm Is Nothing Then Set oFrm = Nothing
    Exit Function

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

MS Access Reserved Word Checker

$
0
0

If you aren’t already aware of it, there are a set of words which should never be used to name

  • Database objects (Tables, Queries, Forms, …)
  • Table/Query Fields
  • Form/Report Controls
  • VBA procedure names, variable, …

these are referred to as Reserved Words and thus Reserved Words need to be avoided at all cost as they can generate strange behaviors or outright failure of your database.

The issue with Reserved Words is that there is simply no way to remember them all.  Furthermore, when taking over another developer’s work, it is next to impossible to review every object, control, … for such terms.

As such, I decided to try and automate the verification process to speed up and guarantee the results.  Hence was born the MS Access Reserved Word Checker.  A very simple tool that

searches through:

  • Tables and their Fields
  • Queries and their Fields
  • Forms and their Controls
  • Reports and their Controls
  • Macros

looking for the use of:

  • Reserved Words
  • Special Characters

in their names.

The results are then tabulated in a form for review


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):

MS Access 2007 Runtime
MS Access 2010 Runtime
Ms Access 2013 Runtime
Ms Access 2016 Runtime

Although we have taken steps to ensure that the software on this site is free of viruses, we provide the software 'AS IS' and we recommend you take the time to scan it again.

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 a Demo Database

Feel free to download a demo copy by using the link provided below:

Download “Access Reserved Word Checker (Access 2013 x32 accdb)” ReservedWordChecker.zip – Downloaded 25 times – 99 KB

Version History

V1.001 (2017-06-22)

  • Added verification of Queries and Query Field Names

V1.000 (2017-06-21)

  • Initial release

MS Access Security Demo Database

$
0
0

Security and Access, where to start?  What to do?

Security can be simple and complicated, it all depends on your needs and abilities.  It can be superficial, as it can be very granular.  As such, I thought I’d present a couple options to help people get going in the right direction.

What does this demo illustrate?

In the demo, I demonstrate 3 different techniques:

The Static Password Approach

  • How to password protect a single command button using a hard-code VBA password

The Dynamic Automatic Authentication

This use a set of security tables that you setup once and then the database can automatically authenticate and apply security as required.  Although this example demonstrates restricting access to a command button, it can be used to control access to forms, reports, …

This is by far the preferred approach and although requires a little more initial setup time, in the long run it will drastically simplify your work.

  • How to restrict executing a command button’s code
  • How to restrict access (visibility) of a command button

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):

MS Access 2007 Runtime
MS Access 2010 Runtime
Ms Access 2013 Runtime
Ms Access 2016 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 a Demo Database

Feel free to download a demo copy by using the link provided below:

Download “MS Access Security_Demo Database (accdb x32)” Security_Demo.zip – Downloaded 72 times – 56 KB

Version History

V1.000 (2017-07-10)

Initial release

Access Calculated Control Blank/Empty/Null

$
0
0

Now the following is for a very niche market, but I thought I’d share none the less.

Access 2010 (although I seen reports in other versions such as Access 2013) had a major issue in which calculated control would not display their content. Yet, if you clicked on the control, it would then display the proper value. Now Microsoft eventually remedied the issue by issuing an update (I believe https://support.microsoft.com/en-us/help/2827138/access-encounters-multiple-issues-if-the-pc-has-been-running-more-than). So there is a real solution available, which is to update your installation.

However, I had a client, whose IT Dept. would not, will not, install the updates and I left me with no choice but to come up with a workaround of my own. As such, I created a routine that simply goes through a form and sets the focus on each control, one by one. It’s not ideal, but it works.

'---------------------------------------------------------------------------------------
' Procedure : Frm_TouchMe
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Goes through a form and sets the focus on each control, one by one
' 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: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' frm       : Form object to go through
'
' Usage:
' ~~~~~~
' Call Frm_TouchMe(Me)
' Call Frm_TouchMe(Me.frm_Orders.Form)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2017-08-18              Initial Release
'---------------------------------------------------------------------------------------
Public Sub Frm_TouchMe(frm As Access.Form)
10        On Error GoTo Error_Handler
          Dim ctl                   As Access.Control

20        With frm.Recordset
30            If .RecordCount <> 0 Then
40                .MoveFirst
50                Do While Not .EOF
60                    For Each ctl In frm.Controls
70                        If ctl.ControlType = acCheckBox Or ctl.ControlType = acComboBox Or ctl.ControlType = acTextBox Then
80                            If ctl.Enabled = True And ctl.Visible = True Then
90                                If frm.DefaultView = 2 Then    'Datasheet
100                                   If ctl.ColumnHidden = False Then
110                                       ctl.SetFocus
120                                   End If
130                               Else
140                                   ctl.SetFocus
150                               End If
160                           End If
170                       End If
180                       DoEvents 'This is necessary otherwise the results were not always consistent
190                   Next ctl
200                   .MoveNext
210               Loop
220               .MoveFirst
230           End If
240       End With

Error_Handler_Exit:
250       On Error Resume Next
260       If Not ctl Is Nothing Then Set ctl = Nothing
270       If Not frm Is Nothing Then Set frm = Nothing
280       Exit Sub

Error_Handler:
290       MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
                 "Error Number: " & Err.Number & vbCrLf & _
                 "Error Source: Frm_TouchMe" & vbCrLf & _
                 "Error Description: " & Err.Description & _
                 Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
                 , vbOKOnly + vbCritical, "An Error has Occured!"
300       Resume Error_Handler_Exit
End Sub

In my case, the forms that were impacted by this issue were subforms, so I created a Load event in the parent forms and simply called the function by doing something like:

Call Frm_TouchMe(Me.frm_Orders.Form)

Et voilà! The problem was no more.

One Word of Caution
Because this effectively loops through all the records and controls, one by one and executes DoEvents each time, this can impact performance and would not be recommended for forms with huge amounts of records (big datasheets for instances).

The proper resolution remains to update Office!

Special Characters and Internationalization of an Application

$
0
0

The Problem

If ever you get into developing applications, especially Access databases, that are deployed on various countries and/or where users change the default Regional settings you will quickly ask yourself why, oh why, did Microsoft make things so complex.

For example creating a value list through VBA, some languages use a comma (,), others the semicolon (;) and so on, and so on it goes for decimal separators, date separators, …

So hard coding the separator is a no-no unless you are in a very controlled environment!

Let’s look at a simple example, such as getting a listing of all the tables within a database to be used as a Value list of a combo box. Normally, our code would look something like:

'---------------------------------------------------------------------------------------
' Procedure : listTables
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Generate a separated listing of all the database tables to be used
'               as a value list for instance
' 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: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' bShowSys  : True/False -> Include system tables or not
'
' Usage:
' ~~~~~~
' sTbls = listTables(True)
' sTbls = listTables(False)
' Me.cboName.RowSource = listTables(True)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2010-06-10              Initial Release
'---------------------------------------------------------------------------------------
Function listTables(bShowSys As Boolean) As String
    On Error GoTo Error_Handler
    Dim db                    As DAO.Database
    Dim td                    As DAO.TableDefs

    Set db = CurrentDb()
    Set td = db.TableDefs
    For Each t In td    'loop through all the fields of the tables
        If Left(t.Name, 4) = "MSys" And bShowSys = False Then GoTo NextTable
        listTables = listTables & t.Name & ","
NextTable:
    Next

    'Truncate the trailing comma on the last entry
    If Len(listTables) > 0 Then listTables = Left(listTables, Len(listTables) - 1)

Error_Handler_Exit:
    On Error Resume Next
    If Not td Is Nothing Then Set td = Nothing
    If Not db Is Nothing Then Set db = Nothing
    Exit Function

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

The issue with the above is the comma in the following line of code because that may not be the list separator defined in the user’s computer settings.

listTables = listTables & t.Name & ","

Don’t get me wrong, the VBA code will run just fine. It is when you go to assign the string to a RowSource (…) that you will face problems.

So what are we to do? What’s the Solution

In Excel, they have a very simple command to determine the values of such separators

Application.International(xlListSeparator)

but there is nothing like that in Access! (banging my head against the wall. Microsoft’s standardization between applications is mind blowing!!!!)

Well, the fact is there are a couple approaches we could take: API, Registry, …

API

Declare Function GetLocaleInfo Lib "kernel32" Alias _
                               "GetLocaleInfoA" (ByVal Locale As Long, ByVal LCType As Long, _
                                                 ByVal lpLCData As String, ByVal cchData As Long) As Long
Declare Function GetUserDefaultLCID% Lib "kernel32" ()
Public Const LOCALE_SLIST = &HC

Public Function GetListSeparator() As String
    On Error GoTo Error_Handler
    Dim ListSeparator         As String
    Dim iRetVal1              As Long
    Dim iRetVal2              As Long
    Dim lpLCDataVar           As String
    Dim Position              As Integer
    Dim Locale                As Long

    Locale = GetUserDefaultLCID()
    iRetVal1 = GetLocaleInfo(Locale, LOCALE_SLIST, lpLCDataVar, 0)
    ListSeparator = String$(iRetVal1, 0)
    iRetVal2 = GetLocaleInfo(Locale, LOCALE_SLIST, ListSeparator, iRetVal1)

    Position = InStr(ListSeparator, Chr$(0))
    If Position > 0 Then
        ListSeparator = Left$(ListSeparator, Position - 1)
        GetListSeparator = ListSeparator
    End If

Error_Handler_Exit:
    On Error Resume Next
    Exit Function

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

Registry

All of these ‘mysterious’ settings are actually neatly stored in the registry and are easily accessible in the HKEY_CURRENT_USER\Control Panel\International key.

And with the following single line of code we can easily read any one of them by simply changing the key to read

CreateObject("WScript.Shell").RegRead("HKEY_CURRENT_USER\Control Panel\International\sList")

As always though, we should turn into a properly reusable function like

'---------------------------------------------------------------------------------------
' Procedure : GetInternationalSetting
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Retrieve the system's regional setting values for internaltional chrs
' 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: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sKey      : The registry key to read (sList, sDecimal, sDate, sThousand, ...)
'
' Usage:
' ~~~~~~
' GetInternationalSetting("sDecimal")
' GetInternationalSetting("sList")
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2017-09-01              Initial Release
'---------------------------------------------------------------------------------------
Function GetInternationalSetting(ByVal sKey As String) As String
    On Error GoTo Error_Handler

    GetInternationalSetting = CreateObject("WScript.Shell").RegRead("HKEY_CURRENT_USER\Control Panel\International\" & sKey)

Error_Handler_Exit:
    On Error Resume Next
    Exit Function

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

With this new information in hand, we can then adapt our original table function so it become flexible for all PC’s and regions of the world by doing

Using the API

'---------------------------------------------------------------------------------------
' Procedure : listTables
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Generate a separated listing of all the database tables to be used
'               as a value list for instance
' 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: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' bShowSys  : True/False -> Include system tables or not
'
' Usage:
' ~~~~~~
' sTbls = listTables(True)
' sTbls = listTables(False)
' Me.cboName.RowSource = listTables(True)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2010-06-10              Initial Release
' 2         2017-09-01              Internationalized version
'---------------------------------------------------------------------------------------
Function listTables(bShowSys As Boolean) As String
    On Error GoTo Error_Handler
    Dim db                    As DAO.Database
    Dim td                    As DAO.TableDefs
    Dim sDelim                As String

    sDelim = GetListSeparator

    Set db = CurrentDb()
    Set td = db.TableDefs
    For Each t In td    'loop through all the fields of the tables
        If Left(t.Name, 4) = "MSys" And bShowSys = False Then GoTo NextTable
        listTables = listTables & t.Name & sDelim
NextTable:
    Next

    'Truncate the trailing comma on the last entry
    If Len(listTables) > 0 Then listTables = Left(listTables, Len(listTables) - Len(sDelim))

Error_Handler_Exit:
    On Error Resume Next
    If Not td Is Nothing Then Set td = Nothing
    If Not db Is Nothing Then Set db = Nothing
    Exit Function

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

Using the function

'---------------------------------------------------------------------------------------
' Procedure : listTables
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Generate a separated listing of all the database tables to be used
'               as a value list for instance
' 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: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' bShowSys  : True/False -> Include system tables or not
'
' Usage:
' ~~~~~~
' sTbls = listTables(True)
' sTbls = listTables(False)
' Me.cboName.RowSource = listTables(True)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2010-06-10              Initial Release
' 2         2017-09-01              Internationalized version
'---------------------------------------------------------------------------------------
Function listTables(bShowSys As Boolean) As String
    On Error GoTo Error_Handler
    Dim db                    As DAO.Database
    Dim td                    As DAO.TableDefs
    Dim sDelim                As String

    sDelim = GetInternationalSetting("sList")

    Set db = CurrentDb()
    Set td = db.TableDefs
    For Each t In td    'loop through all the fields of the tables
        If Left(t.Name, 4) = "MSys" And bShowSys = False Then GoTo NextTable
        listTables = listTables & t.Name & sDelim
NextTable:
    Next

    'Truncate the trailing comma on the last entry
    If Len(listTables) > 0 Then listTables = Left(listTables, Len(listTables) - Len(sDelim))

Error_Handler_Exit:
    On Error Resume Next
    If Not td Is Nothing Then Set td = Nothing
    If Not db Is Nothing Then Set db = Nothing
    Exit Function

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

Of course, then we’d need to perform the same operation through our code that builds lists, equations, … everywhere regional setting characters are used! This is when defining these regional settings as TempVars at the startup of your database makes a lot of sense and modifying function to accept the sDelim as an input variable can greatly simplify your coding life.

Another Approach

Instead of embedding the internationalization code within each function, another approach would be to leave your tried and tested functions alone, and simply use the Replace function when calling them. So you’d take something like

Me.cboName.RowSource = Replace(listTables(True), ",", GetInternationalSetting("sList"))

This way you don’t need to change any of your existing procedures. On the other hand, you need to always remember to add the Replace function to every call. There can also be issues with this approach should the string you are performing a replace on include any comma beyond those used as a list separator.

The choice is yours depending on your needs.

What’s also great with the above API or Function is that it will work in every VBA application! No more need to know in Excel you use this command, … now with one solution you can rule them all!

Useful Links

Retrieve Locale Info
Handle currency, date, and time formats for other locales
Book Internationalization with Visual Basic

Naming Conventions

$
0
0

What is a Naming Convention?

A Naming Convention is simply a standard rule (could be through the use of prefixes, suffixes, typographical convention (syntax styling) such as CamelCase, …) that you apply when creating names for :

  • Objects
    • Tables
    • Queries
    • Forms
    • Reports
    • Macros
    • Modules (See VBA Components)
  • Table Fields
  • Controls (Forms & Reports)
  • VBA Components (Modules, Class Modules) & Variables…
Not Just An Access Thing
This is true in any programming language and not just an Access, MS Office or VBA thing.

Why Use a Naming Convention?

Simple, to standardize your work and make it easier to follow.

For instance, take a simple example of

Public Sub Demo()
    For cars = 0 To 10
        Accident = DateAdd("d", cars, Date)
        Debug.Print Accident
    Next cars
End Sub

what is Accident and cars?  Strings, Integer, Long, …?

Now, if you implement a basic naming convention and prefix all your VBA date variables with dt for Dates and i for Interger numbers you get

Public Sub Demo()
    For icars = 0 To 10
        dtAccident = DateAdd("d", icars, Date)
        Debug.Print dtAccident
    Next icars
End Sub

and the code becomes much more legible.  You don’t even need to look at the procedure’s variable declarations to understand that dtAccident is a Date and icars is a Integer number.

So by using naming conventions you

  • Standardize your work
  • Simplify reading your code (imagine long procedures where you need to scroll up and down to see variable declarations, by using a naming convention, you no longer need to look at the declaration to know exactly what type of variable you are working with)
  • Simplify troubleshooting
  • Aid in finding things when they are listed together (imaging a list of database Objects with object prefixes, it becomes very easy to distinguish forms from reports and tables form queries, …)
Do Not Forget
Some of what you do today is not so much for the immediate benefit, but rather to facilitate your life when you need to revisit your code in a month’s time, in a year’s time, … You may understand today that cars is an integer, but I can guarantee you won’t in a year from now. By using a Notation, it won’t make any difference, you’ll be able to peruse your code and instantaneously start working.

What are Some of the Existing Naming Conventions?

If you Google the subject of Naming Conventions you will be inundated with all sorts of conventions.  In Access, or VBA in general, you will commonly hear of the following ones:

You’ll notice that Hungarian, RVBA and Leszynski are all very similar as they are derivative of the Hungarian.

Let’s Not Get Hung Up on Specifics!

Now, I know some people will be sticklers for one notation or another, but the truth of the matter is that it truly doesn’t make any difference whatsoever!  The name of the game here is consistency!

What is important is that you have some standard and you stick with it throughout any given project.

  • You can use your own notation.  There’s nothing wrong with that.
  • You can change notations from project to project (just don’t change notations within a single project).  Once you start with one, stick with it, or if you decide to change notations, update it throughout your project.

More resources on the subject

MS Access – Automatic Backup of an Access Database

$
0
0

Another common question in Access forums is

I would like to know if there is a way I can automatically backup my database

There are various approaches that can be implemented depending on your needs and/or setup.

IT Department

The easiest solution in a corporate environment is to simply ask the IT department to include your database’s folder as part of their automated backups.

PROs

  • One less thing for you to setup and administer
  • Is done using enterprise level backup software

CONs

  • Makes you reliant on the IT Department.
    • If you have a responsive IT department this isn’t an issue, but if your IT department is unresponsive, this can be very problematic when you need to test the backup system, or god forbid, when you actually need to perform a recovery.

 

Windows Scheduled Task

You yourself could create a scheduled task in Windows to run a VBScript that would perform a backup.

PROs

  • You don’t need to rely on another department (You’re in control)
  • You don’t need to get any permissions/approvals …

CONs

  • One more thing to setup and administer (more work for you)
VBScript Sample
You may like to review MS Access – Backup a Database Using a VBScript for a great VBScript to backup an Access database.

Database Startup

You could setup your database, at startup, to check if a backup was done, and if not do so before continuing to load up.

PROs

  • You don’t need to rely on another department (You’re in control)
  • You don’t need to get any permissions/approvals …
  • You don’t need to even learn about setting up schedule tasks
  • You can easily adding logging and e-mail notifications as part of the backup process and create a graphical interface to reviewing the backups

CONs

  • One more thing to setup and administer (more work for you)

 

What Should be Backed Up

Obviously, everything should be backed up initially, at least once, but after the initial backup, there is no need to backup the database Front-End, and/or other static files.  Furthermore, as you release updates to your front-end or other files, you are going to have them backed up again so you always have a copy of the every version.

What is critical to backup on a regular basis in the database Back-End (the data)

Lock Files
An important aspect of ensuring that your backups are not corrupted is to ensure no one is actively using the database when the backup is made.  As such, the easiest way to do this is to first check for the existence of a lock file (ldb, laccdb).  If a lock file exists, then someone is using the database, so it is not a good time to perform the backup.  This is also why it is best to perform your backup when no one is in the office (at night) or at the startup of the database, thus the first user triggers the backup before loading any form, reports, …

 

Backup Validation

It is critical to regularly validate that the backup process executed, but also, to actually validate the backup files themselves.  I mean you need to actually take a backup, perform a recovery and open the database files to ensure the file/data is sound.

Don’t laugh, I was brought into a company who hard years of backups, but they turned out to all be corrupted, so in fact they had no valid backups and no one had ever checked prior to actually needing them.  At that point it was too late and they lost their entire database!

 

Why Back Up Your Database

I’ve seen people over the years say they don’t need to worry about performing backups.  This is simply ludicrous!  There are a multitude of reasons why backing up your files, little alone your database (your data) is critical.  Things like needing to recover from

  • Accidental development errors
  • Accidental file deletion
  • Corruption
  • Computer/hard drive crashes

In 2018, backups are as critical as the development itself.  After sending typically hundreds of hours, thousands of dollars, developing a database, does it not make sense to protect it!

 

Premade Solution

If you’re looking for a solution that you can simply drop into place, you may like to take a look at MS Access Auto Backup

MS Access – Hide the Ribbon

$
0
0

One more of these common questions in the forums is how to hide the Access application ribbon completely.  As per the usual with Access, this can be accomplished a few different ways:

  • Using VBA
  • Using Ribbon XML

Using VBA

The simplest and easiest way to hide the ribbon completely from the user is to use VBA.  In a single line of code (as shown below) it can be removed.

DoCmd.ShowToolbar "Ribbon", acToolbarNo

and display the ribbon again you simply do

DoCmd.ShowToolbar "Ribbon", acToolbarYes

Using Ribbon XML

Sometimes however, it is nice to have much more control over the ribbon and this is where using custom ribbon XML can become very helpful.  Now Ribbon XML can be setup in various ways (USysRibbons table, User-defined table, XML File, straight from code, …).

Personally, I find using the USysRibbons table the easiest and now include it in all my databases. Then I include a simple ribbon who’s startFromScratch is set to true and has no content (so a blank, non-existent ribbon).

So I normally just import the table from a previous project as quite often the ribbons are useful, but to start a new USysRibbons table from scratch you can simply run the following query

CREATE TABLE USysRibbons (  
     ID              COUNTER,
     RibbonName      TEXT(255),
     RibbonXML       MEMO
);

Then, you can make a new entry in the table.  Provide a RibbonName, I keep it simple and use Blank_Ribbon and then use the following RibbonXML

<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
  <ribbon startFromScratch="true">
  </ribbon>
</customUI>

Then it is simply a question of applying it globally to the database File -> Options -> Current Database -> Ribbon and Toolbar Options -> Ribbon Name and selecting the name of the Ribbon we just created.

The next time you open your database, the ribbon will be gone.  The only way to get it back is to use the shift bypass to regain access to the File menu to be able to change the property.

Now your probably saying to yourself “that a lot of work when I could achieve the same thing with a single line of VBA code!?”  Yes, it is, but the difference is that I can then easily add other custom ribbons and apply them to individual database objects (printing ribbon for reports, data export ribbon for certain forms, …) now that I have the framework setup.

So it comes down to your needs.  Are you simply hiding the ribbon and that’s it or are you looking to get into ribbon customization and give your users a custom experience.


MS Access – Determine Build Number

$
0
0

With the never ending updates for Office 365/2016 it has become necessary to be able to determine what build number you, your users, are running to be able to determine if you have all the latest updates installed, and/or determine if you have a known bugged update installed… so you can determine if you need to update/revert your installation.

As per most things, it isn’t really hard to locate this information once you’ve done so once.

How to Retrieve the Version and Build Information of Your Access 365/2016 Installation

  1. Click on the File tab
  2. Click on Account on the right-hand side menu.  Then look for the version and build information under the About Access heading on the right-side.

 

As I said, it’s not a question of being hard, it’s a question of being aware where Microsoft moved such information to.  Now you know!

MS Access – High DPI issues

$
0
0

With the higher and higher screen resolutions we are starting to see issues with Office application and High DPI displays. For instance, if you look at the following Answers forum thread

Has anybody at Microsoft ever edited an Access Form Chart control on a zoomed high DPI display?

you can see a few screenshots of just how useable Access can become in a High DPI environment.  Now, I mention Access, but this can be an issue with pretty much any application (Excel, Word, Outlook, …) and the fix elaborated below is applicable to any of them as well.

The Fix

There is a little known fix to this issue, but as per the usual it isn’t well known and is left to the user to implement!

  1. Close any open instances of Access (or whatever program you are trying to fix)
  2. Open Windows Explorer and navigate to the program exe (so for Access you need to locate the msaccess.exe file) which is typically found in
  3. C:\Program Files (x86)\Microsoft Office\Office15     (replace the 15 with whatever version you are running)
  4. Then right-click on the file itself and select Properties.
  5. Click on the Compatibility tab
  6. Enable the Disable display scaling on high DPI settings property by clicking on the checkbox.
  7. Click OK, close Windows Explorer and start Access and all should be good.

Now, in the case of Access, since it incorporates other programs within it, you also need to apply the same change to them.  So be sure to repeat the above steps to GRAPH.EXE so any charts/graphs display properly for you!

We can only hope that Microsoft will make such manual tweaks automated in the near future so that an application automatically adjusts and display in an optimal manner depending on its environment.

Access – Unlocking an Access VBA Project

$
0
0

I recently needed to unlock an Access VBA Project for which my client had lost the password. I was surprised to find that there was an incredibly simple hack that can unlock any Access password protected VBA project and I figured I’d share in case it could help others.

Make a copy of the database in question and only work with the copy.  Never work with the original database file just in case anything goes wrong.

Open the database file in any HEX Editor of your choosing.

Find any and all occurrences of the term DPB= and change the B to another value (many tutorials suggest x, but it doesn’t seem to truly make any difference)


Once you have replaced all the occurrences, perform a Save As to save these modification under a new file.

Open the newly saved file in Access (like you would normally) and then go into the VBA editor.  You will receive several error messages, simply dismiss them all (and yes, there can be quite a few!).


Open the Database Properties which will now have no password specified.


Enter a new password and confirm it to resecure the VBA Project with a known password.

Save the VBA Project

Close the database

That’s it, everything should now be in order.  Use your database as you normally do.

 

My Take and Hopes Regarding this Matter
This entire experience has drastically changed my opinion on overall Office/Access security and securing VBA projects as it appears to be completely futile! I’m surprised that with the importance of security in 2018 that Microsoft leaves it so very simple to hack this password. You can unlock an Access VBA project in about 30 seconds with freely available tools.

I’m also hoping that by shedding some light on this issue perhaps Microsoft will make the necessary changes to start protecting people’s information properly. This is completely unacceptable in 2018 IMHO.

 

Access – Working with DBase dbf Files With Names That Are More Than 8 Characters

$
0
0

Have you ever needed to Import or Link a DBase DBF file in an Access? If your DBF filename is longer than 8 characters you’ll be in for a nice treat!


and once you click OK, you’ll get a nice error!!!

 So, it would seem that Access can’t handle dbf files with filenames that are more than 8 characters long.  We’re living back in the days of DOS.

Funny enough, it’s actually that very thought, that gave me the solution to this issue!

Back in the days of DOS, you couldn’t have these super long file names like we do today and the format is now known a ShortFileName and there’s a simple API that enables us to convert a LongFileName into the old ShortFileName format.  I wondered if by simply converting formats Access might be happy once again and perform the Import/Link.

Public Declare Function GetShortPathName Lib "kernel32" _
                                         Alias "GetShortPathNameA" _
                                         (ByVal lpszLongPath As String, _
                                          ByVal lpszShortPath As String, _
                                          ByVal cchBuffer As Long) As Long

'---------------------------------------------------------------------------------------
' Procedure : GetShortName
' Author    : Original author is unknown
'             Modified Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Convert a LongFileName to a ShortFileName (think DOS!)
' 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: None required, Req GetShortPathName Declaration
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile     : Path and filename including extension to convert format
'
' Usage:
' ~~~~~~
' GetShortName("C:\Users\Daniel\Desktop\XBase - dBase sample data.dbf")
'       Returns -> C:\Users\Daniel\Desktop\XBASE-~1.DBF
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         Unknown                 Initial Release
' 2         2018-05-17              Error handling, ...
'---------------------------------------------------------------------------------------
Public Function GetShortName(sFile As String) As String
    On Error GoTo Error_Handler
    Dim sShortFile            As String * 67
    Dim lResult               As Long

    'Make a call to the GetShortPathName API
    lResult = GetShortPathName(sFile, sShortFile, _
                               Len(sShortFile))
    'Trim out unused characters from the string.
    GetShortName = Left$(sShortFile, lResult)

Error_Handler_Exit:
    On Error Resume Next
    Exit Function

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

So after converting the original path

C:\Users\Daniel\Desktop\XBase – dBase sample data.dbf

We get

C:\Users\Daniel\Desktop\XBASE-~1.DBF

Now, we use this new file path/name as our File name (as shown below)


and surprise, surprise the Link/Import works as it should.  So it would seem Access was simply feeling nostalgic!

I know this is far from ideal, but at least it works.

I have notified the Access Dev Team requesting this be added to their ‘To-Do’ list.  There are no guarantees, but at least they were made aware and to me this seems like a very simple thing to address, so no real reason they shouldn’t be able to fix this.

After coming up with this workaround, I did some searching and discovered that this approach had been suggested by others (I guess great minds think alike). Moreover, there is a Uservoice suggestion entitles Allow Access to connect to a dBASE (.dbf) file with a name over 8 characters related to this very issue, so feel free to add your votes to show interest in resolving this bug (this isn’t a feature request as much as resolving a bug in the fundamental processing of the file).

Managing User-Level Security in Access 2007+

$
0
0

Here’s another question I see pop up from time to time in the Forums.

Since Microsoft did away with User-Level Security (ULS) in Access 2007+, people often wonder how they can manage ULS; add/remove users, change password, …?

The reality of the situation is that although the commands are not front and center, ie they aren’t listed in the Ribbon anywhere, they are still actually there.  Just hidden!

So the question becomes, how can we access them?

Well, there are 2 ways to do this:

  • Issue the VBA commands directly
  • Customizing the Ribbon

Issue the VBA commands directly

You can access each of the 3 ULS dialogs by using the following 3 VBA commands.

User and Group Account

DoCmd.RunCommand acCmdUserAndGroupAccounts

User and Group Permissions

DoCmd.RunCommand acCmdUserAndGroupPermissions

User-Level Security Wizard

DoCmd.RunCommand acCmdUserLevelSecurityWizard


 

Customizing the Ribbon

Another options is to simply customize the ribbon.

  1. Right-Click on the Ribbon and Select Customize the Ribbon
  2. In the Choose commands from combo box, select All Commands
  3. In the command listbox, scroll down to the User and … commands
  4. Use the Add >> button to add the select command(s) to the Tab Group of your choosing or, as in my example, create a new Security Tab Group and add them there.
  5. You may also like to add the Set Logon Password command to your custom ribbon tab group for the sake of completeness.

 

Note
Also note this only works with Access .mdb file format databases.  You can’t apply ULS to the newer .accdb file format at all.

 

Viewing all 89 articles
Browse latest View live