this time i need everyone's help to resolve this issue.
I have one splited database and front end and it's used by 150 users. My problem is when the users updating through front end its size is keep on increasing and the back end is increasing slowly. So it's creating latency. I'm connected the backend through linked tables. Please suggest a solution to reduce the size of front end. If I'm doing a compact then its working perfectly.
You can do a Compact on Close anytime you close the DB. It's super-easy; just check the box# As I know, there really isn't a specific time to perform a C/R, but a regular schedule is good thing to set up, whether it be once a day, once a week, or once a month.
Here is a jazzed-up version of the compact on close, which you can run on a regular basis, using Windows Task Scheduler. You are basically controlling one DB from another DB. I used to run these processes overnight at a former consulting job I had. It worked fine for more than 1 year.
Option Compare Database
Option Explicit
' Declare an enumeration of long integer
' constants, to be used as the return values
' for the RepairDatabase() function.
' As Access's CompactRepair() method returns
' TRUE or FALSE, the Enum uses -1 (TRUE) for
' success and 0 for failure.
Public Enum ryCompactResult
cmpCompactSuccessful = -1
cmpCompactFailed = 0
cmpErrorOccurred = 1
cmpSourceFileDoesNotExist = 2
cmpInvalidSourceFileNameExtension = 3
cmpDestinationFileExists = 4
End Enum
Private Sub TestRepair()
Dim strSource As String
Dim strDestination As String
Dim lngRetVal As ryCompactResult
strSource = "C:\MyFolder\db1.mdb"
strDestination = "C:\MyFolder\db2.mdb"
' Call the function:
lngRetVal = RepairDatabase(strSource, strDestination)
' Examine the return value from the function
' and display appropriate message:
Select Case lngRetVal
Case cmpCompactSuccessful
MsgBox "Compact & repair successful.", _
vbOKOnly + vbInformation, _
"Program Information"
Case cmpSourceFileDoesNotExist
MsgBox strSource & vbNewLine & vbNewLine _
& "The above file does not exist.", _
vbOKOnly + vbExclamation, _
"Program Finished"
Case cmpInvalidSourceFileNameExtension
MsgBox strSource & vbNewLine & vbNewLine _
& "The above file has an invalid filename " _
& "extension.", vbOKOnly + vbExclamation, _
"Program Finished"
Case cmpDestinationFileExists
MsgBox strDestination & vbNewLine & vbNewLine _
& "The above destination file exists. " _
& vbNewLine _
& "Please delete the above file or " _
& "use a different destination filename.", _
vbOKOnly + vbExclamation, "Program Finished"
Case cmpErrorOccurred
' The RepairDatabase() function has
' already displayed an error message.
End Select
End Sub
Function RepairDatabase( _
strSource As String, _
strDestination As String) As ryCompactResult
' IN:
'
' strSource:
' The full path to the database that is
' to be compacted.
'
' strDestination:
' The full path to the resultant database
' after strSource has been compacted.
'
' OUT:
'
' This function returns one of the values in
' the ryCompactResult Enum.
Dim lngRetVal As ryCompactResult
Dim strFileName As String
Dim strFileNameExtn As String
Dim lngPos As Long
On Error GoTo Error_RepairDatabase
' See if source file exists:
strFileName = Dir(strSource)
If Len(strFileName) = 0 Then
lngRetVal = cmpSourceFileDoesNotExist
GoTo Exit_RepairDatabase
End If
' See if source filename has appropriate
' filename extension (mdb or accdb).
' First, see if filename contains a period:
lngPos = InStr(strFileName, ".")
If lngPos = 0 Then
' Period not found in filename;
' i.e. no filename extension found.
lngRetVal = cmpInvalidSourceFileNameExtension
GoTo Exit_RepairDatabase
Else
' Get filename extension:
strFileNameExtn = Mid(strFileName, lngPos + 1)
strFileNameExtn = LCase(strFileNameExtn)
Select Case strFileNameExtn
Case "mdb", "accdb"
' Correct filename extension found.
' We can proceed with compact & repair.
Case Else
' Invalid filename extension found.
lngRetVal = cmpInvalidSourceFileNameExtension
GoTo Exit_RepairDatabase
End Select
End If
' Destination file must not exist:
strFileName = Dir(strDestination)
If Len(strFileName) > 0 Then
lngRetVal = cmpDestinationFileExists
GoTo Exit_RepairDatabase
End If
' Compact and repair database:
lngRetVal = Application.CompactRepair( _
strSource, strDestination, True)
Exit_RepairDatabase:
RepairDatabase = lngRetVal
Exit Function
Error_RepairDatabase:
lngRetVal = cmpErrorOccurred
MsgBox "Error No: " & Err.Number _
& vbNewLine & vbNewLine _
& Err.Description, _
vbOKOnly + vbExclamation, _
"Error Information"
Resume Exit_RepairDatabase
End Function
***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** *****
Compact / Repair function below, but not advisable to do arbitrarily on every close - just replace/remove my on error code with your own
Function RepairDatabase(strSource As String, _
strDestination As String) As Boolean
' Input values: the paths and file names of
' the source and destination files.
Dim strSource As String
Dim strDestination As String
strSource = "\\Dg\Debt \2010\Summary\Summary.mdb"
strDestination = "\\Dg\Debt \2010\Summary\Summary_Compact.mdb"
' Trap for errors.
On Error GoTo ErrorRoutine
' Compact and repair the database. Use the return value of
' the CompactRepair method to determine if the file was
' successfully compacted.
RepairDatabase = _
Application.CompactRepair( _
LogFile:=True, _
SourceFile:=strSource, _
DestinationFile:=strDestination)
' Reset the error trap and exit the function.
On Error GoTo 0
Exit Function
' Return False if an error occurs.
Exit_Function:
Exit Function
ErrorRoutine:
RepairDatabase = False
Call LogError(Err.Number, Err.Description, conMod & ".RepairDatabase", , True)
Resume Exit_Function
End Function
Call the function as such:
Call RepairDatabase(strSource, strDestination)
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I don’t think you can run the compact/repair code in DB#2, which is the DB that I want to do the operation on. So, in DB#1, I tried this behind a Form:
Private Sub Form_Load()
Call RepairDatabase(strSource, strDestination)
End Sub
I put this in a module:
'Compact & Repair
Function RepairDatabase(strSource As String, strDestination As String) As Boolean
Dim strSource As String
Dim strDestination As String
strSource = "\\Dg\Debt \2010\Summary\Summary.mdb"
strDestination = "\\Dg\Debt \2010\Summary\Summary_Compact.mdb"
' Trap for errors.
On Error GoTo ErrorRoutine
' Compact and repair the database. Use the return value of
' the CompactRepair method to determine if the file was
' successfully compacted.
RepairDatabase = _
Application.CompactRepair( _
LogFile:=True, _
SourceFile:=strSource, _
DestinationFile:=strDestination)
' Reset the error trap and exit the function.
On Error GoTo 0
Exit Function
' Return False if an error occurs.
Exit_Function:
Exit Function
ErrorRoutine:
RepairDatabase = False
Call LogError(Err.Number, Err.Description, conMod & ".RepairDatabase", , True)
Resume Exit_Function
End Function
Here is a good resource for you to read through when you have time.
http://www.databasedev.co.uk/compacting-and-repairing-ms-access.html
Related
If I have the following code,
x = 4
y = x / 0
Err.Description will return "Division by zero" but is there a way to return "y = x / 0"?
You could have a string variable and before every line of code, you push VBProject.VBComponents("Module1").CodeModule.Lines(...) into the variable. Then, when the error occurs, you have the exact line of code that caused the error stored in the variable!
Throw in a custom error handler and you can msgbox it right to the user's screen!
Sub test()
Dim ErrLine As String
On Error GoTo ErrHand
Dim x As Long, y As Long
ErrLine = ThisWorkbook.VBProject.VBComponents("Module1").CodeModule.Lines(6, 1)
x = 4
ErrLine = ThisWorkbook.VBProject.VBComponents("Module1").CodeModule.Lines(8, 1)
y = x / 0
Exit Sub
ErrHand:
MsgBox ErrLine 'Shows "y = x / 0"
End Sub
This is of course, very tedious and prone to error. It depends on constants for LineStart in the Lines property and if the code is updated or changed, the line numbers may shift and break the whole thing, meaning hundreds of these constants might need to be updated.
And it is mostly pointless, since the user can press Debug in the default error messagebox and it will open the VBProject and bring them right to the line that caused the error.
So yes, it is possible, but it has more cons than pros.
You didn't say what version of VBA you are using, so here's what I use in Access 2010 VBA (Office 2010). It probably works for Access 2007 VBA, and above (any .accdb extension).
This post includes the following functions:
SearchModule - Search a VBA module for the specified text and return the line found. Useful for finding numbered lines and returning the text of the line.
ErrorMsgBox - Display a preformatted error message in an ErrorHandler routine that can look up the actual line from the module/procedure specified. All that's needed is to pass the Err object, Erl, and module name, and it does the rest. All other arguments are optional. Actually, ALL arguments are optional, but you get a lesser message. Requires SearchModule().
MyFunction - A function showing how SearchModule() can be used. Requires SearchModule().
MyFunction2 - A function showing how ErrorMsgBox() can be used. It's a modified version of MyFunction(). Requires SearchModule() and ErrorMsgBox().
Copy/Paste the following code into a module called modModuleFunctions, and it should run as expected (in Access 2010, at least).
Scroll down and do the same for the test functions.
IMPORTANT: See note at the very end of this post about an MSAccess bug discovered.
Option Compare Database
Option Explicit
' Set this to the name of the module that contains this code.
Const MOD_NAME = "modModuleFunctions"
Function SearchModule( _
sModName As String, _
Optional sProcName As String = "", _
Optional sSearchText As String = "", _
Optional sProcType As String = "", _
Optional bStripLineNumbers As Boolean = True, _
Optional bStripComments As Boolean = True, _
Optional bQuiet As Boolean = False _
) As Variant
' Purpose:
' Search sProcName procedure in sModName module
' for sSearchText and return the line of text found.
' Line numbers and comments may be stripped.
'
' Arguments:
' sModName - The name of the module to be searched.
' Required.
'
' sProcName - The name of the procedure to be searched.
' If not specified, then the entire module is searched,
' and SearchModule() sets this argument to the procedure
' containing the text found.
' Optional.
'
' sSearchText - The text to be searched for. Returns Null if not specified.
' Optional (sort of). (See Note 5)
'
' sProcType - The type of procedure found.
' Pass an empty string variable, and SearchModule()
' will set it to the type of procedure that contains
' the text that was found.
' Pass a string into sProcType, and SearchModule()
' leaves it unchanged.
' Optional.
'
' bStripLineNumbers - Do not return line numbers in the returned text.
' Ignored if lines do not start with
' a numeric value followed by a space.
' Optional. Default = True.
'
' bStripComments - Strips any comments from the end of the line.
' Optional. Default = True.
'
' bQuiet - Suppresses error messages from this function.
' Optional. Default = False.
'
' Returns:
' 1. The line of text found, modified by 2 optional arguments.
' 2. Returns Null if sSearchText is NOT specified,
' an error occurs, or the text was not found.
'
' Notes:
' 1. If you are expecting to find unique line numbers,
' as reported by Erl (error line function), you must
' manually insert unique numbers followed by a space,
' at the beginning of the appropriate line(s).
'
' 2. Not all lines need to be numbered, only the ones
' that may be trouble spots). No need to number lines
' that cannot fail.
'
' IMPORTANT: If a line that fails is not numbered, and
' others before it are, VBA sets Erl to the last line number
' read before the failed line, giving you the wrong error line.
' Be sure to number ALL lines that are potential trouble spots,
' else you'll have even MORE trouble debugging your code.
'
' 3. Make sure the line numbers used appear nowhere else
' in your code, or else you may receive unexpected matches.
'
' 4. Any integers may be used.
' Line numbers should be in ascending order,
' else, Erl may not return the right line.
' Often it DOES return the right line number,
' even if out of order, but NOT always.
'
' 5. The Module.Find method (used by this, the SearchModule()
' function) requires the sSearchText argument to be specified.
' So if sSearchText argument is not specified, Module.Find
' would fail. Thus, it is required, but defined as Optional
' in the arguments to this function.
On Error GoTo SearchModule_ERROR
Dim mMod As Module
Dim lStartLine As Long, lEndLine As Long
Dim lStartCol As Long, lEndCol As Long
Dim sLineFound As String
Dim vTmp As Variant
DoCmd.OpenModule sModName
Set mMod = Modules(sModName)
If sProcName = "" Then
lStartLine = 1
lEndLine = mMod.CountOfLines - mMod.CountOfDeclarationLines
Else
lStartLine = mMod.ProcBodyLine(sProcName, vbext_pk_Proc)
lEndLine = lStartLine + mMod.ProcCountLines(sProcName, vbext_pk_Proc)
End If
SearchModule = Null ' Failure or not found returns Null.
If mMod.Find(sSearchText, lStartLine, lStartCol, lEndLine, lEndCol) Then
sLineFound = mMod.Lines(lStartLine, lEndLine - lStartLine + 1)
If sProcType = "" Then ' Extract the type of procedure.
sProcName = mMod.ProcOfLine(lStartLine, vbext_pk_Proc)
lStartLine = mMod.ProcBodyLine(sProcName, vbext_pk_Proc)
sProcType = mMod.Lines(lStartLine, 1)
sProcType = Split(sProcType, "(", 2)(0) ' Remove arguments from declaration.
sProcType = "(" & Left(sProcType, InStrRev(sProcType, " ") - 1) & ")" ' Get type.
End If
If bStripLineNumbers Then
vTmp = Split(sLineFound, " ", 2)
If IsNumeric(vTmp(0)) Then sLineFound = Trim(vTmp(1))
End If
If bStripComments Then sLineFound = Trim(Split(sLineFound, "'")(0))
SearchModule = Trim(sLineFound)
End If
SearchModule_EXIT:
Exit Function
SearchModule_ERROR:
If Not bQuiet Then
MsgBox "Error " & Err & " occurred in SearchModule function." & vbCrLf & vbCrLf & _
Error, vbExclamation, "SearchModule() Error"
End If
Resume SearchModule_EXIT
End Function
Function ErrorMsgBox( _
Optional sAdditionalMessage As String = "", _
Optional oErr As ErrObject = Nothing, _
Optional ByVal sErrLine As String = "0", _
Optional ByVal sModName As String = "", _
Optional ByVal sProcName As String = "", _
Optional ByVal vbButtonsAndIcon As VbMsgBoxStyle = vbExclamation, _
Optional sTitle As String = "Error occurred", _
Optional bStripLineNumbers As Boolean = True, _
Optional bStripComments As Boolean = True, _
Optional bQuiet As Boolean = False _
) As Integer
Const CR2 = vbCrLf & vbCrLf ' Double up the line feeds.
Dim sErrDescr As String
Dim sErrNum As String
Dim vErrorLine As Variant
Dim sMsg As String
Dim sProcType As String ' The type of procedure to be displayed in the message.
' Purpose:
'
' Display a preformatted error message in an ErrorHandler routine,
' that looks up the actual line from the module/procedure specified.
'
' Arguments:
'
' sAdditionalMessage - An additional message to be appended to the
' auto-generated message.
' Optional. Default = ""
'
' oErr - The Err object. Just pass Err to this argument
' from any code.
' Optional. Default = Nothing
'
' sErrLine - The error line. Just pass Erl to this argument
' from any code.
' Optional. This is only needed if you want to extract
' a line from a module. If so, you must also
' include at least the sModName argument.
' Default = "0"
'
' sModName - The name of the module to be searched for the line
' that failed (sErrLine).
' Optional, unless sProcName is specified, which is
' contained in sModName.
' Default = ""
'
' sProcName - The name of the procedure to be searched for the line
' that failed (sErrLine). If not specified, then sModName
' module is searched.
' Optional. Default = ""
'
' vbButtonsAndIcon - This controls the buttons, icons and return value.
' Optional. Default = vbExclamation (See Returns, below).
'
' sTitle - The title you want to display in the MsgBox.
' Optional. Default = "", which sets MsgBox's efault title.
'
' bStripLineNumbers - Do not return line numbers in the text returned
' by SearchModule(). Ignored if lines do not start
' with a numeric value followed by a space.
' Optional. Default = True.
'
' bStripComments - Strips any comments from the line returned by SearchModule().
' Optional. Default = True.
'
' bQuiet - Suppresses error messages from the SearchModule function.
' Optional. Default = False.
'
' Returns:
'
' The same values as the MsgBox function, which depend on the
' vbButtonsAndIcon argument.
' See help on the MsgBox function for valid vbMsgBoxStyle values
' and how they relate to the buttons and icons displayed,
' and possible return values.
'
' Notes:
'
' If sModName is specified, but not sProcName, then line numbers
' in the entire module must be unique for valid lines to be found.
' The SearchModule() function is used to find these lines,
' so line numbers must not also be used for any constants in
' the module, or they may match, erroneously.
' See SearchModule() function, for more information.
If Not oErr Is Nothing Then
sErrNum = oErr.Number
sErrDescr = oErr.Description
End If
' The same string variables passed as ByVal arguments are used to build the message.
If sErrNum = "0" Then
sErrNum = "Error occurred"
Else
sErrNum = "Error " & sErrNum & " occurred"
End If
' Reset errors so we can trap errors from SearchModule() function.
On Error Resume Next
If sErrLine = "0" Then ' Error line number is not available.
sErrLine = "."
Else ' Error line number is available.
If sModName = "" Then
vErrorLine = ""
Else ' Module name is available, so extract line from module.
vErrorLine = SearchModule(sModName, sProcName, sErrLine, sProcType, _
bStripLineNumbers, bStripComments, bQuiet)
End If
If IsNull(vErrorLine) Then ' SearchModule() failed.
vErrorLine = "Line that caused the error:" & vbCrLf & _
"(Couldn't extract text from " & sModName & " module)" & CR2
Else
If vErrorLine <> "" Then vErrorLine = "Line that caused the error:" & CR2 & _
vErrorLine & CR2
End If
sErrLine = " (line # " & sErrLine & ")"
End If
sErrLine = sErrLine & CR2
If sProcName <> "" Then sProcName = " in " & Trim(sProcName & " " & sProcType)
If sErrDescr <> "" Then sErrDescr = sErrDescr & CR2
sMsg = sErrNum & sProcName & sErrLine & sErrDescr & vErrorLine & sAdditionalMessage
ErrorMsgBox = MsgBox(sMsg, vbButtonsAndIcon, sTitle)
End Function
Copy/Paste the following code into a module called modMyFunctionTest.
Once you copied/pasted all four functions into their respective modules, Open the Immediate window (CTRL+G in the Access VBA editor) and type:
? MyFunction()
or
? MyFunction2()
and press enter and you should get explanatory 'divide by zero' errors from the two test function versions.
Option Compare Database
Option Explicit
' Set this to the name of the module that contains this code.
Const MOD_NAME = "modMyFunctionTest"
Function MyFunction() As Variant
' Example of how to use SearchModule() function.
' Returns Null if an error occurred.
Const FUNC_NAME = "MyFunction"
On Error GoTo MyFunction_ERROR
Dim A As Long
Dim B As Long
Dim C As Long
' An error will be generated by this code.
' Note: Line numbers should (must?) be in ascending order.
' Else, Erl doesn't always return the right line.
' Often it does return the right line number, even if out of order.
' They DO have to be unique per procedure.
100 A = 1
200 B = 0
300 C = A / B ' This line will cause an error.
400 C = Int("B") ' This line will cause a different error if it passes the above.
' Set B = 1 in line 200 to see second error message.
999 MyFunction = C
MyFunction_EXIT:
Exit Function
MyFunction_ERROR:
Dim sMsg As String
If Erl Then ' Error line is available.
sMsg = "Error " & Err & " occurred in " & FUNC_NAME & _
" function (line # " & Erl & ")." & vbCrLf & vbCrLf & _
Error & vbCrLf & vbCrLf & _
"Error was caused by the following line:" & vbCrLf & vbCrLf & _
"With line number/comments: " & vbCrLf & _
SearchModule(MOD_NAME, FUNC_NAME, Erl, , False, False, True) _
& vbCrLf & vbCrLf & _
"No line number: " & vbCrLf & _
SearchModule(MOD_NAME, FUNC_NAME, Erl, , True, False, False) _
& vbCrLf & vbCrLf & _
"No comments: " & vbCrLf & _
SearchModule(MOD_NAME, FUNC_NAME, Erl, , False, True, False) _
& vbCrLf & vbCrLf & _
"No line number/comments: " & vbCrLf & _
SearchModule(MOD_NAME, FUNC_NAME, Erl, , True, True, False)
Else ' No Error line is available.
sMsg = "Error " & Err & " occurred in " & FUNC_NAME & _
" function." & vbCrLf & vbCrLf & Error
End If
MsgBox sMsg, vbExclamation, FUNC_NAME & " error"
MyFunction = Null
Resume MyFunction_EXIT
End Function
Function MyFunction2() As Variant
' Example of how to use ErrorMsgBox() function to display an error message.
' Requires ErrorMsgBox() and SearchModule() functions.
Const FUNC_NAME = "MyFunction2"
On Error GoTo MyFunction2_ERROR
Dim A As Long
Dim B As Long
Dim C As Long
' An error will be generated by this code.
' Note: Line numbers should (must?) be in ascending order.
' Else, Erl doesn't always return the right line.
' Often it does return the right line number, even if out of order.
' They DO have to be unique per procedure.
100 A = 1
200 B = 0
300 C = A / B ' This line will cause an error.
400 C = Int("B") ' This line will cause a different error if it passes the above.
' Set B = 1 in line 200 to see second error message.
999 MyFunction2 = C
MyFunction2_EXIT:
Exit Function
MyFunction2_ERROR:
MyFunction2 = ErrorMsgBox("Let's display an additional message.", _
Err, Erl, MOD_NAME, FUNC_NAME, _
vbExclamation, FUNC_NAME & " error")
Resume MyFunction2_EXIT
End Function
IMPORTANT:
Unfortunately, I've found a bug I haven't figured out, yet.
Everything worked perfectly, so I shut down Access and went to bed.
When I got up in the morning, I tested it again, and Erl didn't return a line number (both test functions).
I tested this over and over many times (both functions) and still got no Erl.
If I changed line 200 to "B = 1", then line 400's error message was displayed properly. I got a valid Erl.
When I changed line 200 back to "B = 0", it too worked. I still got a valid Erl. Neither function failed again until I shut down Access and re-started it again.
This was consistent.
It seems that when MS Access starts up a new session, Erl is not set properly.
Currently, I consider this an Access bug.
I'm using a template database that I found somewhere on the internet quite a while ago. I really wish I remember where I found it so I could at least give the author credit for the routines and back-up ideas, but I haven't had any luck thus far.
I am having an issue with the back-end check on loading the database. Here is the code I'm using:
Private Sub Form_Open(Cancel As Integer)
On Error GoTo Err_Handler
Const conFILENOTFOUND As Integer = 3024
Const conPATHNOTFOUND As Integer = 3044
Dim dbs As DAO.Database, rst As DAO.Recordset, tdf As DAO.TableDef
Dim strTable As String, strConnect As String
Set dbs = CurrentDb
' mimimize database window/navigation pane
' DoCmd.SelectObject acForm, Me.Name, True
' DoCmd.Minimize
' test validity of links to back end and open
' form to refersh links if not valid
CheckLinks:
For Each tdf In dbs.TableDefs
If Len(tdf.Connect) > 0 Then
If tdf.Connect <> strConnect Then
strTable = tdf.Name
Set rst = dbs.OpenRecordset(strTable)
strConnect = tdf.Connect
End If
End If
Next tdf
Exit_Here:
Set rst = Nothing
Set tdf = Nothing
Set dbs = Nothing
Exit Sub
Err_Handler:
If Err.Number = conFILENOTFOUND Or Err.Number = conPATHNOTFOUND Then
DoCmd.OpenForm "frmUpdate_Links", _
WindowMode:=acDialog, _
OpenArgs:="ForceQuit"
Resume CheckLinks
Else
MsgBox Err.Description & " (" & Err.Number & ")"
Resume Exit_Here
End If
End Sub
The problem lies in the fact that the form isn't firing back at me saying the back-end is wrong (well, to be honest it IS doing this...) and opening frmUpdate_Links to update the backend links. I'm thinking the conFILENOTFOUND and/or conPATHNOTFOUND error checks are incorrect. I'm currently working with a database that doesn't have any entries in the two tables it uses to check whether the back-end exists or not. Those tables are BackEndLocation and FileLocations. It's supposed to open frmUpdate_Links when there is no entry in these two tables. Instead I get the typical error that occurs when a database cannot find it's back-end.
There are two modules associated with this routine. Here is their code:
First one is BrowseForFileClass which is a Class Module -
Option Compare Database
Option Explicit
' There are default values for the dialog box title and the list of file types
' in the 'file filter' section of the dialog box. The calling VBA code can
' use the following Properties and Methods of this class.
'
' Properties:
' DialogTitle -- the text that is displayed as the title of the
' dialog box. The default is "Browse For a File".
' AdditionalTypes -- one or more additional file types to be added as
' one item in the dialog box's file filter list,
' formatted like this sample:
' "My Files (*.mf1;*.mf2) | *.mf1;*.mf2 | Your Files (*.yf1;*.yf2) *.yf1;*.yf2"
' The following file types are in the built-in list:
' "All Files (*.*)"
' "Text Files (*.txt;*.prn;*.csv)"
' "Word Documents (*.doc)"
' "Word Templates (*.dot)"
' "Rich Text Files (*.rtf)"
' "Excel Files (*.xls)"
' "Databases (*.mdb)"
' "HTML Documents (*.html;*.htm)"
' DefaultType -- the item in the dialog's file filter list that will be
' active when the dialog box is activated. If the
' AdditionalTypes property is not used, the default
' is "All files (*.*)". If the AdditionalTypes property
' is used, this property cannot be used and the file type
' specified in the AdditionalTypes property will be active
' when the dialog box is activated. To set this property,
' specify a string that will match with the desired type,
' such as "*.doc" or "HTML".
' InitialFile -- the file name that is to be displayed in the File Name
' field in the dialog box when it is activated. The
' default is to leave the File Name field blank.
' InitialDir -- the directory/folder which should be active when the
' dialog box is activated. The default is the current
' directory.
'
' Methods:
' GetFileSpec() -- this function activates the dialog box and then returns
' the full path and filename of the file that the User
' has selected. If the User clicks Cancel, a zero
' length string is returned.
'
Private Type OPENFILENAME
lStructSize As Long
hwndOwner As Long
hInstance As Long
lpstrFilter As String
lpstrCustomFilter As Long
nMaxCustrFilter As Long
nFilterIndex As Long
lpstrFile As String
nMaxFile As Long
lpstrFileTitle As String
nMaxFileTitle As Long
lpstrInitialDir As String
lpstrTitle As String
Flags As Long
nFileOffset As Integer
nFileExtension As Integer
lpstrDefExt As String
lCustrData As Long
lpfnHook As Long
lpTemplateName As Long
End Type
Private Declare Function GetOpenFileName Lib "comdlg32.dll" Alias _
"GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Boolean
Private strDialogTitle As String
Private intDefaultType As Integer
Private strNewTypes As String
Private strInitialFile As String
Private strInitialDir As String
Private strFilter As String
Private strFltrLst As String
Private strFltrCnt As String
' This 'Method' routine displays the Open dialog box for the user to
' locate the desired file. Returns the full path to the file.
'
Public Function GetFileSpec()
Dim of As OPENFILENAME
Dim intRet As Integer
'set up the file filter and the default type option
If strNewTypes <> "" Then
of.lpstrFilter = strNewTypes & strFilter
of.nFilterIndex = 1
Else
of.lpstrFilter = strFilter
If intDefaultType <> 0 Then
of.nFilterIndex = intDefaultType
Else
of.nFilterIndex = 1
End If
End If
'define some other dialog options
of.lpstrTitle = strDialogTitle
of.lpstrInitialDir = strInitialDir
of.lpstrFile = strInitialFile & String(512 - Len(strInitialFile), 0)
of.nMaxFile = 511
' Initialize other parts of the structure
of.hwndOwner = Application.hWndAccessApp
of.hInstance = 0
of.lpstrCustomFilter = 0
of.nMaxCustrFilter = 0
of.lpfnHook = 0
of.lpTemplateName = 0
of.lCustrData = 0
of.lpstrFileTitle = String(512, 0)
of.nMaxFileTitle = 511
of.lpstrDefExt = vbNullChar
of.Flags = 0
of.lStructSize = Len(of)
'call the Open dialog routine
intRet = GetOpenFileName(of)
If intRet Then
GetFileSpec = Left(of.lpstrFile, InStr(of.lpstrFile, vbNullChar) - 1)
Else
GetFileSpec = ""
End If
End Function 'End of GetFileSpec
Public Property Let DialogTitle(strTitle As String)
'store the title for the dialog box
strDialogTitle = strTitle
End Property
Public Property Let AdditionalTypes(strAddTypes As String)
Dim Posn As Integer
Dim i As Integer
'don't accept additional types if a default type has been specified
If intDefaultType <> 0 Then
MsgBox "You cannot add to the file type filter if a default type is " & _
"being specified in the DefaultType property. When the " & _
"AdditionalTypes property is used, that item " & _
"is used as the default in the file type filter.", vbCritical, _
"Browse For File Dialog"
Exit Property
End If
'check for the "|" delimiter
Posn = InStr(strAddTypes, "|")
'save the new parameter or report an error
If Posn = 0 Then
MsgBox "The AdditionalTypes property string does not contain at least " & _
"one " & Chr$(34) & "|" & Chr$(34) & " character. " & _
"You must specify an AdditionalTypes property in the same " & _
"format that is shown in the " & _
"following example: " & vbCrLf & vbCrLf & Chr$(34) & _
"My Files (*.mf1;*.mf2) | *.mf1;*.mf2 | Your Files (*.yf1;*.yf2) *.yf1;*.yf2" _
& Chr$(34), vbCritical, "Browse For File Dialog"
strNewTypes = ""
Exit Property
Else
Do While True
If InStr(1, strAddTypes, "|") Then
strNewTypes = strNewTypes & Left$(strAddTypes, _
InStr(1, strAddTypes, "|") - 1) & vbNullChar
strAddTypes = Mid$(strAddTypes, InStr(1, strAddTypes, "|") + 1)
Else
strNewTypes = strNewTypes & vbNullChar
Exit Do
End If
Loop
End If
End Property 'End of AdditionalTypes
Public Property Let DefaultType(strType As String)
Dim Posn As Integer
Posn = InStr(strFltrLst, strType)
'don't accept a default if new types are being specified
If strNewTypes <> "" Then
MsgBox "You cannot set the DefaultType property if you are using the " & _
"AdditionalTypes property to expand the file types filter. " & _
"In that case the type specified in the AdditionalTypes property " & _
"will be the default type.", vbCritical, "Browse For File Dialog"
Exit Property
'make sure the selected default actually exists
ElseIf Posn = 0 Then
MsgBox "The file type you specified in the DefaultType " & _
"property is not in the built-in " & _
"list of file types. You must either specify one of the " & _
"built-in file types or use the AdditionalTypes property " & _
"to specify a complete entry similar to the " & _
"following example: " & vbCrLf & vbCrLf & Chr$(34) & _
"My Files (*.mf) | *.mf" & Chr$(34), vbCritical, _
"Browse For File Dialog"
Exit Property
Else
'set up the selected default
intDefaultType = Trim$(Mid$(strFltrCnt, Posn, 3))
End If
End Property
Public Property Let InitialFile(strIFile As String)
strInitialFile = strIFile
End Property
Public Property Let InitialDir(strIDir As String)
strInitialDir = strIDir
End Property
' This routine initializes the string constants that are used by this class
'
Private Sub Class_Initialize()
'define some initial conditions
strDialogTitle = "Browse For a File"
strInitialDir = ""
strInitialFile = ""
strNewTypes = ""
'define the filter string and the look-up strings
strFilter = "All Files (*.*)" & vbNullChar & "*.*" & vbNullChar & _
"Text Files (*.txt;*.prn;*.csv)" & vbNullChar & "*.txt;*.prn;*.csv" & vbNullChar & _
"Word Documents (*.doc)" & vbNullChar & "*.doc" & vbNullChar & _
"Word Templates (*.dot)" & vbNullChar & "*.dot" & vbNullChar & _
"Rich Text Files (*.rtf)" & vbNullChar & "*.rtf" & vbNullChar & _
"Excel Files (*.xls)" & vbNullChar & "*.xls" & vbNullChar & _
"Databases (*.mdb;*.accdb)" & vbNullChar & "*.mdb;*.accdb" & vbNullChar & _
"Personal Document Format (*.pdf)" & vbNullChar & "*.pdf" & vbNullChar & _
"HTML Documents (*.html;*.htm)" & vbNullChar & "*.html;*.htm" & vbNullChar
strFltrLst = "*.* *.txt *.prn *.csv *.doc *.dot *.rtf *.xls *.mdb *.accdb *.pdf *.html *.htm"
strFltrCnt = " 1 2 2 2 3 4 5 6 7 7 8 9 9"
End Sub
And here is the second module, modBackup -
Option Compare Database
Option Explicit
Declare Function CopyFile& Lib "kernel32" Alias "CopyFileA" (ByVal _
lpExistingFilename As String, ByVal lbNewFileName As String, ByVal _
bFailIfExists As Long)
Public AllowClose As Boolean
Public Sub MakeFileCopy(strExistingFile As String, _
strNewfile As String, _
blnDoNotOverWrite As Boolean, _
Optional blnShowMessage As Boolean = False)
Dim strMessage As String
strExistingFile = strExistingFile
strNewfile = strNewfile
If CopyFile(strExistingFile, strNewfile, blnDoNotOverWrite) = 1 Then
strMessage = "File successfully copied."
Else
strMessage = "File copy failed."
End If
If blnShowMessage Then
MsgBox strMessage, vbInformation, "Copy File"
End If
End Sub
Public Function BackUp(strBackEnd As String, strBackUp As String) As Boolean
Const FILEINUSE = 3356
Dim dbs As DAO.Database
Dim strMessage As String
Dim strBackUpTemp As String
' if back up file exists get user confirmation
' to delete it
If Dir(strBackUp) <> "" Then
strMessage = "Delete existing file " & strBackUp & "?"
If MsgBox(strMessage, vbQuestion + vbYesNo, "Confirm") = vbNo Then
strMessage = "Back up aborted."
MsgBox strMessage, vbInformation, "Back up"
Exit Function
Else
' make temporary copy of backend file and then delete it
strBackUpTemp = Left(strBackUp, InStr(strBackUp, ".")) & "bak"
MakeFileCopy strBackUp, strBackUpTemp, False
Kill strBackUp
End If
End If
On Error Resume Next
' attempt to open backend exclusively
Set dbs = OpenDatabase(Name:=strBackEnd, Options:=True)
Select Case Err.Number
Case 0
' no error so proceed
dbs.Close
Application.CompactRepair strBackEnd, strBackUp
If Err.Number = FILEINUSE Then
' file in use by current user
strMessage = "The file " & strBackEnd & _
" is currently unavailable. " & _
" You may have a table in it open."
MsgBox strMessage
' rename temporary copy of back up file
' if exists, back to original
If Dir(strBackUpTemp) <> "" Then
MakeFileCopy strBackUpTemp, strBackUp, False
Kill strBackUpTemp
End If
Exit Function
Else
On Error GoTo 0
' ensure back up file created
If Dir(strBackUp) = Mid(strBackUp, InStrRev(strBackUp, "\") + 1) Then
strMessage = "Back up successfully carried out."
BackUp = True
' delete temporary copy of back up file if exists
On Error Resume Next
Kill strBackUpTemp
On Error GoTo 0
Else
strMessage = "Back up failed."
' rename temporary copy of back up file
' if exists, back to original
If Dir(strBackUpTemp) <> "" Then
MakeFileCopy strBackUpTemp, strBackUp, False
Kill strBackUpTemp
End If
End If
MsgBox strMessage, vbInformation, "Back up"
End If
Case FILEINUSE
' file in use - inform user
strMessage = "The file " & strBackEnd & _
" is currently unavailable. " & _
" It may be in use by another user."
MsgBox strMessage
' rename temporary copy of back up file,
' if exists, back to original
If Dir(strBackUpTemp) <> "" Then
MakeFileCopy strBackUpTemp, strBackUp, False
Kill strBackUpTemp
End If
Case Else
' unknown error - inform user
MsgBox Err.Description, vbExclamation, "Error"
' rename temporary copy of back up file
' if exists, back to original
If Dir(strBackUpTemp) <> "" Then
MakeFileCopy strBackUpTemp, strBackUp, False
Kill strBackUpTemp
End If
End Select
End Function
Public Function GetBackEndPath() As Variant
GetBackEndPath = DLookup("BackEndPath", "FileLocations")
End Function
Public Function GetBackUpPath() As Variant
GetBackUpPath = DLookup("BackUpPath", "FileLocations")
End Function
I am 100% uncertain which errors the CheckLinks sub-routine is supposed to be looking for. I tried to find some information regarding the different errors, such as 3024 and 3044 but they didn't provide me any useful information as to how exactly these error codes associate with this routine.
The wacky part is the original "template" database works perfectly in all aspects. I copy/pasted over all the modules, routines, forms, etc. and made them my "own" to match up with the host database styles and themes, and now they don't work. What the heck am I doing wrong?
Thanks!
So I figured out what the problem was. The initial form must not be bound to any data. It needs to not rely on the backend in order to load "to the point" of executing the subroutines which check for the proper back-end files.
When I try to save the below code it gives an error message and I can't save it.
below are my error message's image file link.
I'm using Korean Excel 2007, So I don't know what exactly this message is in English but I can give you the meaning of this error message.
(this error message means... couldn't find file.)
(this error message means.... &H8000FFFF system error occurs.)
(I tried Export UserForm File but there were same error messages.)
I tried opening another Excel Window and Pasting Userforms, exporting Userform as files, saving as a different name and saving to a different path, but I kept getting the same error message.
I updated My windows7 to latest version in several days ago.
Below is part of code for sending Email From Excel (All code couldn't upload. It is so long and considered as spam.) and I uploaded UserForm ScreenShots For your Reference.
-----------My Part of Code And UserForm
My UserForm
Function NPP메일보내기함수()
'//ThisWorkbook.SaveAs ThisWorkbook.Path & "\" & Date & " " & "Position Report Ver.2.xlsx", FileFormat:=51 '//xlsm은 52
'//위 방법은 xlsx 저장은 잘 되나 아래와 같은 문제가 있다.
'//I have a Excel sheet, and if I save the file using the Save as... option in Excel VBA the currently open document would close, and switch over to the newly created document.
'//How can I save a copy of the document without switching over the control?
'//해결하려면 여러가지 방법이 있다. 여기엔 하나만 적는다. 아래와 같이 하는건 잘못된 방법이다. SaveCopyAS는 확장자 못 바꿈.
'//ThisWorkbook.SaveCopyAs ThisWorkbook.Path & "\" & Date & " " & "Position Report Ver.2 MEIN.xlsx" '//, FileFormat:=51
'//위 방법은 새창으로 안 열리기는 하나 확장자가 안 바뀜.
'//아래 방법이 새창으로 안 열리면서 확장자도 바뀌는 완벽한 방법임.
'
' Dim wb As Workbook, pstr As String
'
' pstr = ThisWorkbook.Path & "\" & Date & " Position Report Ver. 02 MEIN" & ".xlsm"
' ActiveWorkbook.SaveCopyAs Filename:=y
'
' Set wb = Workbooks.Open(pstr)
' wb.SaveAs Left(pstr, Len(pstr) - 1) & "x", 52
' wb.Close False
'
' Kill pstr
' 오류뜸
'//http://www.excely.com/excel-vba/save-workbook-as-new-file.shtml
ThisWorkbook.Sheets.Copy
Application.DisplayAlerts = False
Dim 매크로파일경로 As String
매크로파일경로 = ThisWorkbook.Path
ActiveWorkbook.SaveAs 매크로파일경로 & "\눈레포트 첨부 엑셀파일\" & Format(Date, "yyyy-mm-dd") & " " & "Position Report Ver.3 MEIN.xlsx", FileFormat:=51
ActiveWorkbook.Close
On Error GoTo Error_Handler
Dim oOutlook As Object
Dim sAPPPath As String
If IsAppRunning("Outlook.Application") = True Then 'Outlook was already running
Set oOutlook = GetObject(, "Outlook.Application") 'Bind to existing instance of Outlook
Else 'Could not get instance of Outlook, so create a new one
sAPPPath = GetAppExePath("outlook.exe") 'determine outlook's installation path
Shell (sAPPPath) 'start outlook
Do While Not IsAppRunning("Outlook.Application")
DoEvents
Loop
Set oOutlook = GetObject(, "Outlook.Application") 'Bind to existing instance of Outlook
End If
' MsgBox "Outlook Should be running now, let's do something"
Const olMailItem = 0
Dim oOutlookMsg As Object
Set oOutlookMsg = oOutlook.CreateItem(olMailItem) 'Start a new e-mail message
Dim 보낼메세지 As String
Dim 반복문카운터 As Integer
For 반복문카운터 = 95 To 127
보낼메세지 = 보낼메세지 & ThisWorkbook.Worksheets("NPP").Range("C" & 반복문카운터).Value & Chr(13) & Chr(10)
Next
With oOutlookMsg
.To = "해사운항팀"
.CC = " 사업안전팀; 최종범차장; 조달팀; 공무팀; 사업팀; 박준영대리; 고현해운"
.BCC = ""
.Subject = Range("C99").Value
'// .Body = Range("C95:C127").Value 요렇게 하면 안돼요.
.Body = 보낼메세지
'//Attachments를 Attachment라고 써서 에러가 나던 것.
.Attachments.Add 매크로파일경로 & "\눈레포트 첨부 엑셀파일\" & Format(Date, "yyyy-mm-dd") & " " & "Position Report Ver.3 MEIN.xlsx"
'//ThisWorkbook.Path하니까 파일이 없다는 오류가 나서 시도해봄.
.Display 'Show the message to the user
End With
Error_Handler_Exit:
On Error Resume Next
Set oOutlook = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: StartOutlook" & vbCrLf & _
"Error Description: " & Err.Description _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
'---------------------------------------------------------------------------------------
' Procedure : IsAppRunning
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Determine is an App is running or not
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sApp : GetObject Application to verify if it is running or not
'
' Usage:
' ~~~~~~
' IsAppRunning("Outlook.Application")
' IsAppRunning("Excel.Application")
' IsAppRunning("Word.Application")
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2014-Oct-31 Initial Release
'---------------------------------------------------------------------------------------
Function IsAppRunning(sApp As String) As Boolean
On Error GoTo Error_Handler
Dim oApp As Object
Set oApp = GetObject(, sApp)
IsAppRunning = True
Error_Handler_Exit:
On Error Resume Next
Set oApp = Nothing
Exit Function
Error_Handler:
Resume Error_Handler_Exit
End Function
'---------------------------------------------------------------------------------------
' Procedure : GetAppExePath
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Determine the path for a given exe installed on the local computer
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sEXEName : Name of the exe to locate
'
' Usage:
' ~~~~~~
' Call GetAppExePath("msaccess.exe")
' GetAppExePath("firefox.exe")
' GetAppExePath("outlook.exe")
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2014-Oct-31 Initial Release
'---------------------------------------------------------------------------------------
Function GetAppExePath(ByVal sExeName As String) As String
On Error GoTo Error_Handler
Dim WSHShell As Object
Set WSHShell = CreateObject("Wscript.Shell")
GetAppExePath = WSHShell.RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\" & sExeName & "\")
Error_Handler_Exit:
On Error Resume Next
Set WSHShell = Nothing
Exit Function
Error_Handler:
If Err.Number = -2147024894 Then
'Cannot locate requested exe????
Else
MsgBox "The following error has occured." & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: GetAppExePath" & vbCrLf & _
"Error Description: " & Err.Description, _
vbCritical, "An Error has Occured!"
End If
Resume Error_Handler_Exit
End Function
I have a function which is Boolean, and returns whether is the cell OK for creating a New Folder based on its value or its not (if it posses following chars:<,>,|,\,*,?)
But from some weird reason, it returns always false, either is a cell OK or not.
So, I have a sub which creates a loop for all rows and creates some .txt files and puts it in auto-generated folders.
Here is my code:
Sub CreateTxtSrb()
Dim iRow As Long
Dim iFile As Integer
Dim sPath As String
Dim sFile As String
Dim iEnd As Range
'iEnd = Cells(Rows.Count, "B").End(xlUp).Row
For iRow = 1 To Cells(Rows.Count, "B").End(xlUp).Row
iFile = FreeFile
With Rows(iRow)
If IsValidFolderName(.Range("B2").Value) = False Or IsValidFolderName(.Range("D2").Value) = False Or IsValidFolderName(.Range("F2").Value) = False Then
MsgBox ("Check columns B,D or F, it cannot contains chars: <,>,?,|,\,/,*,. or a space at the end")
Exit Sub
Else
strShort = IIf(InStr(.Range("E2").Value, vbCrLf), Left(.Range("E2").Value, InStr(.Range("E2").Value, vbCrLf) - 2), .Range("E2").Value)
sPath = "E:\" & .Range("B2").Value & "\"
If Len(Dir(sPath, vbDirectory)) = 0 Then MkDir sPath
sFile = .Range("D2").Value & ".txt"
Open sPath & sFile For Output As #iFile
Print #iFile, .Range("E2").Value
Close #iFile
End If
End With
Next iRow
End Sub
Function IsValidFolderName(ByVal sFolderName As String) As Boolean
'http://msdn.microsoft.com/en- us/library/windows/desktop/aa365247(v=vs.85).aspx#file_and_directory_names
'http://msdn.microsoft.com/en-us/library/ie/ms974570.aspx
On Error GoTo Error_Handler
Dim oRegEx As Object
'Check to see if any illegal characters have been used
Set oRegEx = CreateObject("vbscript.regexp")
oRegEx.Pattern = "[<>:""/\\\|\?\*]"
IsValidFolderName = Not oRegEx.test(sFolderName)
'Ensure the folder name does end with a . or a blank space
If Right(sFolderName, 1) = "." Then IsValidFolderName = False
If Right(sFolderName, 1) = " " Then IsValidFolderName = False
Error_Handler_Exit:
On Error Resume Next
Set oRegEx = Nothing
Exit Function
Error_Handler:
MsgBox ("test")
' MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
' "Error Number: " & Err.Number & vbCrLf & vbCrLf & _
' "Error Source: IsInvalidFolderName" & vbCrLf & _
' "Error Description: " & Err.Description, _
' vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
How can I make it return true if need be?
You don't need the external reference you can simply:
hasInvalidChars = sFolderName like "*[<>|\/:*?""]*"
I added " and : which are also illegal.
(In your example you have HTML entities (E.g. <) - these have no meaning in your RegEx string and are interpreted as 4 characters in the class)
That's a mess. Use a separate function
Public Function IsInvalid(ByVal name As String) As Boolean
Dim regex As Object
Set regex = VBA.CreateObject("VBScript.RegExp")
regex.Pattern = "[\\/:\*\?""<>\|]" 'the disallowed characters
IsInvalid = (regex.Execute(name).Count > 0)
End Function
instead, and call it when appropriate.
This is more an observation than a real question: MS-Access (and VBA in general) is desperately missing a tool where error handling code can be generated automatically, and where the line number can be displayed when an error occurs. Did you find a solution? What is it? I just realized how many hundreds of hours I spared since I found the right answer to this basic problem a few years ago, and I'd like to see what are your ideas and solutions on this very important issue.
What about using "Erl", it will display the last label before the error (e.g., 10, 20, or 30)?
Private Sub mySUB()
On Error GoTo Err_mySUB
10:
Dim stDocName As String
Dim stLinkCriteria As String
20:
stDocName = "MyDoc"
30:
DoCmd.openform stDocName, acFormDS, , stLinkCriteria
Exit_mySUB:
Exit Sub
Err_mySUB:
MsgBox Err.Number & ": " & Err.Description & " (" & Erl & ")"
Resume Exit_mySUB
End Sub
My solution is the following:
install MZ-Tools, a very interesting add-on for VBA. No they did not pay me to write this. Version 3 was free, but since version 8.0, the add-in is commercially sold.
program a standard error handler code such as this one (see MZ-Tools menu/Options/Error handler):
On Error GoTo {PROCEDURE_NAME}_Error
{PROCEDURE_BODY}
On Error GoTo 0
Exit {PROCEDURE_TYPE}
{PROCEDURE_NAME}_Error:
debug.print "#" & Err.Number, Err.description, "l#" & erl, "{PROCEDURE_NAME}", "{MODULE_NAME}"
This standard error code can be then automatically added to all of your procs and function by clicking on the corresponding button in the MZ-Tools menu. You'll notice that we refer here to a hidden and undocumented function in the VBA standard library, 'Erl', which stands for 'error line'. You got it! If you ask MZ-Tools to automatically number your lines of code, 'Erl' will then give you the number of the line where the error occured. You will have a complete description of the error in your immediate window, such as:
#91, Object variable or With block variable not set, l# 30, addNewField, Utilities
Of course, once you realize the interest of the system, you can think of a more sophisticated error handler, that will not only display the data in the debug window but will also:
display it as a message on the screen
Automatically insert a line in an error log file with the description of the error or
if you are working with Access or if you are connected to a database, automatically add a record to a Tbl_Error table!
meaning that each error generated at the user level can be stored either in a file or a table, somewhere on the machine or the network. Are we talking about building an automated error reporting system working with VBA?
Well there are a couple of tools that will do what you ask MZ Tools and FMS Inc come to mind.
Basically they involve adding an:
On Error GoTo ErrorHandler
to the top of each proc
and at the end they put an:
ErrorHandler:
Call MyErrorhandler Err.Number, Err.Description, Err.LineNumber
label with usually a call to a global error handler where you can display and log custom error messages
You can always roll your own tool like Chip Pearson did. VBA can actually access it's own IDE via the Microsoft Visual Basic for Applications Extensibility 5.3 Library. I've written a few class modules that make it easier to work with myself. They can be found on Code Review SE.
I use it to insert On Error GoTo ErrHandler statements and the appropriate labels and constants related to my error handling schema. I also use it to sync up the constants with the actual procedure names (if the function names should happen to change).
There is no need to buy tools DJ mentioned. Here is my code for free:
Public Sub InsertErrHandling(modName As String)
Dim Component As Object
Dim Name As String
Dim Kind As Long
Dim FirstLine As Long
Dim ProcLinesCount As Long
Dim Declaration As String
Dim ProcedureType As String
Dim Index As Long, i As Long
Dim LastLine As Long
Dim StartLines As Collection, LastLines As Collection, ProcNames As Collection, ProcedureTypes As Collection
Dim gotoErr As Boolean
Kind = 0
Set StartLines = New Collection
Set LastLines = New Collection
Set ProcNames = New Collection
Set ProcedureTypes = New Collection
Set Component = Application.VBE.ActiveVBProject.VBComponents(modName)
With Component.CodeModule
' Remove empty lines on the end of the code
For i = .CountOfLines To 1 Step -1
If Component.CodeModule.Lines(i, 1) = "" Then
Component.CodeModule.DeleteLines i, 1
Else
Exit For
End If
Next i
Index = .CountOfDeclarationLines + 1
Do While Index < .CountOfLines
gotoErr = False
Name = .ProcOfLine(Index, Kind)
FirstLine = .ProcBodyLine(Name, Kind)
ProcLinesCount = .ProcCountLines(Name, Kind)
Declaration = Trim(.Lines(FirstLine, 1))
LastLine = FirstLine + ProcLinesCount - 2
If InStr(1, Declaration, "Function ", vbBinaryCompare) > 0 Then
ProcedureType = "Function"
Else
ProcedureType = "Sub"
End If
Debug.Print Component.Name & "." & Name, "First: " & FirstLine, "Lines:" & ProcLinesCount, "Last: " & LastLine, Declaration
Debug.Print "Declaration: " & Component.CodeModule.Lines(FirstLine, 1), FirstLine
Debug.Print "Closing Proc: " & Component.CodeModule.Lines(LastLine, 1), LastLine
' do not insert error handling if there is one already:
For i = FirstLine To LastLine Step 1
If Component.CodeModule.Lines(i, 1) Like "*On Error*" Then
gotoErr = True
Exit For
End If
Next i
If Not gotoErr Then
StartLines.Add FirstLine
LastLines.Add LastLine
ProcNames.Add Name
ProcedureTypes.Add ProcedureType
End If
Index = FirstLine + ProcLinesCount + 1
Loop
For i = LastLines.Count To 1 Step -1
If Not (Component.CodeModule.Lines(StartLines.Item(i) + 1, 1) Like "*On Error GoTo *") Then
Component.CodeModule.InsertLines LastLines.Item(i), "ExitProc_:"
Component.CodeModule.InsertLines LastLines.Item(i) + 1, " Exit " & ProcedureTypes.Item(i)
Component.CodeModule.InsertLines LastLines.Item(i) + 2, "ErrHandler_:"
Component.CodeModule.InsertLines LastLines.Item(i) + 3, " Call LogError(Err, Me.Name, """ & ProcNames.Item(i) & """)"
Component.CodeModule.InsertLines LastLines.Item(i) + 4, " Resume ExitProc_"
Component.CodeModule.InsertLines LastLines.Item(i) + 5, " Resume ' use for debugging"
Component.CodeModule.InsertLines StartLines.Item(i) + 1, " On Error GoTo ErrHandler_"
End If
Next i
End With
End Sub
Put it in a module and call it from Immediate Window every time you add new function or sub to a form or module like this (Form1 is name of your form):
MyModule.InsertErrHandling "Form_Form1"
It will alter your ode in Form1 from this:
Private Function CloseIt()
DoCmd.Close acForm, Me.Name
End Function
to this:
Private Function CloseIt()
On Error GoTo ErrHandler_
DoCmd.Close acForm, Me.Name
ExitProc_:
Exit Function
ErrHandler_:
Call LogError(Err, Me.Name, "CloseIt")
Resume ExitProc_
Resume ' use for debugging
End Function
Create now in a module a Sub which will display the error dialog and where you can add inserting the error to a text file or database:
Public Sub LogError(ByVal objError As ErrObject, moduleName As String, Optional procName As String = "")
On Error GoTo ErrHandler_
Dim sql As String
MsgBox "Error " & Err.Number & " Module " & moduleName & Switch(procName <> "", " in " & procName) & vbCrLf & " (" & Err.Description & ") ", vbCritical
Exit_:
Exit Sub
ErrHandler_:
MsgBox "Error in LogError procedure " & Err.Number & ", " & Err.Description
Resume Exit_
Resume ' use for debugging
End Sub
This code does not enter error handling if there is already "On Error" statement in a proc.
Love it Vlado!
I realize this is an old post, but I grabbed it and gave it a try, but I ran into a number of issues with it, which I managed to fix. Here's the code with fixes:
First of course, be sure to add the "Microsoft Visual Basic for Applications Extensibility 5.3" library to your project, and add these subroutines / modules to your project as well.
First, the module with the main code was named "modVBAChecks", and contained the following two subroutines:
To go through all modules (behind forms, sheets, the workbook, and classes as well, though not ActiveX Designers):
Sub AddErrorHandlingToAllProcs()
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim lCtr As Long
StartNewWorksheetLog
Set VBProj = Workbooks("LabViewAnalysisTools.xla").VBProject
For Each VBComp In VBProj.VBComponents
If VBComp.Type <> vbext_ct_ActiveXDesigner Then
If VBComp.Name <> "modVBAChecks" And VBComp.Name <> "modLogToWorksheet" Then
AddToWksLog "============ Looking at Module """ & VBComp.Name & """"
'InsertErrHandling VBComp.Name
AddToWksLog
AddToWksLog
End If
End If
Next
MsgBox "Done!", vbSystemModal
End Sub
Then the modified version of your code (including a suggested change by
Rafał B.):
Public Sub InsertErrHandling(modsProcName As String)
' Modified from code submitted to StackOverflow by user Vlado, originally found
' here: https://stackoverflow.com/questions/357822/automatically-generating-handling-of-issues
Dim vbcmA As VBIDE.CodeModule
Dim ProcKind As VBIDE.vbext_ProcKind
Dim LineProcKind As VBIDE.vbext_ProcKind
Dim sProcName As String
Dim sLineProcName As String
Dim lFirstLine As Long
Dim lProcLinesCount As Long
Dim lLastLine As Long
Dim sDeclaration As String
Dim sProcType As String
Dim lLine As Long, lLine2 As Long
Dim sLine As String
Dim lcStartLines As Collection, lcLastlines As Collection, scProcsProcNames As Collection, scProcTypes As Collection
Dim bAddHandler As Boolean
Dim lLinesAbove As Long
Set lcStartLines = New Collection
Set lcLastlines = New Collection
Set scProcsProcNames = New Collection
Set scProcTypes = New Collection
Set vbcmA = Application.VBE.ActiveVBProject.VBComponents(modsProcName).CodeModule
' Remove empty lines on the end of the module. Cleanup, not error handling.
lLine = vbcmA.CountOfLines
If lLine = 0 Then Exit Sub ' Nothing to do!
Do
If Trim(vbcmA.Lines(lLine, 1)) <> "" Then Exit Do
vbcmA.DeleteLines lLine, 1
lLine = lLine - 1
Loop
lLine = vbcmA.CountOfDeclarationLines + 1
Do While lLine < vbcmA.CountOfLines
bAddHandler = False
' NOTE: ProcKind is RETRUNED from ProcOfLine!
sProcName = vbcmA.ProcOfLine(lLine, ProcKind)
' Fortunately ProcBodyLine ALWAYS returns the first line of the procedure declaration!
lFirstLine = vbcmA.ProcBodyLine(sProcName, ProcKind)
sDeclaration = Trim(vbcmA.Lines(lFirstLine, 1))
Select Case ProcKind
Case VBIDE.vbext_ProcKind.vbext_pk_Proc
If sDeclaration Like "*Function *" Then
sProcType = "Function"
ElseIf sDeclaration Like "*Sub *" Then
sProcType = "Sub"
End If
Case VBIDE.vbext_ProcKind.vbext_pk_Get, VBIDE.vbext_ProcKind.vbext_pk_Let, VBIDE.vbext_ProcKind.vbext_pk_Set
sProcType = "Property"
End Select
' The "lProcLinesCount" function will sometimes return ROWS ABOVE
' the procedure, possibly up until the prior procedure,
' and often rows BELOW the procedure as well!!!
lProcLinesCount = vbcmA.ProcCountLines(sProcName, ProcKind)
lLinesAbove = 0
lLine2 = lFirstLine - 1
If lLine2 > 0 Then
Do
sLineProcName = vbcmA.ProcOfLine(lLine2, LineProcKind)
If Not (sLineProcName = sProcName And LineProcKind = ProcKind) Then Exit Do
lLinesAbove = lLinesAbove + 1
lLine2 = lLine2 - 1
If lLine2 = 0 Then Exit Do
Loop
End If
lLastLine = lFirstLine + lProcLinesCount - lLinesAbove - 1
' Now need to trim off any follower lines!
Do
sLine = Trim(vbcmA.Lines(lLastLine, 1))
If sLine = "End " & sProcType Or sLine Like "End " & sProcType & " '*" Then Exit Do
lLastLine = lLastLine - 1
Loop
AddToWksLog modsProcName & "." & sProcName, "First: " & lFirstLine, "Lines:" & lProcLinesCount, "Last: " & lLastLine
AddToWksLog "sDeclaration: " & vbcmA.Lines(lFirstLine, 1), lFirstLine
AddToWksLog "Closing Proc: " & vbcmA.Lines(lLastLine, 1), lLastLine
If lLastLine - lFirstLine < 8 Then
AddToWksLog " --------------- Too Short to bother!"
Else
bAddHandler = True
' do not insert error handling if there is one already:
For lLine2 = lFirstLine To lLastLine Step 1
If vbcmA.Lines(lLine2, 1) Like "*On Error GoTo *" And Not vbcmA.Lines(lLine2, 1) Like "*On Error GoTo 0" Then
bAddHandler = False
Exit For
End If
Next lLine2
If bAddHandler Then
lcStartLines.Add lFirstLine
lcLastlines.Add lLastLine
scProcsProcNames.Add sProcName
scProcTypes.Add sProcType
End If
End If
AddToWksLog
lLine = lFirstLine + lProcLinesCount + 1
Loop
For lLine = lcLastlines.Count To 1 Step -1
vbcmA.InsertLines lcLastlines.Item(lLine), "ExitProc:"
vbcmA.InsertLines lcLastlines.Item(lLine) + 1, " Exit " & scProcTypes.Item(lLine)
vbcmA.InsertLines lcLastlines.Item(lLine) + 2, "ErrHandler:"
vbcmA.InsertLines lcLastlines.Item(lLine) + 3, " ShowErrorMsg Err, """ & scProcsProcNames.Item(lLine) & """, """ & modsProcName & """"
vbcmA.InsertLines lcLastlines.Item(lLine) + 4, " Resume ExitProc"
' Now replace any "On Error Goto 0" lines with "IF ErrorTrapping Then On Error Goto ErrHandler"
For lLine2 = lcStartLines(lLine) To lcLastlines(lLine)
sLine = vbcmA.Lines(lLine2, 1)
If sLine Like "On Error GoTo 0" Then
vbcmA.ReplaceLine lLine2, Replace(sLine, "On Error Goto 0", "IF ErrorTrapping Then On Error Goto ErrHandler")
End If
Next
lLine2 = lcStartLines.Item(lLine)
Do
sLine = vbcmA.Lines(lLine2, 1)
If Not sLine Like "* _" Then Exit Do
lLine2 = lLine2 + 1
Loop
vbcmA.InsertLines lLine2 + 1, " If ErrorTrapping Then On Error GoTo ErrHandler"
Next lLine
End Sub
And rather than pushing things to the Immediate window I used subroutines in a module I named "modLogToWorksheet", the full module being here:
Option Explicit
Private wksLog As Worksheet
Private lRow As Long
Public Sub StartNewWorksheetLog()
Dim bNewSheet As Boolean
bNewSheet = True
If ActiveSheet.Type = xlWorksheet Then
Set wksLog = ActiveSheet
bNewSheet = Not (wksLog.UsedRange.Cells.Count = 1 And wksLog.Range("A1").Formula = "")
End If
If bNewSheet Then Set wksLog = ActiveWorkbook.Worksheets.Add
lRow = 1
End Sub
Public Sub AddToWksLog(ParamArray sMsg() As Variant)
Dim lCol As Long
If wksLog Is Nothing Or lRow = 0 Then StartNewWorksheetLog
If Not (IsNull(sMsg)) Then
For lCol = 0 To UBound(sMsg)
If sMsg(lCol) <> "" Then wksLog.Cells(lRow, lCol + 1).Value = "'" & sMsg(lCol)
Next
End If
lRow = lRow + 1
End Sub
And finally, here's my Error Dialog generator:
Public Sub ShowErrorMsg(errThis As ErrObject, strSubName As String, strModName As String _
, Optional vbMBStyle As VbMsgBoxStyle = vbCritical, Optional sTitle As String = APP_TITLE)
If errThis.Number <> 0 Then
MsgBox "An Error Has Occurred in the Add-in. Please inform " & ADMINS & " of this problem." _
& vbCrLf & vbCrLf _
& "Error #: " & errThis.Number & vbCrLf _
& "Description: " & " " & errThis.Description & vbCrLf _
& "Subroutine: " & " " & strSubName & vbCrLf _
& "Module: " & " " & strModName & vbCrLf _
& "Source: " & " " & errThis.Source & vbCrLf & vbCrLf _
& "Click OK to continue.", vbMBStyle Or vbSystemModal, sTitle
End If
End Sub
Hope future users find it useful!