How to Pause Code Execution Until a Form or Report is Closed (Without Using acDialog)
The typical way to halt program execution until the user closes a form is to open the form with the acDialog option:
Debug.Print "About to open MyForm..."
DoCmd.OpenForm "MyForm", WindowMode:=acDialog
Debug.Print "The user closed MyForm..."
This is simple and reliable, but it also comes with a bunch of constraints:
- You can't interact with any other forms or reports until you close or hide the dialog form
- Any code that comes after the
OpenForm
line will not run until the form is closed - Other annoyances that I've bumped into over the years but can't recall right now (feel free to add yours in the comments below)
To work around these constraints, I created a simple procedure named WaitTilObjClosed.
The Approach
We sit in a loop checking for the existence of the object in question until it's no longer open. To keep the computer and application from becoming unresponsive while we sit in our infinite loop, we include calls to both the Sleep API and DoEvents.
The Algorithm
- Enter an infinite
Do...Loop
- Cede processor time to the rest of the application
- Cede processor time to the operating system
- Check to see if the form or report is closed
- If the form or report is closed, exit the loop
The Function
Here is the WaitTilObjClosed()
function on its own without its required dependencies.
Sub WaitTilObjClosed(ObjType As AcObjectType, ObjName As String)
Do
DoEvents
Sleep 1
If (SysCmd(acSysCmdGetObjectState, ObjType, ObjName) = 0) Then
DoEvents 'workaround for Access 2007 Escape key bug
Exit Do
End If
Loop
End Sub
The Full Code
The code below includes required helper functions that I've written about in the past:
I've also included a couple of additional convenience functions to make your calling code more readable:
- WaitTilReportClosed
- WaitTilFormClosed
The code can be copied and pasted into a blank standard module to get a fully-working solution that you can easily integrate into your projects:
Option Compare Database
Option Explicit
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
'---------------------------------------------------------------------------------------
' Procedure : WaitTilObjClosed
' Author : Mike Wolfe
' Date : 1/7/2009
' Source : https://nolongerset.com/waittilobjclosed/
' Purpose : Halts program execution until user closes object. User is generally
' unaffected by the loop.
' Requires : Sleep API sub
' 3/23/09 : Removed initial 1000 ms sleep; changed repeating 200 ms sleep to 1 ms;
' while Sleeping other programs can use processor but Access cannot;
' during DoEvents, other parts of Access can use processor;
' without the Sleep call, processor usage stays at 100% for MSACCESS.EXE
' with a long Sleep call, Access becomes noticeably sluggish
' 12/ 4/15 : Added a workaround for the Access 2007+ Escape key bug: https://social.msdn.microsoft.com/Forums/office/en-US/bb573ad0-7cac-499e-afc9-f9d2ab19cb76/bug-in-access-2007-ltescgt-raises-runtime-error-3059?forum=accessdev#3cbe9f1d-c890-44bc-b0ac-b70405023d34
'---------------------------------------------------------------------------------------
'
Sub WaitTilObjClosed(ObjType As AcObjectType, ObjName As String)
Do
DoEvents
Sleep 1
If (SysCmd(acSysCmdGetObjectState, ObjType, ObjName) = 0) Then
DoEvents 'workaround for Access 2007 Escape key bug
Exit Do
End If
Loop
End Sub
'---------------------------------------------------------------------------------------
' Procedure : WaitTilReportClosed
' Author : Mike Wolfe
' Source : https://nolongerset.com/waittilobjclosed/
' Purpose : Halts program execution until user closes report. User can still print
' report, use other programs, and generally be unaffected by the loop.
' Requires : WaitTilObjClosed routine
'---------------------------------------------------------------------------------------
'
Sub WaitTilReportClosed(RptName As String)
WaitTilObjClosed acReport, RptName
End Sub
'---------------------------------------------------------------------------------------
' Procedure : WaitTilFormClosed
' Author : Mike Wolfe
' Source : https://nolongerset.com/waittilobjclosed/
' Purpose : Halts program execution until user closes form. Allows resizing and moving
' a form, operations that aren't available when opening form in acDialog mode.
' Requires : WaitTilObjClosed routine
'---------------------------------------------------------------------------------------
'
Sub WaitTilFormClosed(FormName As String)
WaitTilObjClosed acForm, FormName
End Sub
Sample Usage
Debug.Print "About to open MyForm..."
DoCmd.OpenForm "MyForm"
Debug.Print "MyForm has been opened..."
WaitTilFormClosed "MyForm"
Debug.Print "The user closed MyForm..."
Cover image created with Microsoft Designer