Logo

MS Access FAQ - Modules

Logo

Table of Contents (15 Questions)

Bullet Waiting for form to Close
Bullet Error Numbers & their text
Bullet Refrencing Subform Objects
Bullet Record X of Y
Bullet Running Dos Internal Commands
Bullet Waiting for an external process to finish
Bullet The Confirmation Dialog
Bullet Playing Sound files
Bullet Proper Case
Bullet Domain functions slow (replacements here)
Bullet Getting Network User Name
Bullet Open drop down of a combo box
Bullet Control Name under Mouse Pointer
Bullet Getting Network User Name (32 bit)
Bullet Trapping ODBC Errors


Q How can I open a form or report in code and make the code wait until that form or report has been closed..
Back to Top
A The simple answer for forms is to open the form as a modal dialog box, like this:

DoCmd OpenForm "frmFormName", , , , , A_DIALOG

Note the commas, the A_DIALOG parameter is the fifth parameter.

Of course, this will not work for reports in preview mode, and may not be the desired result for a particular form, some polling will be called for, e.g.
DoCmd OpenForm "frmFormName"
Do While IsFormLoaded("frmFormName")
  DoEvents
Loop

DoCmd OpenReport "rptReportName"
Do While IsReportLoaded("rptReportName")
  DoEvents
Loop

Then in a global module, the following functions will test if a particular form or report is loaded:

Function IsFormLoaded(pstrFormName As String) As Integer
  IsFormLoaded = SysCmd(SYSCMD_GETOBJECTSTATE, A_FORM, pstrFormName)
End Function

Function IsReportLoaded(pstrReportName As String) As Integer
  IsReportLoaded = SysCmd(SYSCMD_GETOBJECTSTATE, A_REPORT, pstrReportName)
End Function

Updated: 04/11/1996 - Trevor Best


Back to Top
Q Where can I obtain a list of Access Basic Errors and their associated messages
Back to Top
A All Versions: From Access itself, simply write a function that loops around each error number, e.g.
For e = 1 to 9000
  Debug.Print e; " - "; Error(e)
Next

Bear in mind a lot of these are "User Defined Error" or "Reserved Error", you may want to filter these out with an "If" or "Select Case" structure in your function, alternatively you can write each number & message to a text file or Access table.

Updated: 21/12/1997 - Trevor Best


Back to Top
Q How do I access the recordset of a SUBform. I know how to clone the set of a form, but this doesn't work for subforms.
Back to Top
A All Versions: Let me guess, you tried: forms!myform!MySubForm.RecordsetClone

This will always fail as MySubForm points to a control on the form, you need to access it's Form propery, thus:

forms!myform!MySubForm.Form.RecordsetClone

Should work OK.

Updated: 21/12/1997 - Trevor Best


Back to Top
Q How do I emulate the "Record X of Y" that Access displays in the navigation buttons
Back to Top
A Access 2.0: The following function will do this for a form, just send any string and the form object as the parameters,
e.g. in a ControlSource use

=RecordNumber("Item",[Form])

For code, use:
strVariable = RecordNumber("Item",Me)
or
strVariable = RecordNumber("Item",Forms!MyForm)

This will return something like "Item 4 of 899", if the form is on a new record it will return the string "New Record".
------- begin cut here -----------
Function RecordNumber (pstrPreFix As String, pfrm As Form) As String
    On Error GoTo RecordNumber_Err
    Dim rst As Recordset
    Dim lngNumRecords As Long
    Dim lngCurrentRecord As Long
    Dim strTmp As String
    
    Set rst = pfrm.recordsetclone
    rst.MoveLast
    rst.bookmark = pfrm.bookmark
    lngNumRecords = rst.recordcount
    lngCurrentRecord = rst.absoluteposition + 1
    strTmp = pstrPreFix & " " & lngCurrentRecord & " of " & lngNumRecords
RecordNumber_Exit:
    On Error Resume Next
    RecordNumber = strTmp
    rst.Close
    Set rst = Nothing
    Exit Function
RecordNumber_Err:
    Select Case Err
        Case 3021
            strTmp = "New Record"
            Resume RecordNumber_Exit
        Case Else
            strTmp = "#" & Error
            Resume RecordNumber_Exit
    End Select
End Function

------- end cut here -----------

Access 97: Same but for the fact that the trap for new record doesn't work so it displays "Record 0 of xxx", I haven't investigated this yet but I would assume that checking for rst.AbsolutePosition returning -1 could prove fruitful, then again you might like the Record 0 approach.

Updated: 21/12/1997 - Trevor Best


Back to Top
Q How do I run an internal DOS command from within Access Basic, if I try x=Shell("copy file1 file2",1) I get a message "File not found".
Back to Top
A Dos internal commands are processed by the command processor shell so you should be calling this program and not trying to execute the internal command itself.

The command processor is usually command.com but this can vary and so can it's location (indeed it can even reside outside of the dos search path) for this reason it's not a good idea to hard code it's location or name into an application as this may change if the application is moved to another pc. Use the Environ function to retrieve the %COMSPEC% environment variable as this always points to the current command processor shell. e.g.

Dim hTask As Integer
hTask = Shell(Environ("COMSPEC") & " /C COPY file1 file2", 1)

Note: Some of the DOS internal commands are available in Access Basic (in the long form of the command), e.g. RmDir, ChDir, MkDir. The Dir command can be emulated using the Dir() function although this only returns files, not hidden/system files or directory names.

Updated: 21/07/1996 - Trevor Best


Back to Top
Q Now I know how to shell an internal command such as copy, I need to wait for it to finish before continuing, at present Access will carry on to the next statement, which needs to access the new file that's just been copied.
Back to Top
A Access 2.0: This is usual for a multitasking environment such as Windows, however as "Shell" is a function in Access Basic it returns the handle to the task (or module) that it just started so you can use this return value to see when it's finished, you'll need an API call for this.

(declarations)
Declare Function GetModuleUsage Lib "Kernel" (ByVal hModule As Integer) As Integer

(somewhere within a module)
Dim hModule as Integer

hModule = Shell("whatever.exe",1)
Do While GetModuleUsage(hModule)
  DoEvents
Loop
DoCmd Echo True, "This message will only come up when whatever.exe has finished"

--------------
The DoEvents within the loop ensures that Access relinquishes processor time to the called program otherwise it could wait forever.

Access 97: GetModuleUsage was revoked from the Win32 API as programs would run in their own address space. I haven't as yet found a suitable replacement for this. Perhaps some other propeller head can enlighten me?

Updated: 22/12/1997 - Trevor Best


Back to Top
Q How do I stop the message box warnings issued whenever I run an action query with DoCmd RunSQL or DoCmd OpenQuery or from a macro?
Back to Top
A In a macro, SetWarnings: Off
In Code, "DoCmd SetWarnings False".

It's worth noting that you should turn the warnings back on afterwards as this applies to just about everything, even saving a form in design mode so it won't let you abandon it if you wanted to.

Another thing in Code, you can use the .Execute method on a QueryDef object or DataBase Object, this has the advantages of being able to run inside a transaction and so execute faster, "DoCmd" starts another session of Jet (the database engine in Access) so will execute outside of the current default workspace, therefore running independantly of any transactions and you can't rollback from them.

Updated: 21/07/1996 - Trevor Best


Back to Top
Q How do I play a (WAV) sound file?
Back to Top
A If your computer is equipped with a sound card or a speaker driver, you can play .WAV (waveforms). First, add the following declaration (all on one line) to the Declarations section:

Access 2.0
Declare Function csvSndPlaySound Lib "MMSYSTEM.DLL" Alias _
"SndPlaySound" (ByVal FileName As String, ByVal How As _
Integer) As Integer

Next, enter this funciton:

Function csvPlayWave (ByVal FileName As String, ByVal How As _
Integer) As Integer
csvPlayWave = csvSndPlaySound(Trim(FileName), How)
End Function

You can call this from a form or field event like so:

=csvPlayWave("c:\windows\tada.wav", 1)

Access 97
Declare Function PlaySound Lib "winmm.dll" Alias "PlaySoundA" _
(ByVal lpszName As String, ByVal hModule As Long, _
ByVal dwFlags As Long) As Long
Function csvPlayWave (ByVal FileName As String, ByVal How As _
Integer) As Integer
' I have no idea what the hModule bit is, but zero
' works fine here.
csvPlayWave = PlaySound(Trim(FileName),0, How)
End Function

Updated: 22/12/1997 - Chris St. Valentine


Back to Top
Q How do I captitalise the first letter of each word (proper case)
Back to Top
A
Function Proper (var As Variant) As Variant
' Purpose: Convert the case of var so that the first letter of each word capitalized.

   Dim strV As String, intChar As Integer, i As Integer
   Dim fWasSpace As Integer    'Flag: was previous char a space?
   
   If IsNull(var) Then Exit Function
   strV = var
   fWasSpace = True              'Initialize to capitalize first letter.
   For i = 1 To Len(strV)
      intChar = Asc(Mid$(strV, i, 1))
      Select Case intChar
      Case 65 To 90              ' A to Z
         If Not fWasSpace Then Mid$(strV, i, 1) = Chr$(intChar Or &H20)
      Case 97 To 122             ' a to z
         If fWasSpace Then Mid$(strV, i, 1) = Chr$(intChar And &HDF)
      End Select
      fWasSpace = (intChar = 32)
   Next
   Proper = strV
End Function

Updated: 04/11/1996 - Allen Browne


Back to Top
Q Why are the Domain (DLookup(), DCount(), etc.) so slow
Back to Top
A I don't know to be honest, but download this file, unzip it and stick it in a module, replace 'DLookup' with 'tLookup', 'DCount' with 'tCount', etc. and Bob's yer uncle.

I've not yet tested these in Access 97, they do work in 97 but I haven't tested them in terms of speed over 97's built in domain functions. I would however go as far to make the third argument option in 97 to be more in line with the domain functions, this wasn't possible in version 2.0 so you have to supply the third argument (the where clause without the word "where"), even if you just send a zero length string.

Updated: 22/12/1997 - Trevor Best


Back to Top
Q How do I get the Network User ID of the current user
Back to Top
A A simple API function will do for this, I've tested this on a Windows network but not yet on a Novell network.

Access 2.0
Declare Function WNetGetUser Lib "user" (ByVal szUser As String, nBufferSize As Integer) As Integer
Function NetworkUserName () As String

   Dim iStringLength As Integer
   Dim sString As String * 255
   
   iStringLength = Len(sString)
   sString = String$(iStringLength, 0)

   If WNetGetUser(sString, iStringLength) = 0 Then
       NetworkUserName = Left$(sString, iStringLength)
   Else
       NetworkUserName = "Unknown"
   End If

End Function


Access 97
See Below as the credit goes to a different author.

Updated: 22/12/1997 - Steven Drinovsky


Back to Top
Q How do I make a combo box drop down as soon as the user puts the cursor in there.
Back to Top
A Access 2.0 Use an On Enter event procedure like:


Sub cboSupplier_OnEnter()
Sendkeys "{F4}"
' or you can use Sendkeys "%{Down}"
End Sub


Beware when doing this OnEnter, as if the users first action on this combo before entering it is to click the down arrow with the mouse then your code will actually have the opposite effect as the F4 or Alt+Down is a toggle.

Access 97 Combo Boxes have a .Dropdown method.

Updated: 22/12/1997 - Trevor Best


Back to Top
Q How Can I get the name of the control that the mouse pointer passes over, Screen.ActiveControl doesn't work as users have a habit of moving the mouse over other objects on the screen apart from the active one (the awkward so and so's :-)
Back to Top
A This'll take some coding, you'll need two procedures, one to get the name of the control, and one to blank it out when the mouse moves over a blank part of the screen. In a global module, create a global variable and....

(Declarations)
Option Explicit

Global gctlUnderMouse As Control

Function SetCtlUnderMouse(pctl As Control)
  Set gctlUnderMouse = pctl
End Function


Now, in the form, you need to set the MouseMove property of each control, for the background (Form, detail, header, footer) use:

=SetCtlUnderMouse(Nothing)

This will set the global variable to nothing, indicating that the mouse is not hoverig over a control that you're intrested in.

For Controls that you're intrested in use:

=SetCtlUnderMouse([ControlName])

Where controlName is the name of the control, of course this would be ultra tedious doing this in one form, let alone every form in the database so here's a bit of code that will do it for you, just bring up the form in design mode, select the objects you want to have this done to using [Shift] + Click, then open the code window, and in the immediate window type:

CreateSetCtlUnderMouse Me

Here's the code that will run (in a global module):
Sub CreateSetCtlUnderMouse(pfrm As Form)
  Dim ctl As Control, i As Integer
  For i = 0 to pfrm.Count - 1
    Set ctl = pfrm(i)
    If ctl.InSelection Then
      ctl.OnMouseMove = "=SetCtlUnderMouse([" & ctl.Name & "])"
    End If
    Set ctl = Nothing
  Next
End Sub

Updated: 16/11/1997 - Trevor Best


Back to Top
Q How do I get the Network User ID of the current user in the 32 bit version
Back to Top
A Same as for the 16 bit version except the declaration of the API call is different. Also like all 32 bit ports of 16 bit code, Integers are now Long Integers.

Declare Function wu_GetUserName Lib "advapi32" Alias "GetUserNameA" _
                  (ByVal lpBuffer As String, nSize As Long) As Long

Function NetworkUserName () As String

   Dim lngStringLength As Long
   Dim sString As String * 255
   
   lngStringLength = Len(sString)
   sString = String$(lngStringLength, 0)

   If wu_GetUserName(sString, lngStringLength) Then
       NetworkUserName = Left$(sString, lngStringLength)
   Else
       NetworkUserName = "Unknown"
   End If

End Function

Updated: 22/12/1997 - Christopher Schmidt


Back to Top
Q How can I trap a ODBC Error in VBA? All the VBA Err object returns is "ODBC Call Failed"
Back to Top
A Use the DbEngine.Errors collection

Sub Update_TempLib()

    On Error GoTo ErrorTrap
' Executing connect code at this point
    
Exit_errortrap:
    Exit Sub

ErrorTrap:

    Dim myerror As Error
    For Each myerror In DBEngine.Errors
    With myerror
        If .Number <> 3146 Then
        MsgBox .Description
        End If
    End With
    Next
    
    Resume Exit_errortrap
            
End Sub


Updated: 07/05/1998 - Sol Shapiro


Back to Top