Microsoft Access VBA Tutorials for self-paced learning, Class Modules, SQL Techniques, and AI Integration Guides.

Exporting All Access Tables into Excel WorkBook

Exporting Access Tables into Excel.

Earlier, we explored how to work with live MS Access data in Excel. We also created a VBA-based procedure to export Table or Query data directly into the current version of an Excel Workbook. The main advantage of saving data in the current Excel format is that it automatically applies Excel’s default theme and formatting features, unlike the legacy Excel 2003 format used when relying on the 'acSpreadsheetTypeExcel3' to acSpreadsheetTypeExcel9 parameters.

This approach also allowed exporting filtered data based on multiple query criteria and saving each dataset to separate Worksheets within the same Workbook. A further extension of this method enabled exporting filtered records into entirely separate Workbooks as well.

Excel’s tools for data analysis, charting, and reporting are powerful and highly flexible. However, Excel was never designed to serve as a Database Management System. When historical data becomes important for decision-making, budget forecasting, business target setting, and other critical needs, users often find themselves turning to a more robust solution like MS Access.

That said, exporting the full set of Access Tables into Excel is not something that most users will require daily.

Exporting All Access Tables Into an Excel Workbook.

However, if necessary, you can use the VBA program provided below.

The VBA Program ExportAllTables2Excel()

Copy and Paste the Code given below into the Standard Module of your Project.

Public Sub ExportAllTables2Excel()
'----------------------------------------------------------------
'Program : ExportAllTables2Excel
'Purpose : Export All Access Tables into Excel WorkBook
'Author  : a.p.r. pillai
'Rights  : All Rights Reserved by www.msaccesstips.com
'Remarks : Creates separate WorkSheets for each Table
'        : in a single WorkBook.
'        : Table Name is Worksheet Name
'----------------------------------------------------------------
Dim db As Database
Dim xlsFileLoc As String
Dim xlsName As String
Dim xlsPath As String
Dim Tbl As TableDef
Dim tblName As String
Dim j As Integer
Dim wrkBook As Excel.Workbook

On Error GoTo Export2Excel_Err

xlsFileLoc = CurrentProject.Path & "\"
xlsName = "AllTables.xlsx"

xlsPath = xlsFileLoc & xlsName
    
If Len(Dir(xlsPath)) > 0 Then
    Kill xlsPath
End If

    Set wrkBook = Excel.Workbooks.Add
    wrkBook.SaveAs xlsPath
    wrkBook.Close
        
Set db = CurrentDb

j = 0
For Each Tbl In db.TableDefs
    tblName = Tbl.Name
    If Left(tblName, 4) = "MSys" Then 'System Tables
        GoTo nextstep
    Else
        j = j + 1
On Error Resume Next
        DoCmd.TransferSpreadsheet acExport, _
        acSpreadsheetTypeExcel12Xml, tblName, xlsPath, True
        If Err > 0 Then
            Err.Clear
          debug.print tblName
            j = j - 1
            Resume nextstep
        End If
End If
nextstep:
Next

On Error GoTo Export2Excel_Err

   MsgBox j & " Table(s) Exported to File:" & vbCr & xlsPath, , "Export2Excel()"
   
Set wrkBook = Nothing
Set db = Nothing

Export2Excel_Exit:
Exit Sub

Export2Excel_Err:
MsgBox Err & " : " & Err.Description, , "Export2Excel()"
Resume Export2Excel_Exit
End Sub

Caution: Before exporting, check the maximum number of rows available in your version of Excel (press End + Down Arrow in a blank worksheet) and compare it with the largest Access table you plan to export. If a table contains more records than Excel can handle, the extra rows may either spill into a second worksheet or be lost entirely. The code provided has not been tested for this scenario, so proceed with caution and use it at your own risk.

Before running the code, make sure to attach the latest version of the Microsoft Excel Object Library to your Access project. Otherwise, the VBA code may not compile. To do this:

  1. Open the VBA editor (Alt + F11).

  2. From the Tools menu, select References.

  3. In the list, locate Microsoft Excel 16.0 Object Library (or the latest version available).

  4. Select the checkbox to enable the reference.

  5. Click OK to close the References window.

The VBA Code Review.

At the beginning of the code, the required variables are declared.

  •  xlsFileLoc  The variable is initialized with the database path. In this location, a new Excel workbook (in your current version of Excel) will be created to store the exported Access tables.

  • The workbook will be named AllTables.xlsx by default. If you prefer a different name, simply change it in the code. Alternatively, rename it after export.

  • The xlsPath variable is initialized with the full workbook path and filename.

In the next steps, the code creates a new Excel workbook named 'AllTables.xlsx', saves it in the specified location, and then closes it.

If a file with the same name already exists in that location, a warning message will appear. You can then either:

  • Overwrite the existing file, or

  • Cancel the export process to retain the current workbook contents.

Important: Ensure the target Excel file is not open while exporting. If the file is open, the export operation will fail.

The following TransferSpreadsheet command handles the export:

DoCmd.TransferSpreadsheet acExport, _
     acSpreadsheetTypeExcel12Xml, TblName, xlsPath, True

The xlsPath parameter specifies the Excel workbook file where the output data will be saved.

  • If the workbook already exists, the exported data will be written into a new worksheet within that file.

  • If you omit this parameter, Access will create a new target worksheet file, based on the file format defined by the second parameter (for example, acSpreadsheetTypeExcel12Xml).

The acSpreadsheetTypeExcel12Xml Option saves the data in Excel 2007 (and later) .xlsx format, but several other spreadsheet-type options are also available to match your requirements.

When you create a workbook in your current version of Excel and specify it as the target workbook, all the exported tables are saved in separate worksheets within the same file, rather than generating a new workbook for each table. Additionally, the exported data is automatically formatted using the default Office theme of your Excel version, giving it a more polished appearance.

Note: By default, the table export option creates an Excel 2003-format file, based on one of the legacy export options (acSpreadsheetTypeExcel3 to acSpreadsheetTypeExcel9). As a result, the formatting of the exported data may not look as refined as in newer Excel versions. This subject was discussed in detail in an earlier post titled MS Access and TransferSpreadsheet Command. You may refer to that article for a deeper understanding of the procedure, the different version options available, their output formats, and other key considerations.

In the next step, the Current Database object is assigned to the object variable db. The variable j is used to keep a count of the tables exported into the Excel workbook.

All table names can be retrieved from the TableDefs collection of the database object and passed to the DoCmd.TransferSpreadsheet command to transfer their records. However, this collection also contains system tables (hidden objects). Fortunately, all system table names begin with the prefix MSys, which makes them easy to identify and exclude from the export process.

j = 0
For Each Tbl In db.TableDefs
    tblName = Tbl.Name
    If Left(tblName, 4) = "MSys" Then 'System Tables
        GoTo nextstep
    Else
        j = j + 1
On Error Resume Next
        DoCmd.TransferSpreadsheet acExport, _
        acSpreadsheetTypeExcel12Xml, tblName, xlsPath, True
        If Err > 0 Then
            Err.Clear
          Debug.Print tblName
            j = j - 1
            Resume nextstep
        End If
End If
nextstep:
Next 

In the For Each ... Next loop, each table definition from the TableDefs collection is read into the TableDef object Tbl.

The Name property of the table is then assigned to the string variable tblName.

Next, the code checks whether the first four characters of are tblName equal to "MSys". If so, the table is identified as a system table, and the loop skips the export logic, moving on to the next table.

If the export operation fails due to an unforeseen error, that specific table is skipped, and the process continues with the next one. In such cases, the table counter variable is rolled back to reflect the last successful export, ensuring accuracy in the final count. The name of the table that caused the error is printed in the Debug Window, allowing you to investigate and take corrective action later. This way, a failure in a single table does not halt the entire process, and the remaining tables can still be exported as expected.

Once all tables have been processed, a message is displayed showing the total number of tables successfully exported into the Excel workbook.

You may download the attached Demo Database and try it right away.

  1. MS Access And Transfer Spreadsheet Command.
  2. Access And Windows API ShowWindow
  3. Database Backup/Restore From Desktop
  4. Get Disk Free Space - Windows API
  5. Access And Windows API ShowWindow
Share:

Database Backup Restore From Desktop

Database Backup/Restore.

We have already discussed two important topics earlier:

  1. Database Daily Backup – a procedure that runs automatically from within the database the first time it is opened each day. It ensures one backup per day and avoids repeated backups on subsequent openings or closings.

  2. Compacting and Repairing Databases – using a dedicated “maintenance” database with VBA programs to compact and repair multiple databases listed within it, one after another.

Apart from these, Microsoft Access itself provides a built-in option to compact and repair the current database automatically on close.

A New Approach: Backup and Restore with VBScript.

We will explore a new method of using VBScript files saved on the desktop to perform manual backup and restore operations. Unlike the first two methods, this is not an automated process. Instead, you simply double-click a desktop shortcut whenever you want to:

  • Create a backup of your database, or

  • Restore the database from an earlier backup if you encounter corruption or consistency issues.

VBScript (Visual Basic Scripting Edition) is a lightweight version of Visual Basic, often used in web pages and Windows automation. If you are familiar with VBA, you will find the syntax quite straightforward.

Preparing the Backup Script

  1. Open Notepad on your desktop.

  2. Paste the VBScript code (provided in the next section) into the empty file.

  3. Save the file as:

    CreateBackup.vbs

    Make sure the extension is .vbs and not .txt.

  4. Right-click the new file and choose Edit if you want to make changes later.

  5. To run the script, simply double-click the file.

Similarly, you can create another script named RestoreBackup.vbs to restore the database from the last saved copy.

The CreateBackup VB Script:

Call CreateBackup()

Sub CreateBackup()
'======================================================
'Desk-top Shortcut Name: CreateBackup.vbs
'Language              : VBScript - Creates File Backup
'Remark                : Run from Desktop Shortcut
'                      : Edit Backup Path
'======================================================
Dim s, t, p, a
Dim objFSO

Set objFSO = CreateObject("Scripting.FileSystemObject")

' Backup is taken in the File Folder itself
' with Day and Month added (-dd-mm) to the File Name.
' Example: D:\AccTest\Testdb-16-07.accdb

p = "D:\AccTest\NorthWind.accdb" 'Edit

s = InputBox("File PathName:","CreateBackup()" , p)

If objFSO.FileExists(s) Then
  'File Name changes appending with -dd-mm (-day-month) values.
    a = Left(Now(), 5)
    t = Left(s, InStrRev(s, ".") - 1) & "-" & a & Mid(s, InStrRev(s, "."))

'Create the File backup
    a = objFSO.CopyFile(s, t, True)

    MsgBox "Backup Successful!" & vbCr & vbCr & "Source: " & s & vbCr & "Backup: " & t,vbInformation,"CreateBackup()"
Else
    MsgBox "Source PathName: " & s & vbCr & "Not Found, Program Aborted!",vbCritical,"CreateBackup()"
End If
Set objFSO = Nothing
End Sub

Database CreateBackup() Code Line-By-Line.

Understanding the Backup Script

The backup procedure is straightforward. Let us walk through it step by step.

The first statement:

Call CreateBackup()

invokes the CreateBackup() subroutine (shown below). This explicit call is required because when you run a VBScript by double-clicking its desktop shortcut (or by right-clicking and selecting Open), any code placed directly inside a Subroutine or Function will be ignored by the script processor unless it is explicitly invoked.

Important Notes:

  1. Direct Script Execution

    • You may also write the script directly in a Notepad file with the .vbs extension, without wrapping it in a Sub or Function.

    • In that case, the script runs line by line from top to bottom when executed.

  2. Functions in VBScript

    • The definition  Function  is valid, but only if you omit the return type declaration.

    • Examples:

      Function CreateBackup() ' Acceptable End Function Function CreateBackup() As Integer ' Not allowed in VBScript End Function
  3. Variable Declarations

    • VBScript does not allow explicit type declarations (e.g., Dim db_Pathname As String).

    • All variables are treated like VBA Variants, meaning their type is determined automatically by the first value assigned to them.

    • If you are embedding VBScript logic inside VBA (for example, calling it from within Access), then you may use normal VBA-style declarations.

This ensures readers clearly understand:

  • why you used Call CreateBackup(),

  • the difference between .vbs free-flow scripts vs. subroutine/function structures, and

  • How variable handling differs from VBA.

Set objFSO = CreateObject("Scripting.FileSystemObject")

Breaking Down the Backup Script.

  1. Creating the File System Object.

    Set objFSO = CreateObject("Scripting.FileSystemObject")

    This line creates a File System Object (FSO) and assigns it to the variable objFSO. The FSO provides access to files and folders on your computer (similar to VBA’s FileSystemObject).

  2. Setting the Database Path.

    p = "D:\AccTest\NorthWind.accdb"

    The variable p holds the database pathname.

    • This value is shown as the default path in the InputBox() prompt.

    • If you always back up the same database, leave p set to the fixed path.

    • If you want flexibility, you can edit the pathname directly in the InputBox() prompt each time you run the script.

  3. Checking if the Database File Exists.

    If objFSO.FileExists(s) Then

    The FileExists() Method checks whether the source file (the database you want to back up) actually exists at the provided path.

    • If the file exists, the script proceeds with creating the backup.

    • If not, the script can return an error message (or do nothing).

  4. Creating the Backup File Name.
    If the database is found, the backup file name is generated by appending the current day and month in -dd-mm format to the original file name.

    Example:

    D:\AccTest\NorthWind-16-07.accdb

    Here, 16-07 indicates the backup was created on July 16th.

    a = Left(Now(), 5)
    t = Left(s, InStrRev(s, ".") - 1) & "-" & a & Mid(s, InStrRev(s, "."))

Copying the Database File.

The following statement performs the actual backup by calling the CopyFile() method of the objFSO object:

a = objFSO.CopyFile(s, t, True)
  • s (Source File): The full pathname of the database file you want to back up.

  • t (Target File) The new file name, including the date suffix, is created in the same folder.

  • True (Overwrite Option): If the target file already exists, it will be overwritten without warning.

Once the copy operation is completed, the script displays a confirmation message indicating that the backup was successful.

If the source file (s) is not found, the program halts and displays a critical error message.

Important Note:

If you run the backup procedure more than once on the same day, the backup file created earlier will be overwritten automatically, since the overwrite parameter is set to True. If you want to keep multiple backups per day, you’ll need to modify the naming convention (for example, by adding hours and minutes to the file name).

Database RestoreFile() VBScript Code. 

Creating the Restore Script.

Follow the same procedure you used earlier for creating the backup script file in Notepad:

  1. Open Notepad.

  2. Copy and paste the Restore Script code (given below) into the new file.

  3. Save the file with the name RestoreFile.vbs on your Desktop.

    • Ensure the extension is .vbs (not .txt).

  4. A Desktop Shortcut with the .vbs extension will appear, representing the Restore Script.

Whenever you need to restore your database from a backup, simply double-click the RestoreFile.vbs shortcut.

Paste the following code into your Notepad file:

Call RestoreFile()

Sub RestoreFile()
'================================================
'Language              : VBScript
'Desk-top Shortcut Name: RestoreFile.vbs 
'Remarks               : Restore File from Backup
'================================================
Dim db_Current
Dim db_Save
Dim db_Backup
Dim f_bkSource,f_Current, f_Save
Dim objFSO

'The following three demo lines can be replaced
'in the InputBox statement, to Input
'File Pathnames directly.
db_Current = "D:\AccTest\NorthWind.Accdb"      'The file needs replacement
db_Save = "D:\AccTest\NorthWind-Save.Accdb"    'save the [dbReplace] file with a new name
db_Backup = "D:\AccTest\NorthWind-17-07.Accdb" 'Restore from this Backup File

f_bkSource = InputBox("Backup File PathName:","Restore()",db_Backup)
f_Current = InputBox("Restore File PathName:","Restore()",db_Current)
f_save = InputBox("Save Current before replace:","Restore()",db_save)

'Create File System Object
Set objFSO = CreateObject("Scripting.FileSystemObject")

If objFSO.FileExists(f_Current) then
' Save the existing/corrupt file with a new name
' Third Param:TRUE, overwrites the existing file without warning.
    a = objFSO.CopyFile(f_Current, f_save, True)
    a = objFSO.DeleteFile(f_current) 'delete original file after copying with a new name
End If

'Check the Backup file Exists or Not
If objFSO.FileExists(f_bksource) then 
	a = objFSO.CopyFile(f_bkSource, f_Current, True) 'Restore the original file from backup.

  If objFSO.FileExists(f_Current) then 'check restore operation was successful or not.
   	MsgBox "File: " & f_Current & vbcrlf & " Successfully Restored.",vbInformation,"RestoreFile()"
Else MsgBox "Oops! Something went wrong.",vbCritical,"RestoreFile()"
End If Else MsgBox "Backup File: " & f_bksource & vbcrlf & " Not Found!",vbCritical,"RestoreFile()"
End If Set objFSO = Nothing End Sub

RestoreFile() Code Line-by-Line.

RestoreFile.vbs (with Save-before-Restore logic).

Call RestoreFile() Sub RestoreFile() Dim objFSO Dim db_Current, db_Save, db_Backup Dim f_Current, f_Save, f_bkSource ' Default pathnames (can be accepted or changed by user) db_Current = "D:\AccTest\NorthWind.accdb" ' File to be replaced db_Save = "D:\AccTest\NorthWind-Save.accdb" ' Save old version before overwrite db_Backup = "D:\AccTest\NorthWind-17-07.accdb" ' Backup to restore from ' Ask user for actual file pathnames f_Current = InputBox("Enter CURRENT database file to be replaced:", _ "Restore Database", db_Current) f_Save = InputBox("Enter SAVE file name for current database:", _ "Restore Database", db_Save) f_bkSource = InputBox("Enter BACKUP database file to restore from:", _ "Restore Database", db_Backup) ' Create File System Object Set objFSO = CreateObject("Scripting.FileSystemObject") ' 1. Check if Current DB exists, and Save it first If objFSO.FileExists(f_Current) Then objFSO.CopyFile f_Current, f_Save, True Else MsgBox "Current database not found: " & vbCrLf & f_Current, vbCritical, "Restore Failed" Exit Sub End If ' 2. Check if Backup file exists, then restore If objFSO.FileExists(f_bkSource) Then objFSO.CopyFile f_bkSource, f_Current, True MsgBox "Database restored successfully from:" & vbCrLf & f_bkSource & vbCrLf & _ "to:" & vbCrLf & f_Current & vbCrLf & vbCrLf & _ "Old database saved as:" & vbCrLf & f_Save, vbInformation, "Restore Complete" Else MsgBox "Backup file not found: " & vbCrLf & f_bkSource, vbCritical, "Restore Failed" End If End Sub

What happens here:

  1. Defaults are preloaded (db_Current, db_Save, db_Backup), so the InputBoxes show usable pathnames right away.

  2. The user can simply press Enter to accept them or edit them on the fly.

  3. Step 1: The Current DB is first copied to a “safe” file (…-Save.accdb) before it’s overwritten.

  4. Step 2: If the backup file exists, it overwrites the original DB with the chosen backup.

  5. Messages clearly indicate what happened and where the files went.

If objFSO.FileExists(f_Current) then
' Save the existing/corrupt file with a new name
' Third Param:TRUE, overwrites the existing file without warning.
	a = objFSO.CopyFile(f_Current, f_save, True)
        a = objFSO.DeleteFile(f_current) 'delete the original after copying with a new name
        
 End If

The above code segment checks whether the current file specified for replacement exists. If it does, the file is first copied with a modified name for safekeeping. If the file is not found, this step is skipped. Note that the CopyFile method does not return a status value in the variable, even if you assign it, as in a = objFSO.CopyFile(...). This syntax simply allows parentheses around the parameters. The following statement is equally valid and often clearer:

objFSO.CopyFile f_Current, f_save, True

Next, the corrupt file is removed using the DeleteFile() method of the objFSO object.

The following code segment then performs a validation check with the FileExists() method on the backup database file. If the file is found, it is copied back using the original database name—overwriting any existing file. Since the original was deleted in the previous step, this ensures the restored backup replaces it cleanly.  

'Check the Backup file Exists or Not
If objFSO.FileExists(f_bksource) then 
	a = objFSO.CopyFile(f_bkSource, f_Current, True) 'Restore the original file from backup.

  If objFSO.FileExists(f_Current) then 'check restore operation was successful or not.
   	MsgBox "File: " & f_Current & vbcrlf & " Successfully Restored.",vbInformation,"RestoreFile()"
  Else
   	MsgBox "Oops! Something went wrong.",vbCritical,"RestoreFile()"
End If Else MsgBox "Backup File: " & f_bksource & vbcrlf & " Not Found!",vbCritical,"RestoreFile()"
End If

After the restore operation, the program verifies the presence of the original database file to confirm that the restore was successful. If the file is found, a success message is displayed. If the copy operation fails, however, a critical error message is shown.

If the specified backup file cannot be located, the program immediately displays a critical error message and releases the objFSO object from memory before terminating.

Tip: You can back up and restore any type of file besides databases.

  1. Get Disk Free Space - Windows API
  2. Access And Windows API ShowWindow
  3. ListView Control Drag Drop Sort Events.
Share:

Get Disk Free Space Windows API

Get Disk Free Space.

Last week, we explored the ShowWindow and PostMessage Windows API functions. Using the built-in '.hWnd' property of Forms and Reports, we were able to change their window states (Normal, Minimized, Maximized, Hidden, or Closed) without relying on the FindWindow API to fetch their window handles.

This time, we will look at another simple but very useful Windows API function: GetDiskFreeSpace.

This function allows us to check the free space available on any logical drive in the system. Its declaration in VBA is as follows:

#If VBA7 Then

    Declare PtrSafe Function GetDiskFreeSpace Lib "kernel32" _

        Alias "GetDiskFreeSpaceA" ( _

        ByVal lpRootPathName As String, _

        lpSectorsPerCluster As Long, _

        lpBytesPerSector As Long, _

        lpNumberOfFreeClusters As Long, _

        lpTotalNumberOfClusters As Long) As Long

#Else

    Declare Function GetDiskFreeSpace Lib "kernel32" _

        Alias "GetDiskFreeSpaceA" ( _

        ByVal lpRootPathName As String, _

        lpSectorsPerCluster As Long, _

        lpBytesPerSector As Long, _

        lpNumberOfFreeClusters As Long, _

        lpTotalNumberOfClusters As Long) As Long

#End If

GetDiskFreeSpace API Parameters

Declare PtrSafe Function GetDiskFreeSpace Lib "kernel32" _ Alias "GetDiskFreeSpaceA" ( _ ByVal lpRootPathName As String, _ lpSectorsPerCluster As Long, _ lpBytesPerSector As Long, _ lpNumberOfFreeClusters As Long, _ lpTotalNumberOfClusters As Long) As Long

1. lpRootPathName (String)

  • This is the drive name you want to check.

  • It must be a string ending with a backslash.
    Example:

    • "C:\" → checks the C drive

    • "D:\" → checks the D drive

  • If you pass vbNullString, it will use the current drive.

2. lpSectorsPerCluster (Long – Output)

  • The function fills this variable with the number of sectors per cluster on the drive.

  • A cluster is the smallest unit of disk storage allocation.

Example:
If lpSectorsPerCluster = 8 It means each cluster contains 8 sectors.

3. lpBytesPerSector (Long – Output)

  • This returns the number of bytes in each sector.

  • Typically, most drives use 512 bytes per sector, but newer drives may use 4096 bytes per sector.

4. lpNumberOfFreeClusters (Long – Output)

  • Returns the number of free clusters available on the drive.

  • This tells you how many “allocation units” are currently unused.

5. lpTotalNumberOfClusters (Long – Output)

  • Returns the total number of clusters on the drive (both free and used).

6. Return Value (Long)

  • If the function succeeds, it returns nonzero (1).

  • If it fails, it returns 0, and you can call Err.LastDllError to get more details.

How to Calculate Free Space

The free space (in bytes) can be calculated as:

Free Space = SectorsPerCluster × BytesPerSector × NumberOfFreeClusters

Similarly, total disk size can be calculated as:

Total Space = SectorsPerCluster × BytesPerSector × TotalNumberOfClusters

GetDiskFreeSpace API usage into a reusable VBA function.

We’ll wrap it into a function `GetDriveSpace()` that you can call with any drive letter, and it will return total space and free space in GB.

Step 1 – API Declaration

Keep this in a standard module (global):

#If VBA7 Then
    Declare PtrSafe Function GetDiskFreeSpace Lib "kernel32" _
        Alias "GetDiskFreeSpaceA" ( _
        ByVal lpRootPathName As String, _
        lpSectorsPerCluster As Long, _
        lpBytesPerSector As Long, _
        lpNumberOfFreeClusters As Long, _
        lpTotalNumberOfClusters As Long) As Long
#Else
    Declare Function GetDiskFreeSpace Lib "kernel32" _
        Alias "GetDiskFreeSpaceA" ( _
        ByVal lpRootPathName As String, _
        lpSectorsPerCluster As Long, _
        lpBytesPerSector As Long, _
        lpNumberOfFreeClusters As Long, _
        lpTotalNumberOfClusters As Long) As Long
#End If

Step 2 – Reusable Function

We’ll create a function that takes a drive letter (like "C:\" or "D:\") and returns total space and free space.

The DiskFreeSpace Wrapper Function.

We have created a wrapper function, DiskFreeSpace, that calls the Windows API GetDiskFreeSpace Function, to retrieve and calculate the free space on the disk. The disk or computer memory capacity-related quantitative terms that we normally use are Gigabytes, Megabytes, and Kilobytes for communicating Capacity. So, we need to convert the cluster values into these measurements.

  1. Create a new Standard Module in your Database.

  2. Copy and paste the Windows API Code into the global declaration area of the Module.

    The DiskFreeSpace Function Code.

  3. Next, copy and paste the following User function code below the Windows API code in the same Module:

    Private SectorPerCluster As Long
    Private BytesPerSector As Long
    Private FreeClusters As Long
    Private TotalClusters As Long
    
    Private gbf As Double
    Private mbf As Double
    Private kbf As Double
    
    Private tb As Double
    Private gb As Double
    Private mb As Double
    Private kb As Double
    Private fmt As String
    Private msg As String, msg2 As String
    
    Public Function DiskFreeSpace(ByVal strPath As String) As String
    Dim Rtn As Long
    Dim ClusterBytes As Double
    
    On Error GoTo DiskFreeSpace_Err
    
    Rtn = GetDiskFreeSpace(strPath, SectorPerCluster, BytesPerSector, FreeClusters, TotalClusters)
    
    fmt = "#,##0"
    msg = ""
    msg2 = ""
    
    If Rtn Then
    
    'Bytes in a Cluster = Sectors * BytesPerSector
        ClusterBytes = SectorPerCluster * BytesPerSector 'Bytes per cluster
        
    'msg2 = "        Disk Drive: " & UCase(strPath) & vbCrLf & _
            "Sector Per Cluster: " & SectorPerCluster & vbCrLf & _
            "  Bytes Per Sector: " & BytesPerSector & vbCrLf & _
            "     Free Clusters: " & FreeClusters & vbCrLf & _
            "    Total Clusters: " & TotalClusters
            
    'Debug.Print msg2
    
        gbf = ClusterBytes / (1024# ^ 3) 'GB Factor per Cluster
        mbf = ClusterBytes / (1024# ^ 2) 'MB Factor     "
        kbf = ClusterBytes / (1024#)     'KB Factor     "
    
    'free Space
        tb = Int(TotalClusters * gbf) ' Total Space in Gigabytes
        gb = Int(FreeClusters * gbf)  ' Free Space  in     "
        mb = Int(FreeClusters * mbf)  '       "     in Megabytes
        kb = Int(FreeClusters * kbf)  '       "     in Kilobytes
    msg = " Disk Drive: " & UCase(strPath) & vbCrLf & _ "Total Space ( GB ): " & Format(tb, fmt) & vbCrLf & _ " Free Space ( GB ): " & Format(gb, fmt) & vbCrLf & _ " Free Space ( MB ): " & Format(mb, fmt) & vbCrLf & _ " Free Space ( KB ): " & Format(kb, fmt) Else MsgBox "Disk Drive PathName: " & UCase(strPath) & vbCrLf & _ "NOT FOUND!", vbOKOnly + vbCritical, "DiskFreeSpace()" DiskFreeSpace = "" Exit Function End If DiskFreeSpace = msg DiskFreeSpace_Exit: Exit Function DiskFreeSpace_Err: MsgBox Err & " : " & Err.Description, , "DiskFreeSpace()" DiskFreeSpace = "" Resume DiskFreeSpace_Exit End Function

Our new function needs only one parameter, the disk's Root Pathname.  If you look at the Windows API GetDiskFreeSpace Parameter declarations, the first Parameter is declared with ByVal qualification; other parameters are not qualified as such because they are declared as ByRef by default.

The first parameter can be passed directly, like "C:\" or a variable initialized with the disk Root Pathname.  Other parameter variables are declared as Long Integer Types in the Global declaration area. These variable References are passed to the Windows API, and the retrieved information is saved directly into those Variables.

All Variables except Rtn and ClusterBytes are declared in the global declaration area so that our own Function DiskFreeSpace Code looks better and less crowded.

The user-defined function DiskFreeSpace has only one parameter: the Root Pathname of the disk, like "C:\".  The GetDiskFreeSpace Windows API is called with all five parameters from Function DiskFreeSpace().

The Disk Space Value Conversion Calculations.

If the API call was successful, then the variable Rtn will have the Value 1; otherwise, 0.

So testing the variable Rtn is necessary to convert the disk information into Gigabytes, Megabytes, or Kilobytes.

When the API runs successfully, the returned values are in Bytes per Sector, Sectors per Cluster, Disk Free Space in Clusters, and the Disk's total capacity in Clusters.  

The disk capacity is logically grouped into Sectors of 512 Bytes (characters) and a group of 8 Sectors or more known as a Cluster. This is the amount of data the computer can read/write in one attempt. This may change depending on the type of disk drives, like Hard Disk, SSD, Zip Drive, or MicroSD Drive, and their formatting type: NTFS, FAT32, etc. 

The size of a Sector is 512 bytes on most common disk types, but the Sectors per cluster may change, like 8, 16, or 32.  You may test this function on your own machine with different Disk Types to find out.

We are familiar with terms like Gigabytes (GB), Megabytes (MB), Kilobytes (KB), which we normally use to communicate a disk's capacity. First, we will convert the Clusters to bytes, then bytes to GB, MB, or KB.

[Total Bytes per Cluster] = [Bytes Per Sector] * [Sectors Per Cluster] = 512 * 8 = 4096 bytes.

GBF = [Total Bytes per Cluster] / (1024#^3): Gigabytes Factor.

MBF = [Total Bytes per Cluster] / (1024#^2): Megabytes Factor.

KBF = [Total Bytes per Cluster] / (1024#): Kilobytes Factor.

With these values, we can easily convert the Free Cluster Values into any of the above three Values, like:

GB = [Free Space Clusters] * GBF will give the Disk Free Space value in Gigabytes.

Our Function DiskFreeSpace() returns a String Value containing information formatted in such a way that the returned value can be displayed in a MsgBox, printed in the Debug Window, or displayed in a Label Control on a Form that is wide enough to display 5 lines of text.

The sample output Image in the Debug Window and in the MsgBox side-by-side is given below for information:

The DiskFreeSpace() function can be run from the Debug Window, or called from a Function or from an Event Procedure.

The Demo Database is attached for Download and ready to run.



  1. MS-Access Class Module and VBA
  2. MS-Access VBA Class Object Arrays
  3. MS-Access Base Class and Derived Objects
  4. VBA Base Class and Derived Object-2
  5. Base Class and Derived Object Variants
  6. MS-Access Recordset and Class Module
  7. Access Class Module and Wrapper Classes
  8. Wrapper Class Functionality Transformation
Share:

Access And Windows API ShowWindow

Controlling Form and Report Views with Windows API

In Microsoft Access, forms and reports can normally be shown in four modes: Hidden, Minimized, Maximized, or Normal. While Access provides built-in commands (DoCmd.Maximize, DoCmd.Minimize, etc.), using Windows API functions gives much more direct control over the Access window itself.

A Past Encounter with Windows APIs

My first exposure to Windows API usage in MS Access was with a simple but striking visual effect: a form that fades in and then slowly disappears after a few seconds. That piece of VBA code is still with me today. At the time, I didn’t pursue API programming further — but it planted the seed of curiosity about what else could be done.

Revisiting APIs for Form Control

Recently, while exploring discussions about Access window handles, I revisited this subject. To control a form’s window state at the Windows API level, the window handle (hWnd) of the form must first be obtained. This handle is then passed to other API functions (such as ShowWindow) to manipulate the window’s display state.

The Limitation of FindWindow

The FindWindow() API function can locate a window handle by searching for the window’s class name and/or caption. However, in Access, there is an important limitation:

  • FindWindow only succeeds in locating forms if their Popup or Modal property is set to Yes.

  • For standard forms (Popup = No), FindWindow cannot directly retrieve the window handle.

This means that if we want to control a form’s state through APIs, the form must be configured as a Popup (or a Modal).

The ShowWindow API

Once the form’s handle is available, the ShowWindow() API function can be used to change its state. For example:

Const SW_HIDE As Long = &H0
Const SW_NORMAL As Long = &H1
Const SW_MINIMIZED As Long = &H2
Const SW_MAXIMIZED As Long = &H3
Const SW_CLOSE As Long = &H10 '16

By combining FindWindow (to get the handle) and ShowWindow (to set the state), We can achieve effects similar to Windows Explorer window management — but inside Access forms.

The Windows API Functions.

We will learn the usage of the following Windows API Functions in our trial runs to know how they work in MS Access:

Public Const SW_HIDE As Long = &H0
Public Const SW_NORMAL As Long = &H1
Public Const SW_MINIMIZED As Long = &H2
Public Const SW_MAXIMIZED As Long = &H3
Public Const SW_CLOSE As Long = &H10 '16

Public Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" _
    (ByVal lpClassName As String, ByVal lpWindowName As String) As Long


Public Declare PtrSafe Function ShowWindow Lib "user32.dll" ( _
     ByVal hwnd As Long, _
     ByVal nCmdShow As Long) As Long
    
Public Declare PtrSafe Function PostMessage Lib "user32" Alias "PostMessageA" _
    (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
    ByVal lParam As Long) As Long

Experiments in the VBA Debug Window.

Adding the API Declarations to Your Database.

  1. Open one of your Access databases.

  2. Create a new standard module (or open an existing one) and paste the API declaration code into the global declarations area at the top of the module.

  3. Save the module.

Note on 32-bit vs 64-bit Access.

  • If you are running 64-bit Microsoft Access, the API declarations must include the keyword. PtrSafe.

  • If your system is 32-bit, the PtrSafe keyword will cause errors. In that case, simply remove it PtrSafe from the declaration before saving the module.

  1. Open one of your existing Forms in Normal View.

  2. Note down the Title of the open Form.

  3. Come back to the VBA Window (Alt+F11).

  4. Select the Immediate Window (Ctrl+G) option from the View Menu.

  5. Now, type the following command (to run the FindWindow() API Function) directly in the Immediate Window to obtain the Windows Identification Number of the open Form.

 
? FindWindow(vbNullString,"Form1")
0

The FindWindow API requires two parameters:

  1. Class Name – A string that specifies the application’s window class. For Microsoft Access, this value is "OMain".

  2. Window Title – A string representing the exact title text of the window (for example, the caption of a form).

Although both parameters are mandatory, you only need to supply one value. The other parameter can be passed as a null string (vbNullString).

In the example above, we passed vbNullString the Class Name parameter and used the form’s title as the second parameter.

Important Limitation:
The FindWindow() Function will not locate the title of a form unless the form’s Popup property is set to Yes. If the form is not a pop-up, the function fails and returns 0.

  1. Now, open Form1 in Design View. 

  2. Open the Form's Property Sheet and change the Popup property value to Yes.

  3. Save the Form and open it in Normal View again.

  4. Try the above FindWindow command in the Debug Window one more time.

? FindWindow(vbNullString,"Form1")

Result: 197654

If the FindWindow() call succeeds, it returns a non-zero value — the unique window handle (hWnd) of the form. This means you’ve successfully identified the form’s window in memory.

Once you have this handle, you can call the ShowWindow() API to change the form’s view state. For example, you can test it directly from the Immediate (Debug) Window in the VBA editor:

? ShowWindow(hWnd, SW_MINIMIZE) ? ShowWindow(hWnd, SW_MAXIMIZE) ? ShowWindow(hWnd, SW_RESTORE) ? ShowWindow(hWnd, SW_HIDE) ? ShowWindow(hWnd, SW_SHOW)

Where:

  • SW_MINIMIZE (6) → Minimizes the form window.

  • SW_MAXIMIZE (3) → Maximizes the form window.

  • SW_RESTORE (9) → Restores the form to its previous size/state.

  • SW_HIDE (0) → Hides the form from view.

  • SW_SHOW (5) → Makes the form visible again if hidden.


x = ShowWindow(197654, SW_MINIMIZED)

Form1 is minimized and stationed at the bottom of the Application Window.

Application, Forms/Reports Windows Handle Property hwnd.

The MS Access Application Object has a property that holds the Access Application Window's handle.  From within the Access Application, we can read the Application Window's Handle value as shown below:

hwnd = Application.hwndAccessApp 
OR
hwnd = FindWindow("OMain",vbNullString)

We cannot make all Forms and Reports into Popups for the sake of the ShowWindow API or for any other.

The Built-in .hwnd Property.

Fortunately, we don’t need to rely on the Popup property or the FindWindow API to get the window handle of a Form or Report.

Every open Form or Report in Access already exposes its .hWnd property, which stores the window handle assigned by Windows. You can directly read this value and pass it to the ShowWindow API to change the window’s view mode to any of the supported constants (except SW_CLOSE).

Example:

  1. Open your form (e.g., Employees) in Normal View.

  2. Open the Immediate Window in the VBA editor (Alt+F11 → Ctrl+G).

  3. Type the following command to display the window handle of the Employees form:

? Forms!Employees.hWnd

This will print a numeric value — the window’s handle.

Once you have this value, you can call ShowWindow with one of the constants to minimize, maximize, restore, or hide the form.

hndl = Forms("Employees").hwnd 
? hndl
Result: 1508749

If you want to change the Employees form from its current Normal View to Minimized, call the ShowWindow API function and pass the form’s window handle (hWnd) along with the constant for minimizing.

From the Immediate Window in the VBA editor, type:

? ShowWindow(Forms!Employees.hWnd, SW_MINIMIZE)

This command tells Windows to minimize the Employees form directly.

whndl = Forms("Employees").hwnd
rtn = ShowWindow(whndl, SW_MINIMIZED)

OR

rtn = ShowWindow(Forms("Employees").hwnd,2)

Once you have the window handle (hWnd) of a Form or Report, you can experiment with the following ShowWindow options:

ConstantValueEffect on Window
SW_HIDE0Hides the window.
SW_SHOWNORMAL1Restores or shows the window in its normal state.
SW_SHOWMINIMIZED2Minimizes the window.
SW_SHOWMAXIMIZED3Maximizes the window.

Example in the Immediate Window:

? ShowWindow(Forms!Employees.hWnd, SW_SHOWMAXIMIZED)

This maximizes the Employees' form. You can replace SW_SHOWMAXIMIZED with any of the constants above to test other states.

Using it with Reports.

Reports also have the .hWnd property.
Open a Report in Print Preview mode and try:

? ShowWindow(Reports!SalesReport.hWnd, SW_MINIMIZE)

This will minimize the SalesReport window.

Closing a Window with PostMessage

To close a Form or Report programmatically using Windows messages, declare the PostMessage API and call it with the window handle and the WM_CLOSE message:

' Add to your declarations section Private Declare PtrSafe Function PostMessage Lib "user32" Alias "PostMessageA" _ (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As Long Private Const WM_CLOSE As Long = &H10

Then, to close an open Form (say Employees) using its handle:

Dim rtn As Long rtn = PostMessage(Forms!Employees.hWnd, WM_CLOSE, 0, 0)

This sends the WM_CLOSE message to the form’s window, effectively closing it — similar to clicking the X button.

The Sample Demo Database.

A Demo Database with a few sample Forms and Reports is attached for download.

The main form in the demo provides buttons to experiment with different window states (Hide, Normal, Minimize, Maximize) and to close Forms/Reports using the API functions described above.

Open the demo and try out the options:

  1. Select a Form or Report from the list.

  2. Click the desired action button (e.g., Minimize, Maximize).

  3. Observe how the API functions interact with the Access windows in real time.

This makes it easier to understand how Windows handles and how the ShowWindow / PostMessage APIs can be used to control Form and Report windows directly from VBA.

When the Main Form opens, both ListBoxes are initially empty.

  • Click the Open Forms command button to open the sample forms and load their names into the first ListBox.

  • The second ListBox is reserved for sample Reports. Only one of these ListBoxes will be active at a time.

At the top of the form, there is a button labeled “Click to Enable a Disabled ListBox.” Use this button to toggle access between the two ListBoxes.

Once you select an item (Form or Report) from the active ListBox, the Window State option buttons will become available. You can then choose the state you want to apply (Hide, Normal, Minimize, or Maximize).

Tip: If you switch to another Form or Report and want to apply the same window state, you must click the desired Window State option again to apply it.

Finally, the Close All command button will close all open Forms and Reports—including the Main Form itself.

  1. ActiveX ListView Control Tutorial-01.
  2. ListView Control Tutorial-02.
  3. Assigning Images To ListView Items.
  4. ListView Control Drag-Drop Sort Events
  5. ListView Control With MS-Access TreeView
  6. ListView Control With MS-Access TreeView
  7. TreeView/ListView Controls Drag-Drop Events
Share:

PRESENTATION: ACCESS USER GROUPS (EUROPE)

Translate

PageRank

Post Feed


Search

Popular Posts

Blog Archive

Powered by Blogger.

Labels

Forms Functions How Tos MS-Access Security Reports msaccess forms Animations msaccess animation Utilities msaccess controls Access and Internet MS-Access Scurity MS-Access and Internet External Links Queries Array Class Module msaccess reports Accesstips msaccess tips WithEvents Downloads Objects Menus and Toolbars MsaccessLinks Process Controls Art Work Collection Object Property msaccess How Tos Combo Boxes ListView Control Query VBA msaccessQuery Calculation Dictionary Object Event Graph Charts ImageList Control List Boxes TreeView Control Command Buttons Controls Data Emails and Alerts Form Custom Functions Custom Wizards DOS Commands Data Type Key Object Reference ms-access functions msaccess functions msaccess graphs msaccess reporttricks Command Button Report msaccess menus msaccessprocess security advanced Access Security Add Auto-Number Field Type Form Instances ImageList Item Macros Menus Nodes Recordset Top Values Variables msaccess email progressmeter Access2007 Copy Excel Expression Fields Join Methods Microsoft Numbering System RaiseEvent Records Security Split SubForm Table Tables Time Difference Utility WScript Workgroup Wrapper Classes database function msaccess wizards tutorial Access Emails and Alerts Access Fields Access How Tos Access Mail Merge Access2003 Accounting Year Action Animation Attachment Binary Numbers Bookmarks Budgeting ChDir Color Palette Common Controls Conditional Formatting Data Filtering Database Records Defining Pages Desktop Shortcuts Diagram Disk Dynamic Lookup Error Handler Export External Filter Formatting Groups Hexadecimal Numbers Import Labels List Logo Macro Mail Merge Main Form Memo Message Box Monitoring Octal Numbers Operating System Paste Primary-Key Product Rank Reading Remove Rich Text Sequence SetFocus Summary Tab-Page Union Query User Users Water-Mark Word automatically commands hyperlinks iSeries Date iif ms-access msaccess msaccess alerts pdf files reference restore switch text toolbar updating upload vba code