How to use arrays to access files in folder in vba? - vba

had some troubles with this code. It actually is intended to allocate values to the dir1array(ctr1) and dir2array(ctr2) by looping thru all the files in the directory/folder; Is there a way to make this array work ?
Option Explicit
'*********************************************************************
'* Verify if files have the same name before proceeding to compare *
'* their length *
'* DYNAMIC ARRAYs *
'*********************************************************************
Sub findMatchFilenames()
'Dim fso As fileSystemObject
Dim objMapinfo
Dim fso As New Scripting.FileSystemObject
Dim dir1 As Folder
Dim dir2 As Folder
Dim file1 As File
Dim file2 As File
Dim dir1array() As String
Dim dir2array() As String
ReDim dir1array(0 To 100) As String
ReDim dir2array(0 To 100) As String
Dim ctr1 As Integer
Dim ctr2 As Integer
Dim lLen1 As Long, lLen2 As Long
Dim myFile As String, text As String, textline As String
Set fso = New FileSystemObject
Set dir1 = fso.GetFolder("c:\Temp\")
Set dir2 = fso.GetFolder("c:\Tempo\")
ctr1 = 0
For Each file1 In dir1.Files
ctr2 = 0
For Each file2 In dir2.Files
dir1array(ctr1) = file1.Name
dir2array(ctr2) = file2.Name
If dir1array(ctr1) = dir2array(ctr2) Then
MsgBox "" & dir1array(ctr1) & "" & dir2array(ctr2)
Debug.Print file1.Name & " matches " & file2.Name
lLen1 = FileLen(file1)
lLen2 = FileLen(file2)
If lLen1 <> lLen2 Then
Exit Sub
Else
MsgBox "The files have the same length"
End If
End If
ctr2 = ctr2 + 1
Next file2
ctr1 = ctr1 + 1
Next file1
Close #1
End Sub

The following is a variation of your code, but does not use arrays.
Option Explicit
Sub findMatchFilenames()
Dim lLen1 As Long
Dim lLen2 As Long
Dim oFSO As New Scripting.FileSystemObject
Dim dir1 As Folder
Dim dir2 As Folder
Dim oFile1 As File
Dim oFile2 As File
Dim strFolder1 As String
Dim strFolder2 As String
Close #1 ' I always close first when testing (in case I don't get to normal close)
Close #2
Open "C:\Temp\" & Format(Now(), "_SAME_yyyy-mm-dd_hh-mm") & ".txt" For Output As #1
Open "C:\Temp\" & Format(Now(), "_Different_yyyy-mm-dd_hh-mm") & ".txt" For Output As #2
Set oFSO = New FileSystemObject
strFolder1 = "c:\Temp\"
strFolder2 = "c:\Tempo\"
Set dir1 = oFSO.GetFolder(strFolder1)
Set dir2 = oFSO.GetFolder(strFolder2)
For Each oFile1 In dir1.Files
If oFSO.FileExists(strFolder2 & oFile1.Name) Then ' If it matches same name
Set oFile2 = oFSO.GetFile(strFolder2 & oFile1.Name)
If oFile1.Size = oFile2.Size Then
Print #1, oFile1.Name & vbTab & "File found in both folders; Size is the same;"
Debug.Print oFile1.Name & vbTab & "File found in both folders; Size is the same;"
Else
Print #1, oFile1.Name & vbTab & "Found in both folders; Size is DIFFERENT; " & oFile1.Size & " vs: " & oFile2.Size
Debug.Print oFile1.Name & vbTab & "Found in both folders; Size is DIFFERENT; " & oFile1.Size & " vs: " & oFile2.Size
End If
Else ' Same file not found.
Debug.Print "File not present in 2nd folder: " & oFile1.Name
Print #1, oFile1.Name & vbTab & "File NOT found in second folder;"
End If
Next oFile1
Set oFile1 = Nothing
Set oFile2 = Nothing
Set oFSO = Nothing
Close #1
Close #2
End Sub

Related

How to loop through sub folders of folder A to get file name in each subfolder and copy other file with same name from folder B using VBA

There is folder A which contains multiple subfolders like A1,A2, A3 etc which every subfolder has mostly one sometimes 2 word files with the name(eg file_a1) in it. Then, there is other folder B (not a subfolder of A) which contains multiple word files with standard similar (file_a1_XZ) names.
I want to loop in subfolders of A and copy word files from B to respective sub folder e.g A1
File Structure:
Parent Folder
|
|
----Parent B
|
|
--- B
|
-file_a1_XZ
-file_a2_XZ
----Parent A
|
|
--- A
|
|
-- A1
|
-file_a1
-- A2
|
-file_a2
Move Files to Specific Folders Using Dir
Moves files from B to subfolders of A i.e. the filenames contain the names of the subfolders.
Option Explicit
Sub MoveFiles()
Const sFolderPath As String = "C:\Test\T2022\71752347\B\"
Const dFolderPath As String = "C:\Test\T2022\71752347\A\"
Const sExtensionPattern As String = ".doc*"
Dim dFolderName As String: dFolderName = Dir(dFolderPath, vbDirectory)
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Do Until Len(dFolderName) = 0
If dFolderName <> "." And dFolderName <> ".." Then
dict(dFolderName) = Empty
End If
dFolderName = Dir
Loop
Dim Key As Variant
Dim sFileName As String
Dim fCount As Long
For Each Key In dict.Keys
sFileName = Dir(sFolderPath & "*" & Key & "*" & sExtensionPattern)
Do Until Len(sFileName) = 0
fCount = fCount + 1
FileCopy sFolderPath & sFileName, _
dFolderPath & Key & "\" & sFileName
Kill sFolderPath & sFileName
sFileName = Dir
Loop
Next
MsgBox "Files moved: " & fCount, vbInformation
End Sub
If the files in B are in various subfolders, use the following.
Sub MoveFiles()
Const sFolderPath As String = "C:\Test\T2022\71752347\B\"
Const dFolderPath As String = "C:\Test\T2022\71752347\A\"
Const sExtensionPattern As String = ".doc*"
Dim dFolderName As String: dFolderName = Dir(dFolderPath, vbDirectory)
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Do Until Len(dFolderName) = 0
If dFolderName <> "." And dFolderName <> ".." Then
dict(dFolderName) = Empty
End If
dFolderName = Dir
Loop
Dim sFilePaths() As String
Dim sFilePath As String
Dim dFilePath As String
Dim Key As Variant
Dim f As Long
Dim fCount As Long
For Each Key In dict.Keys
sFilePaths = ArrFilePaths(sFolderPath, _
"*" & Key & "*" & sExtensionPattern)
For f = 0 To UBound(sFilePaths)
fCount = fCount + 1
sFilePath = sFilePaths(f)
dFilePath = dFolderPath & Key & "\" & Right(sFilePath, _
Len(sFilePath) - InStrRev(sFilePath, "\"))
FileCopy sFilePath, dFilePath
Kill sFilePath
Next f
Next Key
MsgBox "Files moved: " & fCount, vbInformation
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the file paths of the files in a folder
' in a zero-based string array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ArrFilePaths( _
ByVal FolderPath As String, _
Optional ByVal FilePattern As String = "*.*", _
Optional ByVal DirSwitches As String = "/s/b/a-d") _
As String()
Const ProcName As String = "ArrFilePaths"
On Error GoTo ClearError
' Ensuring that a string array is passed if an error occurs.
ArrFilePaths = Split("") ' LB = 0 , UB = -1
Dim pSep As String: pSep = Application.PathSeparator
If Right(FolderPath, 1) <> pSep Then FolderPath = FolderPath & pSep
Dim ExecString As String ' '%comspec%' or 'cmd.exe' ?
ExecString = "%comspec% /c Dir """ _
& FolderPath & FilePattern & """ " & DirSwitches
Dim Arr() As String: Arr = Split(CreateObject("WScript.Shell") _
.Exec(ExecString).StdOut.ReadAll, vbCrLf)
If UBound(Arr) > 0 Then
ReDim Preserve Arr(0 To UBound(Arr) - 1)
End If
ArrFilePaths = Arr
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Function

Open method not working to open ppts from a ppt

I'm having a bit of trouble here. My code stops with a Run-time error -2147467259 (80004005) Mehod 'Open' of object 'Presentations: failed.
This code presents a warning, prompts for source and target folder and loops through all files in the source folder, opening each file and exporting each slide as an individual file, and again until the last file in the folder.
I put a couple of msgboxes to see if it was a problem with the names, re-wrote the open file segment based on some code from MVP Andy Pope, yet nothing.
Any help is deeply appreciated.
Sub ExportIndividualSlides()
''Application.DisplayAlerts = False
Dim ObjPPAPP As New PowerPoint.Application
Dim objPPPres As PowerPoint.Presentation
Dim objPPSlide As PowerPoint.Slide
'Initial directory path.
Dim SourceFolder As String
Dim TargetFolder As String
SourceFolder = "c:\source"
TargetFolder = "c:\target"
Dim Slide As Long
Dim SourcePresentation As Presentation
Dim SourcePresentationName As String
Dim TargetFileName As String
Dim SourceNamePath
Debug.Print "-- Start --------------------------------"
ActiveWindow.ViewType = ppViewNormal
'Loop through ppt* files only in source folder
SourcePresentationName = Dir(SourceFolder & "\*.ppt*")
MsgBox "SPN:" & SourcePresentationName
While (SourcePresentationName <> "")
SourceNamePath = SourceFolder & "\" & SourcePresentationName
Debug.Print " SourceNamePath"
MsgBox SourceNamePath
Set ObjPPAPP = New PowerPoint.Application
ObjPPAPP.Visible = True
Set objPPPres = ObjPPAPP.Presentations.Open(SourceNamePath)
' On Error GoTo errorhandler
' Open source files
Set SourcePresentation = Presentations.Open(FileName:=SourcePresentationName, WithWindow:=False)
Debug.Print " SourcePresentation: " & SourcePresentation.Name
' Loop through slides
For Slide = 1 To SourcePresentation.Slides.Count
Debug.Print " Slide: " & Slide
' Create a unique filename and save a copy of each slide
TargetFileName = Left(SourcePresentation.Name, InStrRev(SourcePresentation.Name, ".") - 1) & " [" & Slide & "].pptx"
TargetNamePath = TargetFolder & "\" & TargetFileName
Debug.Print " TargetNamePath: " & TargetNamePath
SourcePresentation.Slides(Slide).Export TargetNamePath, "PPTX"
Next Slide
objPPPres = Nothing
SourcePresentation.Close
SourcePresentationName = Dir
Wend
On Error GoTo 0
Exit Sub
errorhandler:
Debug.Print Err, Err.Description
Resume Next
End Sub
This worked for me:
Sub ExportIndividualSlides()
'use const for fixed values
Const SOURCE_FOLDER As String = "c:\source\" 'include terminal \
Const TARGET_FOLDER As String = "c:\target\"
Dim objPres As PowerPoint.Presentation
Dim Slide As Long
Dim SourcePresentationName As String
Dim TargetFileName As String
Dim TargetNamePath As String
Dim SourceNamePath
Debug.Print "-- Start --------------------------------"
ActiveWindow.ViewType = ppViewNormal
On Error GoTo errorhandler
'Loop through ppt* files only in source folder
SourcePresentationName = Dir(SOURCE_FOLDER & "*.ppt*")
Do While Len(SourcePresentationName) > 0
SourceNamePath = SOURCE_FOLDER & SourcePresentationName
Debug.Print "Opening: " & SourceNamePath
Set objPres = Presentations.Open(SourceNamePath)
' Loop through slides
For Slide = 1 To objPres.Slides.Count
Debug.Print " Slide: " & Slide
' Create a unique filename and save a copy of each slide
TargetFileName = Left(objPres.Name, InStrRev(objPres.Name, ".") - 1) & " [" & Slide & "].pptx"
TargetNamePath = TARGET_FOLDER & TargetFileName
Debug.Print " TargetNamePath: " & TargetNamePath
objPres.Slides(Slide).Export TargetNamePath, "PPTX"
Next Slide
objPres.Close
SourcePresentationName = Dir() 'next file
Loop
Exit Sub
errorhandler:
Debug.Print Err, Err.Description
Resume Next
End Sub

Read only one record from Multiple text files into Excel using VBA

I have multiple txt files in a folder, which are tab delimited. Each of these files have a column called EngagementId, which is the same value, irrespective of number of records. However, it changes for every txt file, which is what I want to capture.
I am trying to get the file name in the first row. The GetFileNames() works for that (as pointed out in the comments)
Sub GetFileNames()
Dim sPath As String
Dim sFile As String
Dim iRow As Integer
Dim iCol As Integer
Dim splitFile As Variant
'specify directory to use - must end in "\"
sPath = ActiveWorkbook.Path
iRow = 0
sFile = Dir(sPath & "\Individual Reports\")
Do While sFile <> ""
iRow = iRow + 1
splitFile = Split(sFile, ".txt")
For iCol = 0 To UBound(splitFile)
Sheet1.Cells(iRow, iCol + 1) = splitFile(iCol)
Next iCol
sFile = Dir ' Get next filename
Loop
End Sub
Each of these txt files have one column (which is in the 13th position in each of the text files), called "EngagementId". I want to pull only the first "Engagement Id", which is from the 2nd row(since the first row contains headers).
Sub Extractrec()
Dim filename As String, nextrow As Long, MyFolder As String
Dim MyFile As String, text As String, textline As String
MyFolder = ActiveWorkbook.Path
MyFile = Dir(MyFolder & "\Individual Reports\*.txt")
Do While MyFile <> ""
Open (MyFolder & MyFile) For Input As #1
Do Until EOF(1)
Line Input #1, LineFromFile
LineItems = Split(LineFromFile, "\t") 'second loop text is already stored
'-> see reset text
Sheet1.Cells(iRow, iCol + 2).Value = LineItems(13, 2)
Loop
Close #1
Loop
Using an ADODB.Recordset to query would be more versatile.
Sub Example()
On Error Resume Next
Dim rs As Object, f As Object, conn As Object
Dim FolderPath As String, FileName As String, FilterString As String
FolderPath = "C:\Users\best buy\Downloads\stackoverfow\Sample Data File\"
FileName = "example.csv"
FilterString = "WHERE EngagementId = 20"
Set rs = getDataset(FolderPath, FileName, FilterString)
Do While Not rs.BOF And Not rs.EOF
Debug.Print rs.Fields("EngagementId")
Debug.Print rs.Fields("Company")
Debug.Print rs.Fields("City")
Debug.Print rs.Fields("State")
rs.MoveNext
Loop
Set conn = rs.ActiveConnection
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
End Sub
Function getDataset(FolderPath As String, FileName As String, FilterString As String) As Object
Dim conn As Object, rs As Object
Set conn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
conn.Open ("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & FolderPath & ";" & _
"Extended Properties=""text; HDR=Yes; FMT=Delimited; IMEX=1;""")
rs.ActiveConnection = conn
rs.Source = "SELECT * FROM " & FileName & " " & FilterString
rs.Open
Set getDataset = rs
End Function
Since you only need the second line of each file, you don't need to loop, just read and discard the fist line, then read and split the second one:
Open (MyFolder & MyFile) For Input As #1 'MyFolder & MyFile won't be the correct name (probably should be MyFolder & "\Individual Reports\" & MyFile)
Line Input #1, LineFromFile 'line to discard
Line Input #1, LineFromFile 'line to use
LineItems = Split(LineFromFile, vbTab)
Sheet1.Cells(someplace).Value = LineItems(13) ' replace some place with the correct value that we don't know
Close #1

Exporting MS Access Forms and Class / Modules Recursively to text files?

I found some code on an ancient message board that nicely exports all of the VBA code from classes, modules and forms (see below):
Option Explicit
Option Compare Database
Function SaveToFile() 'Save the code for all modules to files in currentDatabaseDir\Code
Dim Name As String
Dim WasOpen As Boolean
Dim Last As Integer
Dim I As Integer
Dim TopDir As String, Path As String, FileName As String
Dim F As Long 'File for saving code
Dim LineCount As Long 'Line count of current module
I = InStrRev(CurrentDb.Name, "\")
TopDir = VBA.Left(CurrentDb.Name, I - 1)
Path = TopDir & "\" & "Code" 'Path where the files will be written
If (Dir(Path, vbDirectory) = "") Then
MkDir Path 'Ensure this exists
End If
'--- SAVE THE STANDARD MODULES CODE ---
Last = Application.CurrentProject.AllModules.Count - 1
For I = 0 To Last
Name = CurrentProject.AllModules(I).Name
WasOpen = True 'Assume already open
If Not CurrentProject.AllModules(I).IsLoaded Then
WasOpen = False 'Not currently open
DoCmd.OpenModule Name 'So open it
End If
LineCount = Access.Modules(Name).CountOfLines
FileName = Path & "\" & Name & ".vba"
If (Dir(FileName) <> "") Then
Kill FileName 'Delete previous version
End If
'Save current version
F = FreeFile
Open FileName For Output Access Write As #F
Print #F, Access.Modules(Name).Lines(1, LineCount)
Close #F
If Not WasOpen Then
DoCmd.Close acModule, Name 'It wasn't open, so close it again
End If
Next
'--- SAVE FORMS MODULES CODE ---
Last = Application.CurrentProject.AllForms.Count - 1
For I = 0 To Last
Name = CurrentProject.AllForms(I).Name
WasOpen = True
If Not CurrentProject.AllForms(I).IsLoaded Then
WasOpen = False
DoCmd.OpenForm Name, acDesign
End If
LineCount = Access.Forms(Name).Module.CountOfLines
FileName = Path & "\" & Name & ".vba"
If (Dir(FileName) <> "") Then
Kill FileName
End If
F = FreeFile
Open FileName For Output Access Write As #F
Print #F, Access.Forms(Name).Module.Lines(1, LineCount)
Close #F
If Not WasOpen Then
DoCmd.Close acForm, Name
End If
Next
MsgBox "Created source files in " & Path
End Function
However, this code does not solve my problem since I have 110 ms-access *.mdb's that I need to export the vba from into text files suitable for grepping.
The paths to the 110 files I'm interested in are already stored in a table, and my code already gained this information recursively (along with some other filtering)...so the recursive part is done.
Most of these files are opened by a single access user security file, an .mdw and I have tried several methods of opening them. ADO and ADOX worked great when I was searching for linked tables in these directories...but the code above involves being inside the database you are exporting the data from, and I want to be able to do this from a separate database that opens all of the mdbs and performs the export on each of them.
One of my attempts at this involved using the PrivDBEngine class to connect to the databases externally, but it doesn't allow me to access the Application object which is what the export code above requires.
Private Sub exportToFile(db_path As String, db_id As String, loginInfo As AuthInfoz, errFile As Variant)
Dim pdbeNew As PrivDBEngine
Dim db As DAO.Database
Dim ws As DAO.Workspace
Dim rst As DAO.Recordset
Dim cn As ADODB.Connection ' ADODB.Connection
Dim rs As ADODB.Recordset ' ADODB.Recordset
Dim strConnect As String
Dim blnReturn As Boolean
Dim Doc As Document
Dim mdl As Module
Dim lngCount As Long
Dim strForm As String
Dim strOneLine As String
Dim sPtr As Integer
Set fso = CreateObject("Scripting.FileSystemObject")
Set exportFile = fso.CreateTextFile("E:\Tickets\CSN1006218\vbacode\" & db_id & ".txt", ForAppending)
' Export stuff...
On Error GoTo errorOut
Set pdbeNew = New PrivDBEngine
With pdbeNew
.SystemDB = loginInfo.workgroup
.DefaultUser = loginInfo.username
.DefaultPassword = loginInfo.password
End With
Set ws = pdbeNew.Workspaces(0)
Set db = ws.OpenDatabase(db_path)
For Each Doc In db.Containers("Modules").Documents
DoCmd.OpenModule Doc.Name
Set mdl = Modules(Doc.Name)
exportFile.WriteLine ("---------------------")
exportFile.WriteLine ("Module Name: " & Doc.Name)
exportFile.WriteLine ("Module Type: " & mdl.Type)
exportFile.WriteLine ("---------------------")
lngCount = lngCount + mdl.CountOfLines
'For i = 1 To lngCount
' strOneLine = mdl.Lines(i, 1)
' exportFile.WriteLine (strOneLine)
'Next i
Set mdl = Nothing
DoCmd.Close acModule, Doc.Name
Next Doc
Close_n_exit:
If Not (db Is Nothing) Then
Call wk.Close
Set wk = Nothing
Call db.Close
End If
Call exportFile.Close
Set exportFile = Nothing
Set fso = Nothing
Exit Sub
errorOut:
Debug.Print "----------------"
Debug.Print "BEGIN: Err"
If err.Number <> 0 Then
Msg = "Error # " & Str(err.Number) & " was generated by " _
& err.Source & Chr(13) & "Error Line: " & Erl & Chr(13) & err.Description
'MsgBox Msg, , "Error", err.HelpFile, err.HelpContext
Debug.Print Msg
End If
Resume Close_n_exit
End Sub
Is there anyway to access the application object from a PrivDBEngine? I have alot of modules that need grepping.
You can also try this code. It will preserve the items' filetypes (.bas, .cls, .frm)
Remember to refer to / Check the Microsoft Visual Basic For Applications Extensibility Library in
VBE > Tools > References
Public Sub ExportAllCode()
Dim c As VBComponent
Dim Sfx As String
For Each c In Application.VBE.VBProjects(1).VBComponents
Select Case c.Type
Case vbext_ct_ClassModule, vbext_ct_Document
Sfx = ".cls"
Case vbext_ct_MSForm
Sfx = ".frm"
Case vbext_ct_StdModule
Sfx = ".bas"
Case Else
Sfx = ""
End Select
If Sfx <> "" Then
c.Export _
Filename:=CurrentProject.Path & "\" & _
c.Name & Sfx
End If
Next c
End Sub
You can use the Access.Application object.
Also, in order to avoid multiple confirmation dialogs when opening the databases, just change the security level in Tools / Macros / Security.
And to open multiple databases with user/password you can join the workgroup (Tools / Security / Workgroup administrator) and log in with the desired user/password (from the database with the SaveToFile function), then run the code. Remember, later on, to join the default workgroup (you can try to join an inexistent workgroup and access will revert to the default).
Option Explicit
Option Compare Database
'Save the code for all modules to files in currentDatabaseDir\Code
Public Function SaveToFile()
On Error GoTo SaveToFile_Err
Dim Name As String
Dim WasOpen As Boolean
Dim Last As Integer
Dim i As Integer
Dim TopDir As String, Path As String, FileName As String
Dim F As Long 'File for saving code
Dim LineCount As Long 'Line count of current module
Dim oApp As New Access.Application
' Open remote database
oApp.OpenCurrentDatabase ("D:\Access\myDatabase.mdb"), False
i = InStrRev(oApp.CurrentDb.Name, "\")
TopDir = VBA.Left(oApp.CurrentDb.Name, i - 1)
Path = TopDir & "\" & "Code" 'Path where the files will be written
If (Dir(Path, vbDirectory) = "") Then
MkDir Path 'Ensure this exists
End If
'--- SAVE THE STANDARD MODULES CODE ---
Last = oApp.CurrentProject.AllModules.Count - 1
For i = 0 To Last
Name = oApp.CurrentProject.AllModules(i).Name
WasOpen = True 'Assume already open
If Not oApp.CurrentProject.AllModules(i).IsLoaded Then
WasOpen = False 'Not currently open
oApp.DoCmd.OpenModule Name 'So open it
End If
LineCount = oApp.Modules(Name).CountOfLines
FileName = Path & "\" & Name & ".vba"
If (Dir(FileName) <> "") Then
Kill FileName 'Delete previous version
End If
'Save current version
F = FreeFile
Open FileName For Output Access Write As #F
Print #F, oApp.Modules(Name).Lines(1, LineCount)
Close #F
If Not WasOpen Then
oApp.DoCmd.Close acModule, Name 'It wasn't open, so close it again
End If
Next
'--- SAVE FORMS MODULES CODE ---
Last = oApp.CurrentProject.AllForms.Count - 1
For i = 0 To Last
Name = oApp.CurrentProject.AllForms(i).Name
WasOpen = True
If Not oApp.CurrentProject.AllForms(i).IsLoaded Then
WasOpen = False
oApp.DoCmd.OpenForm Name, acDesign
End If
LineCount = oApp.Forms(Name).Module.CountOfLines
FileName = Path & "\" & Name & ".vba"
If (Dir(FileName) <> "") Then
Kill FileName
End If
F = FreeFile
Open FileName For Output Access Write As #F
Print #F, oApp.Forms(Name).Module.Lines(1, LineCount)
Close #F
If Not WasOpen Then
oApp.DoCmd.Close acForm, Name
End If
Next
'--- SAVE REPORTS MODULES CODE ---
Last = oApp.CurrentProject.AllReports.Count - 1
For i = 0 To Last
Name = oApp.CurrentProject.AllReports(i).Name
WasOpen = True
If Not oApp.CurrentProject.AllReports(i).IsLoaded Then
WasOpen = False
oApp.DoCmd.OpenReport Name, acDesign
End If
LineCount = oApp.Reports(Name).Module.CountOfLines
FileName = Path & "\" & Name & ".vba"
If (Dir(FileName) <> "") Then
Kill FileName
End If
F = FreeFile
Open FileName For Output Access Write As #F
Print #F, oApp.Reports(Name).Module.Lines(1, LineCount)
Close #F
If Not WasOpen Then
oApp.DoCmd.Close acReport, Name
End If
Next
MsgBox "Created source files in " & Path
' Reset the security level
Application.AutomationSecurity = msoAutomationSecurityByUI
SaveToFile_Exit:
If Not oApp.CurrentDb Is Nothing Then oApp.CloseCurrentDatabase
If Not oApp Is Nothing Then Set oApp = Nothing
Exit function
SaveToFile_Err:
MsgBox ("Error " & Err.Number & vbCrLf & Err.Description)
Resume SaveToFile_Exit
End Function
I have added code for the Reports modules. When I get some time I'll try to refactor the code.
I find this a great contribution. Thanks for sharing.
Regards
================= EDIT ==================
After a while I found the way to export the whole database (tables and queries included) and have been using it for version control in Git.
Of course, if you have really big tables what you really want is a backup. This I use with the tables in its initial state, many of them empty, for development purposes only.
Option Compare Database
Option Explicit
Private Const VB_MODULE As Integer = 1
Private Const VB_CLASS As Integer = 2
Private Const VB_FORM As Integer = 100
Private Const EXT_TABLE As String = ".tbl"
Private Const EXT_QUERY As String = ".qry"
Private Const EXT_MODULE As String = ".bas"
Private Const EXT_CLASS As String = ".cls"
Private Const EXT_FORM As String = ".frm"
Private Const CODE_FLD As String = "code"
Private Const mblnSave As Boolean = True ' False: just generate the script
'
'
Public Sub saveAllAsText()
Dim oTable As TableDef
Dim oQuery As QueryDef
Dim oCont As Container
Dim oForm As Document
Dim oModule As Object
Dim FSO As Object
Dim strPath As String
Dim strName As String
Dim strFileName As String
'**
On Error GoTo errHandler
strPath = CurrentProject.path
Set FSO = CreateObject("Scripting.FileSystemObject")
strPath = addFolder(FSO, strPath, Application.CurrentProject.name & "_" & CODE_FLD)
strPath = addFolder(FSO, strPath, Format(Date, "yyyy.mm.dd"))
For Each oTable In CurrentDb.TableDefs
strName = oTable.name
If left(strName, 4) <> "MSys" Then
strFileName = strPath & "\" & strName & EXT_TABLE
If mblnSave Then Application.ExportXML acExportTable, strName, strFileName, strFileName & ".XSD", strFileName & ".XSL", , acUTF8, acEmbedSchema + acExportAllTableAndFieldProperties
Debug.Print "Application.ImportXML """ & strFileName & """, acStructureAndData"
End If
Next
For Each oQuery In CurrentDb.QueryDefs
strName = oQuery.name
If left(strName, 1) <> "~" Then
strFileName = strPath & "\" & strName & EXT_QUERY
If mblnSave Then Application.SaveAsText acQuery, strName, strFileName
Debug.Print "Application.LoadFromText acQuery, """ & strName & """, """ & strFileName & """"
End If
Next
Set oCont = CurrentDb.Containers("Forms")
For Each oForm In oCont.Documents
strName = oForm.name
strFileName = strPath & "\" & strName & EXT_FORM
If mblnSave Then Application.SaveAsText acForm, strName, strFileName
Debug.Print "Application.LoadFromText acForm, """ & strName & """, """ & strFileName & """"
Next
strPath = addFolder(FSO, strPath, "modules")
For Each oModule In Application.VBE.ActiveVBProject.VBComponents
strName = oModule.name
strFileName = strPath & "\" & strName
Select Case oModule.Type
Case VB_MODULE
If mblnSave Then oModule.Export strFileName & EXT_MODULE
Debug.Print "Application.VBE.ActiveVBProject.VBComponents.Import """ & strFileName & EXT_MODULE; """"
Case VB_CLASS
If mblnSave Then oModule.Export strFileName & EXT_CLASS
Debug.Print "Application.VBE.ActiveVBProject.VBComponents.Import """ & strFileName & EXT_CLASS; """"
Case VB_FORM
' Do not export form modules (already exported the complete forms)
Case Else
Debug.Print "Unknown module type: " & oModule.Type, oModule.name
End Select
Next
If mblnSave Then MsgBox "Files saved in " & strPath, vbOKOnly, "Export Complete"
Exit Sub
errHandler:
MsgBox "Error " & Err.Number & ": " & Err.Description & vbCrLf
Stop: Resume
End Sub
'
'
' Create a folder when necessary. Append the folder name to the given path.
'
Private Function addFolder(ByRef FSO As Object, ByVal strPath As String, ByVal strAdd As String) As String
addFolder = strPath & "\" & strAdd
If Not FSO.FolderExists(addFolder) Then MkDir addFolder
End Function
'
EDIT2
When saving queries, they often get changed in trivial aspects which I don't want to get commited to the git repository. I changed the code so it just exports the SQL code in the query.
For Each oQuery In CurrentDb.QueryDefs
strName = oQuery.Name
If Left(strName, 1) <> "~" Then
strFileName = strPath & "\" & strName & EXT_QUERY
saveQueryAsText oQuery, strFileName
End If
Next
'
' Save just the SQL code in the query
'
Private Sub saveQueryAsText(ByVal oQuery As QueryDef, ByVal strFileName As String)
Dim intFile As Integer
intFile = FreeFile
Open strFileName For Output As intFile
Print #intFile, oQuery.sql
Close intFile
End Sub
And to import and recreate the database I use another module, mDBImport. In the repository, the modules are contained in the 'modules' subfolder:
Private Const repoPath As String = "C:\your\repository\path\here"
Public Sub loadFromText(Optional ByVal strPath As String = REPOPATH)
dim FSO as Object
Set oFolder = FSO.GetFolder(strPath)
Set FSO = CreateObject("Scripting.FileSystemObject")
For Each oFile In oFolder.files
Select Case FSO.GetExtensionName(oFile.Path)
Case "tbl"
Application.ImportXML oFile.Path, acStructureAndData
Case "qry"
intFile = FreeFile
Open oFile.Path For Input As #intFile
strSQL = Input$(LOF(intFile), intFile)
Close intFile
CurrentDb.CreateQueryDef Replace(oFile.Name, ".qry", ""), strSQL
Case "frm"
Application.loadFromText acForm, Replace(oFile.Name, ".frm", ""), oFile.Path
End Select
Next oFile
' load modules and class modules
strPath = FSO.BuildPath(strPath, "modules")
If Not FSO.FolderExists(strPath) Then Err.Raise vbObjectError + 4, , "Modules folder doesn't exist!"
Set oFolder = FSO.GetFolder(strPath)
With Application.VBE.ActiveVBProject.VBComponents
For Each oFile In oFolder.files
Select Case FSO.GetExtensionName(oFile.Path)
Case "cls", "bas"
If oFile.Name <> "mDBImport.bas" Then .Import oFile.Path
End Select
Next oFile
End With
MsgBox "The database objects where correctly loaded.", vbOKOnly, "LoadFromText"
Exit Sub
errHandler:
MsgBox Err.Description, vbCritical + vbOKOnly
End Sub
Like for MS Excel, you can also use a loop over the Application.VBE.VBProjects(1).VBComponents and use the Export method to export your modules/classes/forms:
Const VB_MODULE = 1
Const VB_CLASS = 2
Const VB_FORM = 100
Const EXT_MODULE = ".bas"
Const EXT_CLASS = ".cls"
Const EXT_FORM = ".frm"
Const CODE_FLD = "Code"
Sub ExportAllCode()
Dim fileName As String
Dim exportPath As String
Dim ext As String
Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
' Set export path and ensure its existence
exportPath = CurrentProject.path & "\" & CODE_FLD
If Not FSO.FolderExists(exportPath) Then
MkDir exportPath
End If
' The loop over all modules/classes/forms
For Each c In Application.VBE.VBProjects(1).VBComponents
' Get the filename extension from type
ext = vbExtFromType(c.Type)
If ext <> "" Then
fileName = c.name & ext
debugPrint "Exporting " & c.name & " to file " & fileName
' THE export
c.Export exportPath & "\" & fileName
Else
debugPrint "Unknown VBComponent type: " & c.Type
End If
Next c
End Sub
' Helper function that translates VBComponent types into file extensions
' Returns an empty string for unknown types
Function vbExtFromType(ByVal ctype As Integer) As String
Select Case ctype
Case VB_MODULE
vbExtFromType = EXT_MODULE
Case VB_CLASS
vbExtFromType = EXT_CLASS
Case VB_FORM
vbExtFromType = EXT_FORM
End Select
End Function
Only takes a fraction of a second to execute.
Cheers
Lovely answer Clon.
Just a slight variation if you are trying to open MDBs that has a startup form and/or a AutoExec macro and above doesn't always seem to work reliably.
Looking at this answer on another website: By pass startup form / macros and scrolling almost to the end of the discussion is some code which temporarily gets rid of the startup form settings and extracts the AutoExec macro to your database before writing over it with an TempAutoExec macro (which does nothing), does some work (between lines 'Read command bars and app.CloseCurrentDatabase) and then fixes everything back again.
IDK why no one has suggested this before, but here is a small piece of code I use for this. Pretty simple and straightforward
Public Sub VBAExportModule()
On Error GoTo Errg
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT MSysObjects.Name FROM MSysObjects WHERE Type=-32761", dbOpenDynaset, dbSeeChanges)
Do Until rs.EOF
Application.SaveAsText acModule, rs("Name"), "C:\" & rs("Name") & ".txt"
rs.MoveNext
Loop
Cleanup:
If Not rs Is Nothing Then rs.Close
Set rs = Nothing
Exit Sub
Errg:
GoTo Cleanup
End Sub
another way is keep most used code in one external master.mdb
and join it to any count of *.mdbs trough Modules->Tools->References->Browse->...\master.mdb
the only problem in old 97 Access you can Debug, Edit and Save directly in destination.mdb,
but in all newer, since MA 2000, 'Save' option is gone and any warnings on close unsaved code

File searching in VBA

I wrote a vba code that browse all path folder and search for "strings.xml" file.
Dim oFS As Office.FileSearch
Dim i As Integer
Set oFS = Application.FileSearch
With oFS
.NewSearch
.FileType = msoFileTypeAllFiles
.Filename = "strings.xml"
.LookIn = "D:\Workspace"
.SearchSubFolders = True
.Execute
MsgBox "Finish ! " & .FoundFiles.Count & " item found !"
End With
However, in my workspace I have many "strings.xml" files that this current code locates and but I only want to find the "strings.xml" within a specific subfolder; e.g. ./values/strings.xml files.
The following will look recursively under your root working folder for Values\Strings.xml matches and list them in a Scripting.Dictionary object.
The main file/folder search is performed by the simple Dir function.
Sub dir_ValuesStringsXML_list()
Dim f As Long, ff As String, fp As String, fn As String, tmp As String
Dim vfn As Variant, dFILEs As Object 'New scripting_dictionary
Set dFILEs = CreateObject("Scripting.Dictionary")
dFILEs.CompareMode = vbTextCompare
'set vars for c:\temp\Workspace\*\Values\Strings.xml
fp = Environ("TMP") & Chr(92) & "Workspace"
ff = "Values"
fn = "Strings.xml"
dFILEs.Item(fp) = 0
'get folder list
Do
f = dFILEs.Count
For Each vfn In dFILEs
If Not CBool(dFILEs.Item(vfn)) Then
tmp = Dir(vfn & Chr(92) & Chr(42), vbDirectory)
Do While CBool(Len(tmp))
If Not CBool(InStr(1, tmp, Chr(46))) Then
dFILEs.Item(vfn & Chr(92) & tmp) = 0
End If
tmp = Dir
Loop
'Debug.Print dFILEs.Count
dFILEs.Item(vfn) = 1
End If
Next vfn
Loop Until f = dFILEs.Count
'remove the folders and check for Values\Strings.xml
For Each vfn In dFILEs
If CBool(dFILEs.Item(vfn)) Then
If LCase(Split(vfn, Chr(92))(UBound(Split(vfn, Chr(92))))) = LCase(ff) And _
CBool(Len(Dir(vfn & Chr(92) & fn, vbReadOnly + vbHidden + vbSystem))) Then
dFILEs.Item(vfn & Chr(92) & fn) = 0
End If
dFILEs.Remove vfn
End If
Next vfn
'list the files
For Each vfn In dFILEs
Debug.Print "from dict: " & vfn
Next vfn
dFILEs.RemoveAll: Set dFILEs = Nothing
End Sub
If you wish to convert the late binding of the Scripting.Dictionary to early binding, you must add Microsoft Scripting Runtime to the VBE's Tools ► References.
I think you are saying that you want to look in the sub-folder "\values" for files called strings.xms
If that's right, try the below amended code:
Dim oFS As Office.FileSearch
Dim i As Integer
Set oFS = Application.FileSearch
With oFS
.NewSearch
.FileType = msoFileTypeAllFiles
.Filename = "strings.xml"
.LookIn = "D:\Workspace\values"
.SearchSubFolders = True
.Execute
MsgBox "Finish ! " & .FoundFiles.Count & " item found !"
End With
of course, you may not want to specify the sub-folder.
Here is another option:
Dim sPath As String
Dim sFil As String
Dim strName As String
sPath = "D:\Workspace\values" 'Change Path
sFil = Dir(sPath & "string.xml") 'All files in Directory matching name
Do While sFil <> ""
strName = sPath & sFil
sFil = Dir
'Your Code Here.
i=i+1
Loop
MsgBox "Finish ! " & .FoundFiles.Count & " item found !"
Have you considered using the FileSystemObject to do a recursive search in a sub-folder only?
MSDN - How to do a recursive search using the FileSystemObject
HTH
Philip
replace:
sPath = "D:\Workspace\values" 'Change Path
sFil = Dir(sPath & "string.xml") 'All files in Directory matching name
with:
sPath = "D:\Workspace\values\" 'Change Path
sFil = Dir(sPath & "*.xl*") 'All files in Directory matching name