Difference between revisions of "Microsoft Access VBA Code Snippets"

From database24
Jump to navigation Jump to search
Line 48: Line 48:
 
* Lines 10, 20ff: The variable type should always be converted explicitly, although VBA is able to cast implicit conversions (like strResult = datNow).
 
* Lines 10, 20ff: The variable type should always be converted explicitly, although VBA is able to cast implicit conversions (like strResult = datNow).
  
== modAccess ==
+
== Modules ==
modAccess consists basically of methods, which are specific to Microsoft Access like methods for retrieving information about properties, checking Access objects for their existence.
+
* [[Microsoft Access modAccess|modAccess]] consists basically of methods, which are specific to Microsoft Access like methods for retrieving information about properties, checking Access objects for their existence.
 
 
=== getApplicationTitle ===
 
<syntaxhighlight lang="vb">
 
Function getApplicationTitle() As String
 
    Dim strResult As String
 
   
 
    strResult = CurrentDb.Properties("AppTitle").Value
 
   
 
    getApplicationTitle = strResult
 
End Function
 
</syntaxhighlight>
 
 
 
=== getProjectName ===
 
The first guess for this is usually '''CurrentProject.Name''' but unfortunately this just returns the name of the file. If you want to bind your settings to a certain project, you certainly don't want to rely on the exact naming of a file; to the contrary you want to be able to use your stored settings no matter what the database file is named.
 
 
 
<syntaxhighlight lang="vb">
 
Function getProjectName() As String
 
    Dim strResult As String
 
   
 
    strResult = Application.VBE.ActiveVBProject.Name
 
   
 
    getProjectName = strResult
 
End Function
 
</syntaxhighlight>
 
 
 
=== setAccessAttribute / setupAccessAttributes ===
 
<syntaxhighlight lang="vb">
 
Public Function setAccessAttribute( _
 
    strName As String, _
 
    varType As Variant, _
 
    varValue As Variant _
 
    ) As Boolean
 
    Dim blnResult As Boolean
 
   
 
    Dim dbs As Database
 
    Dim prp As Property
 
   
 
    Set dbs = CurrentDb
 
    On Error GoTo handleSetAccessAttributeError
 
    dbs.Properties(strName) = varValue
 
    blnResult = True
 
   
 
    setAccessAttribute = blnResult
 
    Exit Function
 
   
 
handleSetAccessAttributeError:
 
    If Err.Number = 3270 Then
 
        'Property not found
 
        Set prp = dbs.CreateProperty(strName, varType, varValue)
 
        dbs.Properties.Append prp
 
        Resume Next
 
    Else
 
        'Unknown error
 
        blnResult = False
 
    End If
 
End Function
 
</syntaxhighlight>
 
 
 
<syntaxhighlight lang="vb">
 
Public Sub setupAccessAttributes()
 
    'Application
 
    setAccessAttribute "AppTitle", dbText, getProject
 
    setAccessAttribute "AppIcon", dbText, "d:\project\appicon.ico"
 
 
 
    'Startup
 
    setAccessAttribute "StartupForm", dbText, "frmMain"
 
    setAccessAttribute "StartupShowDbWindow", dbBoolean, False
 
    setAccessAttribute "StartupShowStatusBar", dbBoolean, True
 
 
 
    'Allowance
 
    setAccessAttribute "AllowFullMenus", dbBoolean, True
 
    setAccessAttribute "AllowBuiltinToolbars", dbBoolean, True
 
    setAccessAttribute "AllowToolbarChanges", dbBoolean, False
 
    setAccessAttribute "AllowShortcutMenus", dbBoolean, True
 
    setAccessAttribute "AllowBreakIntoCode", dbBoolean, True
 
    setAccessAttribute "AllowSpecialKeys", dbBoolean, True
 
    setAccessAttribute "AllowBypassKey", dbBoolean, True
 
End Sub
 
</syntaxhighlight>
 
 
 
=== setDebug / isDebug ===
 
<syntaxhighlight lang="vb">
 
Sub setDebug(blnDebug As Boolean)
 
    saveSetting getProjectName, "RunTime", "Debug", CStr(blnDebug)
 
End Sub
 
</syntaxhighlight>
 
 
 
<syntaxhighlight lang="vb">
 
Function isDebug() As Boolean
 
    Dim blnResult As Boolean
 
   
 
    blnResult = CBool(GetSetting(getProjectName, "RunTime", "Debug", 0))
 
   
 
    isDebug = blnResult
 
End Function
 
</syntaxhighlight>
 
 
 
=== existsTable ===
 
<syntaxhighlight lang="vb">
 
Function existsTable(strTable As String) As Boolean
 
    Dim blnResult As Boolean
 
    Dim tdf As TableDef
 
   
 
    blnResult = False
 
    For Each tdf In CurrentDb.TableDefs
 
        If tdf.Name = strTable Then
 
            blnResult = True
 
            Exit For
 
        End If
 
    Next
 
   
 
    existsTable = blnResult
 
End Function
 
</syntaxhighlight>
 
 
 
=== existsQuery ===
 
<syntaxhighlight lang="vb">
 
Function existsQuery(strQuery As String) As Boolean
 
    Dim blnResult As Boolean
 
    Dim qdf As QueryDef
 
   
 
    blnResult = False
 
    For Each qdf In CurrentDb.QueryDefs
 
        If qdf.Name = strQuery Then
 
            blnResult = True
 
            Exit For
 
        End If
 
    Next
 
   
 
    existsQuery = blnResult
 
End Function
 
</syntaxhighlight>
 
 
 
=== setTableDescription / getTableDescription ===
 
The property "Description" is not initially present in a table definition. Therefore it has to be created and appended to the property collection of the table definition on first usage.
 
 
 
<syntaxhighlight lang="vb">
 
Sub setTableDescription(strTable As String, strDescription As String)
 
    Dim dbs As Database
 
    Dim tdf As TableDef
 
    Dim prp As Property
 
   
 
    Set dbs = CurrentDb
 
    Set tdf = dbs.TableDefs(strTable)
 
   
 
    On Error GoTo handleSetTableDescriptionError
 
    tdf.Properties("Description").Value = strDescription
 
    On Error GoTo 0
 
    Exit Sub
 
   
 
handleSetTableDescriptionError:
 
    If Err.Number = 3270 Then
 
        Set prp = tdf.CreateProperty("Description", dbText, strDescription)
 
        tdf.Properties.Append prp
 
        Resume Next
 
    Else
 
        MsgBox Err.Number & ": " & Err.Description
 
    End If
 
End Sub
 
</syntaxhighlight>
 
 
 
<syntaxhighlight lang="vb">
 
Public Function getTableDescription(strTable As String) As String
 
    Dim strResult As String
 
 
 
    On Error Resume Next
 
    strResult = CurrentDb.TableDefs(strTable).Properties("Description").Value
 
    On Error GoTo 0
 
   
 
    getTableDescription = strResult
 
End Function
 
</syntaxhighlight>
 
 
 
=== inList ===
 
<syntaxhighlight lang="vb">
 
Public Function inList(cmb As ComboBox, var As Variant) As Boolean
 
    Dim blnResult As Boolean
 
    Dim intIndex As Integer
 
   
 
    blnResult = False
 
    With cmb
 
        For intIndex = Abs(.ColumnHeads) To .ListCount - 1
 
            If CLng(.ItemData(intIndex)) = CLng(Nz(var)) Then
 
                blnResult = True
 
                Exit For
 
            End If
 
        Next
 
    End With
 
   
 
    inList = blnResult
 
End Function
 
</syntaxhighlight>
 
 
 
=== displayActiveUsers ===
 
The usage of the Windows Script Host Object in order to identify the user's workstation is optional.
 
 
 
<syntaxhighlight lang="vb">
 
Sub displayActiveUsers()
 
    Dim strUsers As String
 
    Dim cn As New ADODB.Connection
 
    Dim rs As New ADODB.Recordset
 
    Dim wshNet As WshNetwork
 
 
 
    strUsers = "Computer Name"
 
    Set cn = CurrentProject.Connection
 
    Set rs = cn.OpenSchema( _
 
        Schema:=adSchemaProviderSpecific, _
 
        SchemaId:="{947bb102-5d43-11d1-bdbf-00c04fb92675}" _
 
        )
 
    Debug.Print _
 
        rs.Fields(0).Name & " " & _
 
        rs.Fields(1).Name ' & " " & _
 
        rs.Fields(2).Name & " " & _
 
        rs.Fields(3).Name
 
    Set wshNet = New WshNetwork
 
    With rs
 
        Do While Not .EOF
 
            strUsers = strUsers & vbCrLf & Chr$(149) & "  " & Left(.Fields(0).Value, Len(wshNet.ComputerName))
 
            If Left(.Fields(0).Value, Len(wshNet.ComputerName)) = wshNet.ComputerName Then
 
                strUsers = strUsers & " (me)"
 
                Debug.Print _
 
                    wshNet.ComputerName & "*    " & _
 
                    wshNet.UserName ' & " " & _
 
                    .Fields(2).Value & " " & _
 
                    .Fields(3).Value
 
            Else
 
                Debug.Print _
 
                    Left(.Fields(0).Value, Len(wshNet.ComputerName)) & "      " & _
 
                    Trim(.Fields(1).Value) ' & " " & _
 
                    .Fields(2).Value & " " & _
 
                    .Fields(3).Value
 
            End If
 
            .MoveNext
 
        Loop
 
    End With
 
    MsgBox strUsers, vbOKOnly, "Current Users"
 
End Sub
 
</syntaxhighlight>
 
 
 
=== linkTable ===
 
<syntaxhighlight lang="vb">
 
Public Sub linkTable( _
 
    strTable As String, _
 
    strFilePath As String, _
 
    Optional strTableSource As String = "" _
 
    )
 
    Dim dbs As Database
 
    Dim tdf As TableDef
 
   
 
    If strTableSource = "" Then
 
        strTableSource = strTable
 
    End If
 
   
 
    setStatus "Linking table '" & strTable & "' to " & strFilePath & " (" & strTableSource & ") ..."
 
    Set dbs = CurrentDb
 
    If existsTable(strTable) Then
 
        DoCmd.DeleteObject acTable, strTable
 
    End If
 
    Set tdf = dbs.CreateTableDef(strTable)
 
    With tdf
 
        .Connect = ";DATABASE=" & strFilePath
 
        .SourceTableName = strTableSource
 
    End With
 
    dbs.TableDefs.Append tdf
 
    setTableDescription strTable, strFilePath
 
    setStatus
 
End Sub
 
</syntaxhighlight>
 
 
 
=== updateForms ===
 
This method is used to reflect global changes in all open forms. The error handling is necessary in case a corresponding method should be missing.
 
<syntaxhighlight lang="vb">
 
Public Sub updateForms()
 
    Dim aob As AccessObject
 
   
 
    For Each aob In CurrentProject.AllForms
 
        With aob
 
            If .IsLoaded Then
 
                On Error Resume Next
 
                Forms.Item(.Name).updateForm
 
                On Error GoTo 0
 
            End If
 
        End With
 
    Next
 
End Sub
 
</syntaxhighlight>
 
 
 
In order to use this, two prerequisites must be met, which will be illustrated using a combo box which display a customer (cmbCustomer) and should reflect changes of a globally stored customer id (using setCustomerId / getCustomerId, for example):
 
 
 
* the changing procedure must call updateForms after the value has been changed
 
<syntaxhighlight lang="vb">
 
Private Sub cmbCustomer_AfterUpdate()
 
    setCustomerId cmbCustomer.Value
 
    updateForms
 
End Sub
 
</syntaxhighlight>
 
 
 
* every form, which uses the customer id, must have a public method "updateForm" which is responsible for reflecting the change in the form (or report).
 
<syntaxhighlight lang="vb">
 
Public Sub updateForm()
 
    cmbCustomer.Value = getCustomerId
 
End Sub
 
</syntaxhighlight>
 
 
 
=== removeDeprecatedObjects ===
 
<syntaxhighlight lang="vb">
 
Public Sub removeDeprecatedObjects(Optional blnExecute As Boolean = False)
 
    'All objects
 
    removeObjects "^[z]?_.*", blnExecute
 
    removeObjects "[0-9]{6}", blnExecute
 
    removeObjects "_alt[0-9]*$", blnExecute
 
   
 
    'Specific tables
 
    removeTables "(einfüge|import)fehler", blnExecute
 
    removeTables "^(temp|test)", blnExecute
 
   
 
    'Specific queries
 
    removeQueries "^abfrage", blnExecute
 
   
 
    'Specific forms
 
   
 
    'Specific reports
 
   
 
    If Not blnExecute Then
 
        Debug.Print
 
        Debug.Print _
 
            "If you would like to delete the above mentioned objects," & vbCrLf & _
 
            "please run 'removeDeprecatedObjects true'."
 
    End If
 
End Sub
 
</syntaxhighlight>
 
 
 
=== removeObjects ===
 
<syntaxhighlight lang="vb">
 
Public Sub removeObjects(strPattern As String, Optional blnExecute As Boolean = False)
 
    removeTables strPattern, blnExecute
 
    removeQueries strPattern, blnExecute
 
    removeForms strPattern, blnExecute
 
    removeReports strPattern, blnExecute
 
End Sub
 
</syntaxhighlight>
 
 
 
=== removeTables ===
 
<syntaxhighlight lang="vb">
 
Public Sub removeTables(strPattern As String, Optional blnExecute As Boolean = False)
 
    Dim tdf As TableDef
 
    Dim strTable As String
 
 
    For Each tdf In CurrentDb.TableDefs
 
        strTable = tdf.Name
 
        If matches(strTable, strPattern) Then
 
            setStatus "Removing table '" & strTable & "' ..."
 
            Debug.Print "Removing table '" & strTable & "' ..."
 
            If blnExecute Then
 
                DoCmd.DeleteObject acTable, strTable
 
            End If
 
        End If
 
    Next
 
    setStatus
 
End Sub
 
</syntaxhighlight>
 
 
 
=== removeQueries ===
 
<syntaxhighlight lang="vb">
 
Public Sub removeQueries(strPattern As String, Optional blnExecute As Boolean = False)
 
    Dim qdf As QueryDef
 
    Dim strQuery As String
 
 
    For Each qdf In CurrentDb.QueryDefs
 
        strQuery = qdf.Name
 
        If matches(strQuery, strPattern) Then
 
            setStatus "Removing query '" & strQuery & "' ..."
 
            Debug.Print "Removing query '" & strQuery & "' ..."
 
            If blnExecute Then
 
                DoCmd.DeleteObject acQuery, strQuery
 
            End If
 
        End If
 
    Next
 
    setStatus
 
End Sub
 
</syntaxhighlight>
 
 
 
=== removeForms ===
 
<syntaxhighlight lang="vb">
 
Public Sub removeForms(strPattern As String, Optional blnExecute As Boolean = False)
 
    Dim afs As AllForms
 
    Dim aob As AccessObject
 
    Dim strForm As String
 
 
    Set afs = CurrentProject.AllForms
 
    For Each aob In afs
 
        strForm = aob.Name
 
        If matches(strForm, strPattern) Then
 
            setStatus "Removing Form '" & strForm & "' ..."
 
            Debug.Print "Removing Form '" & strForm & "' ..."
 
            If blnExecute Then
 
                DoCmd.DeleteObject acForm, strForm
 
            End If
 
        End If
 
    Next
 
    setStatus
 
End Sub
 
</syntaxhighlight>
 
 
 
=== removeReports ===
 
<syntaxhighlight lang="vb">
 
Public Sub removeReports(strPattern As String, Optional blnExecute As Boolean = False)
 
    Dim arp As AllReports
 
    Dim aob As AccessObject
 
    Dim strReport As String
 
 
    Set arp = CurrentProject.AllReports
 
    For Each aob In arp
 
        strReport = aob.Name
 
        If matches(strReport, strPattern) Then
 
            setStatus "Removing Report '" & strReport & "' ..."
 
            Debug.Print "Removing Report '" & strReport & "' ..."
 
            If blnExecute Then
 
                DoCmd.DeleteObject acReport, strReport
 
            End If
 
        End If
 
    Next
 
    setStatus
 
End Sub
 
</syntaxhighlight>
 
 
 
=== setTableFieldType ===
 
<syntaxhighlight lang="vb">
 
Public Sub setTableFieldType(strTable As String, strField As String, strType As String)
 
    Dim strSql As String
 
 
    strSql = _
 
        "ALTER TABLE " & strTable & " ALTER COLUMN " & strField & " " & strType
 
    executeSql strSql
 
End Sub
 
</syntaxhighlight>
 
 
 
=== setTableFieldFormat ===
 
<syntaxhighlight lang="vb">
 
Public Sub setTableFieldFormat(strTable As String, strField As String, strFormat As String)
 
    Dim db As Database
 
    Dim tdf As TableDef
 
    Dim fld As Field
 
    Dim prp As Property
 
   
 
    Set db = CurrentDb
 
    Set tdf = db.TableDefs(strTable)
 
    Set fld = tdf.Fields(strField)
 
    On Error GoTo handleSetTableFieldFormatError
 
    fld.Properties("Format") = strFormat
 
    On Error GoTo 0
 
    tdf.Fields.Refresh
 
    Exit Sub
 
   
 
handleSetTableFieldFormatError:
 
    If Err.Number = 3270 Then
 
        fld.Properties.Append tdf.CreateProperty("Format", dbText, strFormat)
 
    End If
 
    Resume Next
 
End Sub
 
</syntaxhighlight>
 
 
 
=== displayGaps ===
 
<syntaxhighlight lang="vb">
 
Public Sub displayGaps( _
 
    strTable As String, _
 
    Optional strField As String = "Id", _
 
    Optional lngStart = 1, _
 
    Optional lngEnd = -1, _
 
    Optional lngLength = -1, _
 
    Optional lngCount = -1 _
 
    )
 
 
 
    Dim strTableMissing As String
 
    Dim strSql As String
 
   
 
    strTableMissing = "_" & strTable & "_Missing_" & strField
 
    strSql = _
 
        "  SELECT Current.Id - 1      AS MissingId            " & vbCrLf & _
 
        "    INTO " & strTableMissing & "                      " & vbCrLf & _
 
        "    FROM " & strTable & "    AS [Current]            " & vbCrLf & _
 
        "LEFT JOIN " & strTable & "    AS Previous              " & vbCrLf & _
 
        "      ON Current.Id          = Previous.Id + 1      " & vbCrLf & _
 
        "    WHERE Current.Id          > " & lngStart + 1 & "  " & vbCrLf & _
 
        "      AND Previous.Id        IS NULL                  "
 
    If lngEnd <> -1 Then
 
        strSql = strSql & _
 
            "      AND Current.Id          < " & lngEnd + 1 & "    "
 
    End If
 
    If lngCount <> -1 Then
 
        strSql = Replace(strSql, "SELECT", "SELECT TOP " & lngCount)
 
    End If
 
    executeSql strSql
 
   
 
    DoCmd.OpenTable strTableMissing
 
End Sub
 
</syntaxhighlight>
 
 
 
=== turnOffSubDataSheets ===
 
<syntaxhighlight lang="vb">
 
Public Sub turnOffSubDataSheets()
 
    Dim db As Database
 
    Dim tdf As TableDef
 
    Dim prp As Property
 
    Dim strProperty As String
 
    Dim strValueRight As String
 
    Dim strValueWrong As String
 
   
 
    setStatus "Removing sub data sheets ..."
 
    Set db = CurrentDb
 
    strProperty = "SubDataSheetName"
 
    strValueRight = "[None]"
 
    strValueWrong = "[Auto]"
 
   
 
    For Each tdf In db.TableDefs
 
        With tdf
 
            If (.Attributes And dbSystemObject) = 0 Then
 
                On Error GoTo handlePropertyError
 
                If .Properties(strProperty).Value = strValueWrong Then
 
                    setStatus "Removing sub data sheets " & .Name & " ..."
 
                    .Properties(strProperty).Value = strValueRight
 
                    setStatus "Removing sub data sheets ..."
 
                End If
 
                On Error GoTo 0
 
            End If
 
        End With
 
    Next
 
    db.Close
 
    setStatus
 
    Exit Sub
 
   
 
handlePropertyError:
 
    With Err
 
        If .Number = 3270 Then
 
            Set prp = tdf.CreateProperty(strProperty)
 
            prp.Type = 10
 
            prp.Value = strValueRight
 
            tdf.Properties.Append prp
 
        Else
 
            Debug.Print .Number & " : " & .Description
 
        End If
 
        .Clear
 
    End With
 
    Resume Next
 
End Sub
 
</syntaxhighlight>
 
  
 
== modSetting ==
 
== modSetting ==

Revision as of 13:20, 1 July 2010

ActiveX Data Objects (ADO)

Retrieving and processing a recordset works like this

    Dim cn As ADODB.Connection
    Dim rs As ADODB.Recordset
    
    Set cn = CurrentProject.Connection
    Set rs = New ADODB.Recordset
    With rs
        .Open _
            Source:="SELECT * FROM tblTable", _
            ActiveConnection:=cn
        .MoveFirst
        Do While Not .EOF
            'Record based instructions
            .MoveNext
        Loop
        .Close
    End With

Setter and Getter

In order to store and retrieve settings from the user's registry, it is wise to implement Setters and Getters. The general code for a variable named "Id" of the variable type "Long" looks like this:

10 Public Sub setId(lngId As Long)
11     SaveSetting getProjectName, "RunTime", "Id", CStr(lngId)
12 End Sub
20 Public Function getId() As Long
21     Dim lngResult As Long
22     
23     lngResult = CLng(GetSetting(getProjectName, "RunTime", "Id", 0))
24     
25     getId = lngResult
26 End Function

A few remarks on this code:

  • Lines 11 and 23: "RunTime" just indicates that the variable will change frequently while working with the database. You may want to use different "categories" for your settings such as "RunTime", "Import", "User" and so on in order to reflect either the scope or the frequency of the settings. If you are using distinct namespaces like this, you should consider putting the namespace into the names of the functions, so that on the one hand it is possible to tell from the names which settings are meant and on the other hand you could reuse a term like Id in both namespaces; example: getId vs getImportId
  • Lines 11 and 23: The setting's name ("Id") should be the same that the functions have ("setId", "getId").
  • Lines 10, 20ff: The variable type should always be converted explicitly, although VBA is able to cast implicit conversions (like strResult = datNow).

Modules

  • modAccess consists basically of methods, which are specific to Microsoft Access like methods for retrieving information about properties, checking Access objects for their existence.

modSetting

debugPrintUserRunTimeSettings

Sub debugPrintUserRunTimeSettings()
    Dim arrSetting() As String
    Dim intIndex As Integer
    
    Debug.Print
    Debug.Print "User RunTime Settings"
    Debug.Print "---------------------"
    On Error GoTo handleGetAllSettingsError
    arrSetting = GetAllSettings(getProjectName, "RunTime")
    On Error GoTo 0
    For intIndex = LBound(arrSetting) To UBound(arrSetting)
        Debug.Print arrSetting(intIndex, 0) & " : " & arrSetting(intIndex, 1)
    Next
    Exit Sub
    
handleGetAllSettingsError:
    Debug.Print "No user settings available."
End Sub

deleteUserSettings

Sub deleteUserSettings()
    Debug.Print
    Debug.Print "User Settings"
    Debug.Print "-------------"
    On Error Resume Next
    DeleteSetting getProjectName
    Debug.Print "All user settings deleted."
End Sub

modUi

setStatus

Public Sub setStatus(Optional strMessage As String = "Bereit")
    strMessage = _
        getUserName & " - " & _
        strMessage
    SysCmd acSysCmdSetStatus, strMessage
End Sub

modFunction

round

Microsoft Access' Round() function is different from the one implemented in Microsoft Excel. The behavious can be imitated using a user defined function.

The "round2 Solution" is limited by the size of the long data type (roughly 2 billion):

Public Function round2(ByVal dblValue As Double, intDecimal As Integer) As Double
    Dim dblResult As Double
    
    Dim lngFactor As Long
    
    lngFactor = 10 ^ (intDecimal)
    dblResult = CLng(dblValue * lngFactor) / lngFactor
    
    round2 = dblResult
End Function

The "round3 Solution" is limited by the amount of decimal places of the currency data type (4 decimal places):

Public Function round3(ByVal dblValue As Double, intDecimal As Integer) As Double
    Dim dblResult As Double
    
    Dim lngFactor As Long
    
    lngFactor = 10 ^ (intDecimal)
    dblResult = CCur(dblValue / lngFactor) * lngFactor
    
    round3 = dblResult
End Function

lookupRecordset

The domain aggregate functions suffer from performance problems. This function performs a simple lookup in a recordset. The performance of this function compared to the built-in function still has to be proven.

Public Function lookupRecordset( _
    ByVal strFieldName As String, _
    ByVal strSource As String, _
    Optional ByVal strCriteria As String = vbNullString _
    ) As Variant
    Dim varResult as Variant
    
    Dim strSql As String
    Dim rst As Recordset
    
    strSql = _
        "SELECT " & strFieldName & "    " & vbCrLf & _
        "  FROM " & strSource & "       "
    If strCriteria > vbNullString Then
        strSql = strSql & vbCrLf & _
            " WHERE " & strCriteria
    End If
    Set rst = CurrentDb.OpenRecordset(strSql, dbOpenSnapshot)
    With rst
        If .EOF Then
                varResult = Null
            Else
                varResult = .Fields(0)
        End If
        .Close
    End With
    
    lookupRecordset = varResult
End Function

modSql

Constants

Public Const strFormatSqlDate As String = "yyyy\-mm\-dd"
Public Const strFormatSqlDateCriterion As String = "\#yyyy\-mm\-dd\#"

executeSql

Public Function executeSql(strSql As String) As Long
    Dim lngResult As Long
    
    Const strFormatHms As String = "hh:mm:ss"
    
    Dim datStart As Date
    Dim strErr As String
    Dim strTable As String
    Dim strAffected As String
    
    lngResult = 0
    
    If isDebug Then
        Debug.Print "----------------------------------------"
        Debug.Print strSql
        Debug.Print
        datStart = Now
        Debug.Print "Start      : " & Format(datStart, strFormatHms)
    End If

    With CurrentDb
        'DoCmd.SetWarnings False
        On Error GoTo handleExecuteError
        If isDebug Then
            .Execute strSql, dbFailOnError
        Else
            .Execute strSql
        End If
        On Error GoTo 0
        'DoCmd.SetWarnings True
        DoEvents
        
        strAffected = "Records    "
        Select Case .RecordsAffected
        Case 1
            'Last inserted id
            lngResult = .OpenRecordset("SELECT @@IDENTITY")(0)
            If lngResult = 0 Then
                lngResult = 1
            Else
                strAffected = "Last Id    "
            End If
        Case Else
            'Number of affected records
            lngResult = .RecordsAffected
        End Select
    End With
    
    If isDebug Then
        Debug.Print "End        : " & Format(Now, strFormatHms)
        Debug.Print "             " & "--------"
        Debug.Print "Duration   : " & Format(Now - datStart, strFormatHms)
        Debug.Print strAffected & ": " & Format(lngResult, "#,##0")
        Debug.Print "----------------------------------------"
        Debug.Print
    End If

    executeSql = lngResult
    Exit Function
    
handleExecuteError:
    'On Error GoTo handleExecuteSqlErrorFinal
    'DoCmd.RunSQL strSql
    'On Error GoTo 0
    'resume next
    'handleExecuteSqlErrorFinal:

    Select Case Err.Number
    
    Case 3010:
        'Table already exists
        strErr = Err.Description
        strTable = Mid( _
            Err.Description, _
            InStr(1, strErr, "'", vbTextCompare) + 1, _
            InStrRev(strErr, "'", -1, vbTextCompare) - _
                InStr(1, strErr, "'", vbTextCompare) - 1 _
            )
        DoCmd.Close acTable, strTable, acSaveNo
        DoCmd.DeleteObject acTable, strTable
        Resume
    
    Case 2008:
        'Table is open
        DoCmd.Close acTable, strTable, acSaveNo
        Resume
    
    Case Else
        strErr = Err.Number & " : " & Err.Description
        Debug.Print "########################################"
        Debug.Print "# ERR " & Err.Number & " : " & Err.Description
        Debug.Print "# ABORT"
        Debug.Print "########################################"
        Debug.Print
        If isDebug Then
            Stop
        End If
    
    End Select
End Function

showSqlResult

Public Sub showSqlResult(strSql As String, Optional strQuery As String = "")
    Dim dbs As Database
    Dim qdf As QueryDef
    
    If strQuery = "" Then
        strQuery = "SQL Result"
    End If
    If existsQuery(strQuery) Then
        DoCmd.DeleteObject acQuery, strQuery
    End If
    Set dbs = CurrentDb
    Set qdf = dbs.CreateQueryDef(Name:=strQuery, SQLText:=strSql)
    DoCmd.OpenQuery strQuery
    Do While CurrentData.AllQueries(strQuery).IsLoaded
        DoEvents
    Loop
    DoCmd.DeleteObject acQuery, strQuery
End Sub

getSqlAmount

Public Function getSqlAmount(cur As Currency) As String
    Dim strResult As String
    
    strResult = CStr(cur)
    strResult = Replace( _
        Expression:=strResult, _
        Find:=",", _
        Replace:=".", _
        Compare:=vbTextCompare _
        )
    
    getSqlAmount = strResult
End Function

getSqlDate

Public Function getSqlDate(dat As Date) As String
    Dim strResult As String
    
    strResult = Format(dat, strFormatSqlDate)
    
    getSqlDate = strResult
End Function

getSqlDateCriterion

Public Function getSqlDateCriterion(dat As Date) As String
    Dim strResult As String
    
    strResult = Format(dat, strFormatSqlDateCriterion)
    
    getSqlDateCriterion = strResult
End Function

modFso

assertPath

Sub assertPath(strPath)
    Dim fso As FileSystemObject
    Dim blnFirst As Boolean
    
    blnFirst = True
    Set fso = New FileSystemObject
    With fso
        If Not .FolderExists(strPath) Then
            On Error GoTo handleCreateFolderError
            .CreateFolder strPath
            On Error GoTo 0
        End If
    End With
    Exit Sub
    
handleCreateFolderError:
    If Err.Number = 76 And blnFirst Then
        Debug.Print strPath & " not found - trying to create parent ..."
        assurePath fso.GetParentFolderName(strPath)
        blnFirst = False
        Resume
    Else
        MsgBox "The path '" & strPath & "' could not be found, nor created.", vbExclamation + vbOKOnly
    End If
End Sub

modWsh

Windows Scripting Host Object based methods

getUserName

Function getUserName() As String
    Dim strResult As String
    
    Dim wshNet As WshNetwork
    
    Set wshNet = New WshNetwork
    strResult = wshNet.UserName
    
    getUserName = strResult
End Function

getComputerName

Function getComputerName() As String
    Dim strResult As String
    
    Dim wshNet As WshNetwork
    
    Set wshNet = New WshNetwork
    strResult = wshNet.ComputerName
    
    getComputerName = strResult
End Function

getUserSuffix

Public Function getUserSuffix() As String
    Dim strResult As String
    
    strResult = getUserName
    strResult = Replace(strResult, ".", "")
    strResult = Replace(strResult, ",", "")
    strResult = Replace(strResult, "-", "")
    strResult = Replace(strResult, " ", "")
    strResult = "_" & strResult
    
    getUserSuffix = strResult
End Function

getUserTempTable

Public Function getUserTempTable(strSource As String) As String
    Dim strResult As String
    
    Const strTablePrefix = "_tbl"
    
    strResult = strTablePrefix & Mid(strSource, 4) & getUserSuffix
    
    getUserTempTable = strResult
End Function

modExcel

Variables

Private xl As Excel.Application
Private blnCreated As Boolean

getExcel

Private Function getExcel(Optional blnVisible As Boolean = False) As Excel.Application
    If xl Is Nothing Then
        On Error Resume Next
        Set xl = GetObject(, "Excel.Application")
        On Error GoTo 0
        If xl Is Nothing Then
            Set xl = CreateObject("Excel.Application")
            xl.Visible = blnVisible
            blnCreated = True
        Else
            blnCreated = False
        End If
    End If
    
    Set getExcel = xl
End Function

quitExcel

Sub quitExcel()
    With xl
        If blnCreated Then
            .Quit
            blnCreated = False
        End If
    End With
End Sub

openExcelWorkbook

Public Function openExcelWorkbook(strPathFile As String) As Excel.Workbook
    Dim wbkResult As Excel.Workbook
    
    With getExcel
        Set wbkResult = .Workbooks.Open(strPathFile)
    End With
    
    Set openExcelWorkbook = wbkResult
End Function

closeExcelWorkbook

Public Sub closeExcelWorkbook(strWorkbook As String, Optional blnSave As Boolean = True, Optional blnQuit As Boolean = False)
    With xl
        With .Workbooks(strWorkbook)
            If blnSave Then
                .Save
            End If
            .Close SaveChanges:=False
        End With
        If blnQuit Then
            quitExcel
        End If
    End With
End Sub

applyCorporateStyle

Public Sub applyCorporateStyle(strFile As String)
    Dim fso As FileSystemObject
    
    Dim xls As Excel.Application
    Dim wbk As Workbook
    Dim wks As Worksheet
    Dim rng As Range
    
    Set fso = New FileSystemObject
    
    If Not fso.FileExists(strFile) Then
        MsgBox "The specified file does not exist."
        Exit Sub
    End If
    
    If Not Right(strFile, 4) = ".xls" Then
        MsgBox "The specified file is not a Microsoft Excel file."
        Exit Sub
    End If
    
    Set wbk = openExcelWorkbook(strFile)
    Set xls = wbk.Parent
    
    For Each wks In wbk.Worksheets
        With wks
            'Delete empty sheets
            If .UsedRange.Address = "$A$1" Then
                xls.DisplayAlerts = False
                .Delete
                xls.DisplayAlerts = True
            Else
                .Activate

                'Remove gridlines and freeze pane
                .Cells(2, 1).Select
                With xls.ActiveWindow
                    .DisplayGridlines = False
                    .FreezePanes = True
                End With
            
                'Format first row
                Set rng = .UsedRange.Rows(1)
                With rng
                    .Font.Bold = True
                    .Font.Color = vbWhite
                    .Interior.Color = vbRed
                
                    'Enable AutoFilter
                    If wks.AutoFilterMode = False Then
                        .AutoFilter
                    Else
                        .AutoFilter
                        .AutoFilter
                    End If
                
                End With
                
                'Adjust column widths
                .UsedRange.Columns.AutoFit
                
            End If

            'Number formatting
            For Each rng In .Columns
                
                'First row values
                With rng.Cells(2, 1)
                    If IsDate(.Value) Then
                        rng.NumberFormat = strFormatDate
                    ElseIf IsNumeric(.Value) Then
                        rng.NumberFormat = strFormatCurrency
                    End If
                End With
            Next

        End With
    Next
    
    closeExcelWorkbook strWorkbook:=wbk.Name, blnSave:=True
End Sub

subTotalColumn

Public Sub subTotalColumn( _
    strFilePath As String, _
    strWorksheet As String, _
    ParamArray arrColumnName() _
    )
    Dim wbk As Workbook
    Dim wks As Worksheet
    Dim rng As Range
    
    Dim varColumnName As Variant
    Dim rngFormula As Range
    Dim rngSum As Range
    Dim intRow As Integer
 
    Set wbk = openExcelWorkbook(strFilePath)
    Set wks = wbk.Worksheets(strWorksheet)
    With wks
        
        intRow = .UsedRange.Rows.Count
        
        'Add lines for sub totals
        .Rows("1:1").Insert Shift:=xlDown
        .Rows("1:1").Insert Shift:=xlDown
        .Rows("1:1").Insert Shift:=xlDown
        
        intRow = intRow + 3
        
        For Each varColumnName In arrColumnName
        
            Set rngFormula = .Rows(4).Find( _
                What:=varColumnName).Offset(-2, 0)
            
            Set rngSum = .Range( _
                .Cells(5, rngFormula.column), _
                .Cells(intRow, rngFormula.column))
            
            Debug.Print
            Debug.Print "UsedRg : " & .UsedRange.Rows.Count
            Debug.Print "intRow : " & intRow
            Debug.Print "rngSum : " & rngSum.Address
            Debug.Print
            
            If IsNumeric(rngSum.Cells(1, 1)) Then
                rngFormula.Formula = _
                    "=SUBTOTAL(9," & rngSum.Address & ")"
            Else
                rngFormula.Formula = _
                    "=SUBTOTAL(3," & rngSum.Address & ")"
                rngFormula.NumberFormat = "0 ""Receipts"""
            End If
        Next
        
    End With
    
    rngFormula.EntireRow.Font.Bold = True
    
    closeExcelWorkbook strWorkbook:=wbk.Name, blnSave:=True
End Sub

sumColumn

Public Sub sumColumn( _
    strFilePath As String, _
    strWorksheet As String, _
    ParamArray arrColumn() _
    )
    Dim wbk As Workbook
    Dim wks As Worksheet
    Dim rng As Range
    
    Dim varIndex As Variant
    Dim rngSum As Range
    Dim intRow As Integer
 
    Set wbk = openExcelWorkbook(strFilePath)
    Set wks = wbk.Worksheets(strWorksheet)
    With wks
        intRow = .UsedRange.Rows.Count
        For Each varIndex In arrColumn
            Set rngSum = .Range( _
                .Cells(1, varIndex), _
                .Cells(intRow, varIndex))
            rngSum.Cells(intRow, 1).Offset(2, 0).Formula = "=SUM(" & rngSum.Address & ")"
        Next
    End With
    
    closeExcelWorkbook strWorkbook:=wbk.Name, blnSave:=True
End Sub

formatColumnNumber

Public Sub formatColumnNumber( _
    strFilePath As String, _
    strWorksheet As String, _
    strNumberFormat As String, _
    ParamArray arrColumn() _
    )
    Dim wbk As Workbook
    Dim wks As Worksheet
    Dim rng As Range
    
    Dim varIndex As Variant
 
    Set wbk = openExcelWorkbook(strFilePath)
    Set wks = wbk.Worksheets(strWorksheet)
    With wks
        For Each varIndex In arrColumn
            .Columns(varIndex).NumberFormat = strNumberFormat
        Next
    End With
    
    closeExcelWorkbook strWorkbook:=wbk.Name, blnSave:=True
End Sub

formatColumnBackground

Public Sub formatColumnBackground( _
    strFilePath As String, _
    strWorksheet As String, _
    lngColor As Long, _
    ParamArray arrColumn() _
    )
    Dim wbk As Workbook
    Dim wks As Worksheet
    Dim rng As Range
    
    Dim intRow As Integer
    Dim varIndex As Variant
 
    Set wbk = openExcelWorkbook(strFilePath)
    Set wks = wbk.Worksheets(strWorksheet)
    With wks
        intRow = .UsedRange.Rows.Count
        For Each varIndex In arrColumn
            .Range( _
                Cells(2, varIndex), _
                Cells(intRow, varIndex) _
            ).Interior.Color = lngColor
        Next
    End With
    
    closeExcelWorkbook strWorkbook:=wbk.Name, blnSave:=True
End Sub

modString

getAlNumString

Public Function getAlNumString(strSource As String) As String
    Dim strResult As String
 
    Dim intIndex As Integer
    Dim strCharacter As String
 
    strResult = ""
    For intIndex = 1 To Len(strSource)
        strCharacter = Mid(strSource, intIndex, 1)
        Select Case Asc(strCharacter)
 
        ''A - Z, a - z
        Case 65 To 90, 97 To 122
            strResult = strResult & strCharacter
        
        '0 - 9
        Case 48 To 57
            strResult = strResult & strCharacter
        
        Case Else
            'nothing
        
        End Select
    Next
 
    getAlNumString = strResult
End Function

getAlNumPunctString

Public Function getAlNumPunctString(strSource As String) As String
    Dim strResult As String
 
    Dim intIndex As Integer
    Dim strCharacter As String
 
    strResult = ""
    For intIndex = 1 To Len(strSource)
        strCharacter = Mid(strSource, intIndex, 1)
        Select Case Asc(strCharacter)
 
        'A - Z, a - z
        Case 65 To 90, 97 To 122
            strResult = strResult & strCharacter
        
        '0 - 9
        Case 48 To 57
            strResult = strResult & strCharacter
        
        '<space>, <comma>, <hyphen>, <dot>, <slash>
        Case 20, 44 To 47
            strResult = strResult & strCharacter
        
        Case Else
            'nothing
        
        End Select
    Next
 
    getAlNumPunctString = strResult
End Function

getPrintableString

Public Function getPrintableString(strSource As String) As String
    Dim strResult As String
 
    Dim intIndex As Integer
    Dim strCharacter As String
 
    strResult = ""
    For intIndex = 1 To Len(strSource)
        strCharacter = Mid(strSource, intIndex, 1)
        Select Case Asc(strCharacter)
 
        'ASCII printable characters
        Case 20 To 126
            strResult = strResult & strCharacter
 
        Case Else
            'nothing
        
        End Select
    Next
 
    getPrintableString = strResult
End Function

matches

Public Function matches(strString As String, strRegEx As String) As Boolean
    Dim blnResult As Boolean
    
    Dim rgx As RegExp
    
    Set rgx = New RegExp
    With rgx
        .Pattern = strRegEx
        blnResult = .Test(strString)
    End With
    
    matches = blnResult
End Function