FileDialog in Word 2011 VBA - vba

I'm hoping for a bit of a sanity check. I'm adapting a Word add-in (written in VBA for Word 2010) for Mac, specifically, at this point, Word 2011. I'm aware of many of the differences, but one that I haven't been able to find much documentation on is the apparent lack of FileDialog. The closest I've come to an answer is here: http://www.rondebruin.nl/mac.htm where the author uses Application.GetOpenFilename. That method doesn't seem to exist for Word, though (the focus of that site is Excel).
Does anyone know how to use the file and folder picker dialogs that FileDialog makes available? I'm not familiar with Applescript, really, but I've had to learn a little in order to get around Word 2011's funky file management issues (Dir, FileCopy, etc.). So, if that's the answer, any sense of what the code might look like in Applescript would be greatly appreciated. (I more or less know how to translate that into VBA).

I believe you have to use Apple Script in order to do this a bit better on the Mac. The following code allows the user to select text files which is returned as an array from the function. You would simply be able to modify the Apple Script to return other file types and select directories, I'll leave that to you.
The code that calls the function and displays a message box with all the files:
Sub GetTextFilesOnMac()
Dim vFileName As Variant
'Call the function to return the files
vFileName = Select_File_Or_Files_Mac
'If it's empty then the user cancelled
If IsEmpty(vFileName) Then Exit Sub
'Loop through all the files specified
For ii = LBound(vFileName) To UBound(vFileName)
MsgBox vFileName(ii)
Next ii
End Sub
And the function that does the Apple Script work:
Function Select_File_Or_Files_Mac() As Variant
'Uses AppleScript to select files on a Mac
Dim MyPath As String, MyScript As String, MyFiles As String, MySplit As Variant
'Get the documents folder as a default
On Error Resume Next
MyPath = MacScript("return (path to documents folder) as String")
'Set up the Apple Script to look for text files
MyScript = "set applescript's text item delimiters to "","" " & vbNewLine & _
"set theFiles to (choose file of type " & " {""public.TEXT""} " & _
"with prompt ""Please select a file or files"" default location alias """ & _
MyPath & """ multiple selections allowed true) as string" & vbNewLine & _
"set applescript's text item delimiters to """" " & vbNewLine & _
"return theFiles"
'Run the Apple Script
MyFiles = MacScript(MyScript)
On Error GoTo 0
'If there are multiple files, split it into an array and return the results
If MyFiles <> "" Then
MySplit = Split(MyFiles, ",")
Select_File_Or_Files_Mac = MySplit
End If
End Function
Finally, it can be a bit of a pain specifying different file types, if you want to specify only Word documents, then replace public.TEXT with com.microsoft.word.doc, however this won't allow .docx or .docm files. You need to use org.openxmlformats.wordprocessingml.document and org.openxmlformats.wordprocessingml.document.macroenabled respectively for these. For more info on these see: https://developer.apple.com/library/mac/#documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html

Related

Open file explorer and search for variable in textbox

I the user to enter a keyword in a text box and when the macro is executed;
Open a new Windows' File Explorer window designated by a path.
path = C:\Users\ME\Desktop\Folder7
Search from the variable in SearchBox1 (this is a ActiveX text box on a worksheet.)
mySearch = sht.OLEObjects("SearchBox1").Object.Text & "*"
I saw multiple posts using Shell commands to open a File Explorer Window.
Call Shell("explorer.exe " & Chr(34) & "search-ms:query=*.pdf&crumb=location:C:\Users\ME\Desktop\Folder7" & Chr(34), vbNormalFocus)
When I run the above line there is an error from the explorer.
'Windows cannot find ". Make sure you typed the name correctly, and then try again.'
I need the macro to search for all files associated with the string. Folder names, file names, and words/characters within each type of document. They have all been OCR'd and Indexed by Windows.
It should have the ability to search for incomplete words as well.
I got Shell to open an explorer window to the path by
Call Shell("explorer.exe " & Chr(34) & "C:\Users\ME\Desktop\Folder7" & Chr(34), vbNormalFocus)
How can I search all folders and sub-folders within this freshly opened window?
I don't need the results compiled into Excel or any other program. I just need to make a quick search button as if you were to manually open this folder and use the search bar.
This worked for me:
Sub Tester()
ShowSearch "C:\_Stuff\test", "*.pdf" 'search by file name
ShowSearch "C:\_Stuff\Mail\", "reminder", True 'search by keyword
End Sub
Sub ShowSearch(searchWhere, searchFor, Optional SearchByKeyword As Boolean = False)
Const CMD As String = "explorer.exe ""search-ms:crumb=name:{query}&crumb=location:{location}"" "
Dim s
s = Replace(CMD, "{query}", WorksheetFunction.EncodeURL(searchFor))
s = Replace(s, "{location}", WorksheetFunction.EncodeURL(searchWhere))
If SearchByKeyword Then s = Replace(s, "crumb=name:", "crumb=")
'Debug.Print s
Shell s
End Sub
Note: WorksheetFunction.EncodeURL() is 2013 and later. For alternatives see:
How can I URL encode a string in Excel VBA?
Double Click on Cell to search
This is a solution I have combined from various places to open an explorer window at a path, that are filtered (searched) by the term in the selected cell using the windows File Explorer search function. It is triggered by a double click on a cell that contains the search term:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Application.Intersect(Target, Range("A1:AA1048576")) Is Nothing Then
Dim d As String
Dim searchpath As String
Dim searchlocation As String
Cancel = True
d = Selection.Value
'change window name to make sure new explorer window is opened for each instance
'copy string from manual search
searchpath = "search-ms:displayname=" & d & "%20Results%20&crumb=System.Generic.String%3A"
'copy string from manual search (e.g. my documents replace USERNAME)
searchlocation = "&crumb=location:C%3A%5CUsers%5CUSERNAME%5CDocuments"
If Not d = "" Then
Call Shell("explorer.exe """ & searchpath & d & searchlocation & "", 1)
'src: https://stackoverflow.com/questions/24376850/open-explorer-search-from-excel-hyperlink
End If
End If
End Sub
This opens the window in VbNormalFocus, with the window title set to the cell variable (d). The ensures that if this code is run on another cell value a new separate window will be opened. Without this I found the next time I ran the code the explorer window was not updated with the new search value, but just changed focus to the previous result.
edit: "copy from search bar" is the string after location: in the address bar of a manual search in explorer
Using ActiveX Controls
Add an ActiveX Text box (TextBox1) and button (CommandButton1) and add the following codeto the command button:
Private Sub CommandButton1_Click()
Dim d As String
Dim searchpath As String
Dim searchlocation As String
Cancel = True
d = TextBox1.Value
'change window name to make sure new explorer window is opened for each instance
'copy string from manual search
searchpath = "search-ms:displayname=" & d & "%20Results%20&crumb=System.Generic.String%3A"
'copy string from manual search (e.g. my documents replace USERNAME)
searchlocation = "&crumb=location:C%3A%5CUsers%5CUSERNAME%5CDocuments"
If Not d = "" Then
Call Shell("explorer.exe """ & searchpath & d & searchlocation & "", 1)
'src: https://stackoverflow.com/questions/24376850/open-explorer-search-from-excel-hyperlink
End If
End Sub
Now the user can change the text in the text box and clicking the button will open a windows file explorer search of the designated folder in the code.
Screenshot example using button search for "Editable Search Text"
EDIT
You can include additional search functions with the Windows search syntax:
http://download.microsoft.com/download/8/1/7/8174a74e-3d8d-4478-abc6-84cd51ad93c4/Windows_Desktop_Advanced_Query_Reference.pdf
Eg. you can search within the folder for all files with a partial match of each word in the string by changing the search variable "d:
...
d = Selection.Value
d = "(" & Replace(d, " ", " OR ") & ")"
...
if the selection (d) had a value of Where will I find it
This will search for (Where OR will OR I OR find OR it) in windows explorer and would return files with names such as WHEREver and Last WILL and testament. I've found this useful for qualitative information, where casting a wider search is acceptable and can be easily filtered through by the user (NOTE: the above example would also return all files with a name containing i so it is not very specific!)
executing Dir() empty after a Dir() with a given path will start to list all files in that dir, you just use if InStr() <> 0 to check against your value.
sFileName = Dir(path)
Do While sFileName > ""
tmp = tmp & sFileName & ";" : sFileName = Dir()
Loop
List() = Split(tmp, ";")
there you have a list of all files inside that path, you can check sub folders the same way by going through each one doing the same thing.
I do not take credit for the idea, it's awesome that you can do this. I just took the idea a step further and made it modular, so you can add any type of search:
Sub searchInExplorer_TEST()
'searchInExplorer "D:\", , , True, "*.jpg", True, "24 Feb 20"
searchInExplorer "D:\", , , , , True, "24 Feb 20", True, "picture"
End Sub
Sub searchInExplorer(searchWhere _
, Optional isSearchAll As Boolean, Optional strAll _
, Optional isSearchName As Boolean, Optional strName _
, Optional isSearchModified As Boolean, Optional strModified _
, Optional isSearchType As Boolean, Optional strType)
'*****************************************************
'https://stackoverflow.com/questions/52671500/vba-to-open-file-explorer-and-search-for-variable-in-textbox
'ALLOWS SEARCHING IN WINDOWS EXPLORER USING VARIABLES
'EITHER USE SEARCH ALL OR OTHER SEARCH TIMES
'EACH SEARCH TYPE HAS AN ON/OFF SWITCH AND A STRING VARIABLE TO SEARCH BY
'*****************************************************
Dim STR As String
STR = "explorer.exe ""search-ms:"
If isSearchAll Then
STR = STR & "crumb=:" & WorksheetFunction.EncodeURL(strAll)
Else
If isSearchName Then
STR = STR & "&crumb=name:" & WorksheetFunction.EncodeURL(strName)
End If
If isSearchModified Then
STR = STR & "&crumb=modified:" & WorksheetFunction.EncodeURL(strModified)
End If
If isSearchType Then
STR = STR & "&crumb=kind:" & WorksheetFunction.EncodeURL(strType)
End If
End If
STR = STR & "&crumb=location:" & WorksheetFunction.EncodeURL(searchWhere)
STR = STR & """ "
Debug.Print STR
Shell STR
End Sub

VBA saving Excel to Sharepoint freezes forever with a screen showing “Getting list of available content types and properties…”

I have VBA that, along with a whole lot of other stuff, saves an excel workbook to SharePoint (enterprise 2010 I think) and it works fine most of the time but every once in while, when a user runs the VBA, the Excel freezes with a pop up showing "Getting list of available content types and properties...". If the user selects cancel another pop up come up "Run-time error '1004': Method 'SaveAs' of object '_Workbook' failed. If the user selects 'Debug' the last line of VBA is highlighted as creating the error.
Dim fileName As String
Dim excelDirName As String
fileName = [c9]
excelDirName = [c16] & "/"
ThisWorkbook.SaveAs excelDirName & fileName & ".xls"
Since this works sometimes (and it worked for over 6 months without this happening) and not other times I am not sure what it could be and I am thinking something was updated in SharePoint.
I would write it a bit differently, so as to make it more robust:
With SomeSpecificSheet
Dim path As String
path = .Range("SavePath").Value
Dim fileName As String
fileName = .Range("SaveFileName").Value
End With
Debug.Assert Trim(path) <> vbNullString
Debug.Assert Trim(fileName) <> vbNullString
Dim savePath As String
savePath = path & "/" & fileName
ThisWorkbook.SaveAs savePath
Note:
Be explicit about the worksheet you're reading from - you're currently reading from whatever the active sheet is, and unless every single worksheet in ThisWorkbook has the expected values in $C$9 and $C$16, that's asking for trouble.
Use named ranges, so that if a user inserts a column before column C or a row before row 9, your code still refers to the correct cells.
Let SaveAs determine the file's extension.
Use Debug.Assert to verify assumptions (and break before you freeze). Alternatively, you can explicitly validate the values, for example:
If path = vbNullString Or fileName = vbNullString Then
MsgBox "I need a path!"
Exit Sub
End If

Charts template folder

I've developed a superuseful addon which recreates source data for charts "disconnected" from Excel. So I .SaveChartTemplate then ApplyChartTemplate to a new chart, and need to delete temp. template.
Don't know how to get chart template folder. I assume that it's Environ("AppData") & "\Microsoft\Шаблоны\Charts\" but it's obvious that it works only with russian locale.
After analyzing registry keys, i've come to realize that templates folder name can be found in HKCU\Software\Microsoft\Office\<OFFICE VERSION>\Common\General\Templates
Assuming the above a possible solution is
Function chartTemplatesFolder() As String
On Error GoTo er:
Dim templatesFolder As String
templatesFolder = CreateObject("WScript.Shell").RegRead( _
"HKCU\Software\Microsoft\Office\" & Application.Version & "\Common\General\Templates")
chartTemplatesFolder = Environ("AppData") & "\Microsoft\" & templatesFolder & "\Charts\"
Exit Function
er:
End Function

Removing internal link to Word-templates via VBA

I'm trying to create a small VB-application that removes the internal link in Word Documents, to their templates.
I have found this guide
http://word.tips.net/Pages/T001437_Batch_Template_Changes.html
and am trying to modify it, to use with VBA instead of Macro programming inside of Office.
However, I'm getting stuck on how to get the Document.Open to work. Any help is appreciated.
This is supposed to run as a free-standing application, and not runt from within Word.
I'm looking for a way to perform what the Macro does, but not from within Word.
There are two pieces of bad news to give here.
1) A document has to have a template. You cannot remove it, only change it to something else.
2) Changing a template does nothing anyway. See this page.
I am wonder if the problem with the Open method is that you are trying to open ".doc" extension files, not the modern ".docx" extension files. The VBA subroutine you linked to only does ".doc" files. This VBA code does both:
Function StringEndsWith( _
ByVal strValue As String, _
CheckFor As String) As Boolean
Dim sCompare As String
Dim lLen As Long
lLen = Len(CheckFor)
If lLen > Len(strValue) Then Exit Function
sCompare = Right(strValue, lLen)
StringEndsWith = StrComp(sCompare, CheckFor, vbTextCompare) = 0
End Function
Sub ChangeTemplates()
Dim strDocPath As String
Dim strTemplateB As String
Dim strCurDoc As String
Dim docCurDoc As Document
' set document folder path and template strings
strDocPath = "C:\tmp\"
' get first doc - only time need to provide file spec
strCurDoc = Dir(strDocPath & "*.doc*")
' ready to loop (for as long as file found)
Do While strCurDoc <> ""
If (StringEndsWith(strCurDoc, ".doc") Or StringEndsWith(strCurDoc, ".docx")) Then
' open file
Set docCurDoc = Documents.Open(FileName:=strDocPath & strCurDoc)
' change the template back to Normal
docCurDoc.AttachedTemplate = ""
' save and close
docCurDoc.Close wdSaveChanges
End If
' get next file name
strCurDoc = Dir
Loop
MsgBox "Finished"
End Sub
long time between answers but may be useful to others. If you have access to the VBE of the Word document [Alt F11], and you want to remove the reference then go to "Tools/References" [top menu] and deselect it from the list of reference files. I had a similar issue where template no longer existed, but it was still being 'referenced' in the Project window, so I did the above.

MS Access: how to compact current database in VBA

Pretty simple question, I know.
If you want to compact/repair an external mdb file (not the one you are working in just now):
Application.compactRepair sourecFile, destinationFile
If you want to compact the database you are working with:
Application.SetOption "Auto compact", True
In this last case, your app will be compacted when closing the file.
My opinion: writting a few lines of code in an extra MDB "compacter" file that you can call when you want to compact/repair an mdb file is very usefull: in most situations the file that needs to be compacted cannot be opened normally anymore, so you need to call the method from outside the file.
Otherwise, the autocompact shall by default be set to true in each main module of an Access app.
In case of a disaster, create a new mdb file and import all objects from the buggy file. You will usually find a faulty object (form, module, etc) that you will not be able to import.
If you have the database with a front end and a back end. You can use the following code on the main form of your front end main navigation form:
Dim sDataFile As String, sDataFileTemp As String, sDataFileBackup As String
Dim s1 As Long, s2 As Long
sDataFile = "C:\MyDataFile.mdb"
sDataFileTemp = "C:\MyDataFileTemp.mdb"
sDataFileBackup = "C:\MyDataFile Backup " & Format(Now, "YYYY-MM-DD HHMMSS") & ".mdb"
DoCmd.Hourglass True
'get file size before compact
Open sDataFile For Binary As #1
s1 = LOF(1)
Close #1
'backup data file
FileCopy sDataFile, sDataFileBackup
'only proceed if data file exists
If Dir(sDataFileBackup, vbNormal) <> "" Then
'compact data file to temp file
On Error Resume Next
Kill sDataFileTemp
On Error GoTo 0
DBEngine.CompactDatabase sDataFile, sDataFileTemp
If Dir(sDataFileTemp, vbNormal) <> "" Then
'delete old data file data file
Kill sDataFile
'copy temp file to data file
FileCopy sDataFileTemp, sDataFile
'get file size after compact
Open sDataFile For Binary As #1
s2 = LOF(1)
Close #1
DoCmd.Hourglass False
MsgBox "Compact complete. " & vbCrLf & vbCrLf _
& "Size before: " & Round(s1 / 1024 / 1024, 2) & "MB" & vbCrLf _
& "Size after: " & Round(s2 / 1024 / 1024, 2) & "MB", vbInformation
Else
DoCmd.Hourglass False
MsgBox "ERROR: Unable to compact data file."
End If
Else
DoCmd.Hourglass False
MsgBox "ERROR: Unable to backup data file."
End If
DoCmd.Hourglass False
Try adding this module, pretty simple, just launches Access, opens the database, sets the "Compact on Close" option to "True", then quits.
Syntax to auto-compact:
acCompactRepair "C:\Folder\Database.accdb", True
To return to default*:
acCompactRepair "C:\Folder\Database.accdb", False
*not necessary, but if your back end database is >1GB this can be rather annoying when you go into it directly and it takes 2 minutes to quit!
EDIT: added option to recurse through all folders, I run this nightly to keep databases down to a minimum.
'accCompactRepair
'v2.02 2013-11-28 17:25
'===========================================================================
' HELP CONTACT
'===========================================================================
' Code is provided without warranty and can be stolen and amended as required.
' Tom Parish
' TJP#tomparish.me.uk
' http://baldywrittencod.blogspot.com/2013/10/vba-modules-access-compact-repair.html
' DGF Help Contact: see BPMHelpContact module
'=========================================================================
'includes code from
'http://www.ammara.com/access_image_faq/recursive_folder_search.html
'tweaked slightly for improved error handling
' v2.02 bugfix preventing Compact when bAutoCompact set to False
' bugfix with "OLE waiting for another application" msgbox
' added "MB" to start & end sizes of message box at end
' v2.01 added size reduction to message box
' v2.00 added recurse
' v1.00 original version
Option Explicit
Function accSweepForDatabases(ByVal strFolder As String, Optional ByVal bIncludeSubfolders As Boolean = True _
, Optional bAutoCompact As Boolean = False) As String
'v2.02 2013-11-28 17:25
'sweeps path for .accdb and .mdb files, compacts and repairs all that it finds
'NB: leaves AutoCompact on Close as False unless specified, then leaves as True
'syntax:
' accSweepForDatabases "path", [False], [True]
'code for ActiveX CommandButton on sheet module named "admin" with two named ranges "vPath" and "vRecurse":
' accSweepForDatabases admin.Range("vPath"), admin.Range("vRecurse") [, admin.Range("vLeaveAutoCompact")]
Application.DisplayAlerts = False
Dim colFiles As New Collection, vFile As Variant, i As Integer, j As Integer, sFails As String, t As Single
Dim SizeBefore As Long, SizeAfter As Long
t = Timer
RecursiveDir colFiles, strFolder, "*.accdb", True 'comment this out if you only have Access 2003 installed
RecursiveDir colFiles, strFolder, "*.mdb", True
For Each vFile In colFiles
'Debug.Print vFile
SizeBefore = SizeBefore + (FileLen(vFile) / 1048576)
On Error GoTo CompactFailed
If InStr(vFile, "Geographical Configuration.accdb") > 0 Then MsgBox "yes"
acCompactRepair vFile, bAutoCompact
i = i + 1 'counts successes
GoTo NextCompact
CompactFailed:
On Error GoTo 0
j = j + 1 'counts failures
sFails = sFails & vFile & vbLf 'records failure
NextCompact:
On Error GoTo 0
SizeAfter = SizeAfter + (FileLen(vFile) / 1048576)
Next vFile
Application.DisplayAlerts = True
'display message box, mark end of process
accSweepForDatabases = i & " databases compacted successfully, taking " & CInt(Timer - t) & " seconds, and reducing storage overheads by " & Int(SizeBefore - SizeAfter) & "MB" & vbLf & vbLf & "Size Before: " & Int(SizeBefore) & "MB" & vbLf & "Size After: " & Int(SizeAfter) & "MB"
If j > 0 Then accSweepForDatabases = accSweepForDatabases & vbLf & j & " failures:" & vbLf & vbLf & sFails
MsgBox accSweepForDatabases, vbInformation, "accSweepForDatabases"
End Function
Function acCompactRepair(ByVal pthfn As String, Optional doEnable As Boolean = True) As Boolean
'v2.02 2013-11-28 16:22
'if doEnable = True will compact and repair pthfn
'if doEnable = False will then disable auto compact on pthfn
On Error GoTo CompactFailed
Dim A As Object
Set A = CreateObject("Access.Application")
With A
.OpenCurrentDatabase pthfn
.SetOption "Auto compact", True
.CloseCurrentDatabase
If doEnable = False Then
.OpenCurrentDatabase pthfn
.SetOption "Auto compact", doEnable
End If
.Quit
End With
Set A = Nothing
acCompactRepair = True
Exit Function
CompactFailed:
End Function
'source: http://www.ammara.com/access_image_faq/recursive_folder_search.html
'tweaked slightly for error handling
Private Function RecursiveDir(colFiles As Collection, _
strFolder As String, _
strFileSpec As String, _
bIncludeSubfolders As Boolean)
Dim strTemp As String
Dim colFolders As New Collection
Dim vFolderName As Variant
'Add files in strFolder matching strFileSpec to colFiles
strFolder = TrailingSlash(strFolder)
On Error Resume Next
strTemp = ""
strTemp = Dir(strFolder & strFileSpec)
On Error GoTo 0
Do While strTemp <> vbNullString
colFiles.Add strFolder & strTemp
strTemp = Dir
Loop
If bIncludeSubfolders Then
'Fill colFolders with list of subdirectories of strFolder
On Error Resume Next
strTemp = ""
strTemp = Dir(strFolder, vbDirectory)
On Error GoTo 0
Do While strTemp <> vbNullString
If (strTemp <> ".") And (strTemp <> "..") Then
If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
colFolders.Add strTemp
End If
End If
strTemp = Dir
Loop
'Call RecursiveDir for each subfolder in colFolders
For Each vFolderName In colFolders
Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
Next vFolderName
End If
End Function
Private Function TrailingSlash(strFolder As String) As String
If Len(strFolder) > 0 Then
If Right(strFolder, 1) = "\" Then
TrailingSlash = strFolder
Else
TrailingSlash = strFolder & "\"
End If
End If
End Function
For Access 2013, you could just do
Sendkeys "%fic"
This is the same as typing ALT, F, I, C on your keyboard.
It's probably a different sequence of letters for different versions, but the "%" symbol means "ALT", so keep that in the code. you may just need to change the letters, depending on what letters appear when you press ALT
Letters that appear when pressing ALT in Access 2013
In response to the excellent post by jdawgx:
Please be aware of a flaw in the code for CompactDB() above.
If the database's "AppTitle" property is defined (as happens when an "Application title" is defined in the database properties), this invalidates the "default window title" logic shown, which can cause the script to fail, or "behave unpredictably". So, adding code to check for an AppTitle property - or using API calls to read the Window title text from the Application.hWndAccessApp window could both be much more reliable.
Additionally, in Access 2019, we have observed that:
SendKeys "multi-key-string-here"
... may also not work reliably, needing to be replaced with:
SendKey (single-character)
'put a DoEvents or Sleep 150 here
SendKey (single-character)
'put a DoEvents or Sleep 150 here
SendKey (single-character)
'put a DoEvents or Sleep 150 here
SendKey (single-character)
...to get proper responses from the Access UI.
ALSO for Access 2019:
Sendkeys "%yc" ( <-- works for Access 2016)
is no longer correct.
it is now:
Sendkeys "%y1c"
...and if that little change wasn't enough - try to determine (in code) how to tell the difference between Access 2016 and 2019 - Good Luck!! because
Application.Version alone won't help, and even combining Application.Version and Application.Build is not a guarantee (unless you are in a controlled-release enterprise environment, and then it may work as the possible version/build #s in circulation should be more limited).
Yes it is simple to do.
Sub CompactRepair()
Dim control As Office.CommandBarControl
Set control = CommandBars.FindControl( Id:=2071 )
control.accDoDefaultAction
End Sub
Basically it just finds the "Compact and repair" menuitem and clicks it, programatically.
I did this many years back on 2003 or possibly 97, yikes!
If I recall you need to use one of the subcommands above tied to a timer. You cannot operate on the db with any connections or forms open.
So you do something about closing all forms, and kick off the timer as the last running method. (which will in turn call the compact operation once everything closes)
If you haven't figured this out I could dig through my archives and pull it up.
When the user exits the FE attempt to rename the backend MDB preferably with todays date in the name in yyyy-mm-dd format. Ensure you close all bound forms, including hidden forms, and reports before doing this. If you get an error message, oops, its busy so don't bother. If it is successful then compact it back.
See my Backup, do you trust the users or sysadmins? tips page for more info.
DBEngine.CompactDatabase source, dest
Application.SetOption "Auto compact", False '(mentioned above)
Use this with a button caption: "DB Not Compact On Close"
Write code to toggle the caption with "DB Compact On Close"
along with Application.SetOption "Auto compact", True
AutoCompact can be set by means of the button or by code, ex: after importing large temp tables.
The start up form can have code that turns off Auto Compact, so that it doesn't run every time.
This way, you are not trying to fight Access.
If you don't wish to use compact on close (eg, because the front-end mdb is a robot program that runs continually), and you don't want to create a separate mdb just for compacting, consider using a cmd file.
I let my robot.mdb check its own size:
FileLen(CurrentDb.Name))
If its size exceeds 1 GB, it creates a cmd file like this ...
Dim f As Integer
Dim Folder As String
Dim Access As String
'select Access in the correct PF directory (my robot.mdb runs in 32-bit MSAccess, on 32-bit and 64-bit machines)
If Dir("C:\Program Files (x86)\Microsoft Office\Office\MSACCESS.EXE") > "" Then
Access = """C:\Program Files (x86)\Microsoft Office\Office\MSACCESS.EXE"""
Else
Access = """C:\Program Files\Microsoft Office\Office\MSACCESS.EXE"""
End If
Folder = ExtractFileDir(CurrentDb.Name)
f = FreeFile
Open Folder & "comrep.cmd" For Output As f
'wait until robot.mdb closes (ldb file is gone), then compact robot.mdb
Print #f, ":checkldb1"
Print #f, "if exist " & Folder & "robot.ldb goto checkldb1"
Print #f, Access & " " & Folder & "robot.mdb /compact"
'wait until the robot mdb closes, then start it
Print #f, ":checkldb2"
Print #f, "if exist " & Folder & "robot.ldb goto checkldb2"
Print #f, Access & " " & Folder & "robot.mdb"
Close f
... launches the cmd file ...
Shell ExtractFileDir(CurrentDb.Name) & "comrep.cmd"
... and shuts down ...
DoCmd.Quit
Next, the cmd file compacts and restarts robot.mdb.
Try this. It works on the same database in which the code resides. Just call the CompactDB() function shown below. Make sure that after you add the function, you click the Save button in the VBA Editor window prior to running for the first time. I only tested it in Access 2010. Ba-da-bing, ba-da-boom.
Public Function CompactDB()
Dim strWindowTitle As String
On Error GoTo err_Handler
strWindowTitle = Application.Name & " - " & Left(Application.CurrentProject.Name, Len(Application.CurrentProject.Name) - 4)
strTempDir = Environ("Temp")
strScriptPath = strTempDir & "\compact.vbs"
strCmd = "wscript " & """" & strScriptPath & """"
Open strScriptPath For Output As #1
Print #1, "Set WshShell = WScript.CreateObject(""WScript.Shell"")"
Print #1, "WScript.Sleep 1000"
Print #1, "WshShell.AppActivate " & """" & strWindowTitle & """"
Print #1, "WScript.Sleep 500"
Print #1, "WshShell.SendKeys ""%yc"""
Close #1
Shell strCmd, vbHide
Exit Function
err_Handler:
MsgBox "Error " & Err.Number & ": " & Err.Description
Close #1
End Function
Please Note the following - all of you who favor doing a "Compact on Close" solution for MS-Access.
I used to prefer that option too, until one day, when I received the WORST error message possible from the DBEngine during a Compress & Repair operation:
"Table MSysObjects is corrupt - Table Truncated."
Now, you have probably never realized that THAT error is even a possibility.
Well, it is. And if you ever see it, your ENTIRE DATABASE, and EVERYTHING IN IT is now simply GONE. poof!
What is funny about that is that Access will let you actually reopen the "fixed" database, only, the Access window and menu items are all now utterly useless (except to close the DB and exit access again) because ALL the tables (including the other MSYS* tables, forms, queries, reports, code modules, & macros) are simply gone - and with the disk space previously allocated to them released to the tender mercies of the Windows OS - unless you have additional protection than the bog-standard recycle bin, which won't help you either.
So, if you REALLY want to accept the risk of Compact on Close completely clobbering your database - with NO POSSIBILITY of recovering it, then please...do carry on.
If, OTOH, like me you find that risk an unacceptable one, well, don't enable C&R-on-Close - ever again.
Check out this solution VBA Compact Current Database.
Basically it says this should work
Public Sub CompactDB()
CommandBars("Menu Bar").Controls("Tools").Controls ("Database utilities"). _
Controls("Compact and repair database...").accDoDefaultAction
End Sub
There's also Michael Kaplan's SOON ("Shut One, Open New") add-in. You'd have to chain it, but it's one way to do this.
I can't say I've had much reason to ever want to do this programatically, since I'm programming for end users, and they are never using anything but the front end in the Access user interface, and there's no reason to regularly compact a properly-designed front end.