MS ACCESS VBA Move/Copy Files - vba

I am using the below code to reference a table that has full path information to move (or copy) files from 1 location to another. However, it's not moving anything, but is completing per my Debug.Print message (Move Complete 2/22/2021 1:22:41 PM). Any thoughts on what I'm missing?
Additionally, I'd like to build the folder/subfolder structure where the file was located in the source...but don't know how to achieve that...and pointers on how to do this?
Sub copy_files_from_table()
Dim FSO As Object
Dim source As String
Dim destination As String
Dim SQL As String
Dim RS As DAO.Recordset
Dim db As DAO.Database
SQL = "select * from file_test"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set db = CurrentDb
Set RS = db.OpenRecordset(SQL)
source = RS!LocalFile
destination = "C:\Temp\Test"
While Not RS.EOF
If Dir(source, vbDirectory) <> "" Then
objFSO.CopyFolder source, destination:=destination '
Debug.Print "Move Folder Command Complete From: " & destination
Else
End If
RS.MoveNext
Wend
Debug.Print "Move Complete " & Now()
End Sub
Appreciate any help provided.

So far, I have gotten the following code to work on file paths <259; however, anything longer is causing the code to error. Since I'm pretty green on coding:) any suggestions how I can get around the long file path names?
Sub CopyFilesFromTable2()
On Error GoTo ErrorHandler
Dim source As String
Dim destination As String
Dim FSO As New FileSystemObject
Dim SQL As String
Dim RS As DAO.Recordset
Dim db As DAO.Database
'Test Table
SQL = "select * from file_test"
'Prod Table
'SQL = "select * from file"
Set FSO = CreateObject("Scripting.FileSystemObject")
Set db = CurrentDb
Set RS = db.OpenRecordset(SQL)
source = RS!LocalFile
File = VBA.FileSystem.Dir(source)
destination = "D:\Temp\Test\"
With RS
If Not .BOF And Not .EOF Then
.MoveLast
.MoveFirst
While (Not .EOF)
FSO.CopyFile RS!LocalFile, destination
.MoveNext
Wend
End If
.Close
End With
ExitSub:
Set RS = Nothing
'set to nothing
MsgBox "Done!"
Exit Sub
ErrorHandler:
MsgBox Err, vbCritical
Resume ExitSub
End Sub

Related

Automated Attachment Import Module -- [Error: "Run-time error '424': Object required"]

This is my first time actually posting in a forum so let me know if I am using any bad practice here.
I am using access to organize iMessage data to format into a human-readable book. The database contains timestamp and attachment filename fields. The actual attachment file names contain said timestamp and filename values with some filler between. My goal is to parse through several thousand texts and import all attachments where applicable. See example below.
Database Example: https://i.stack.imgur.com/6rcgi.png
File Example: https://i.stack.imgur.com/0xvjy.png
I have the module written out as seen below and am getting "Error: "Run-time error '424': Object required" referencing line 27. Very helpful, Microsoft.
Option Compare Database
Function ImportAttach()
Dim db As DAO.Database
Dim rs As DAO.Recordset2
Dim rsT As DAO.Recordset2
Dim rsA As DAO.Recordset2
Dim fldT As DAO.Field2
Dim fldA As DAO.Field2
Dim strTimeStamp As String
Dim strFileName As String
Dim strPath As String
Dim strFile As String
Set db = CurrentDb
Set rs = db.OpenRecordset("iMessageDB")
Set fldT = rs("TimeStamp")
Set fldA = rs("Attachment")
strFilePath = "C:\Users\XPS\Documents\Projects\iMessage Book\attachments\"
rs.MoveFirst
Do Until rs.EOF
Set rsT = fldT.Value '<-- Error Here
Set rsA = fldA.Value
If IsNull(strFileName) Then
rs.MoveNext
Else
strFile = Dir(strPath & strTimeStamp & "*" & strFileName)
rs.Edit
rsA.AddNew
rsA("FileData").LoadFromFile strPathFileName
rsA.Update
End If
strFile = Dir
rsA.Close
rs.Update
rs.MoveNext
Loop
rs.Close
db.Close
Set fldT = Nothing
Set fldA = Nothing
Set rsT = Nothing
Set rsA = Nothing
Set rs = Nothing
Set db = Nothing
End Function
I have not found a good solution elsewhere, so I appreciate any help.

Move Files to New Directory based on SQL Query Data

Hoping someone could help:)
I have an MS Access table that holds file names along with other data Example:
AcctNum, FileName
12345, abc123.pdf
I have written a SQL to sort the file name based on account number. Example: Select * from tblTest where AcctNum='12345'
Files with these file names are located in a directory (C:\Test\<filename>.pdf)
I need to move the selected files based on the SQL I've written to move those files to a different directory (D:\Test\FromCode). Filename 'abc123.pdf' is located in C:\Test move to D:\test, loop through SQL results to move files based on criteria.
Attached is some VBA that I have written that I can get a file to copy from source to destination manually when I input the exact filename (as seen in the code, the file name is "abc123"), but I want to tie it to the mysql query recordset and move the group which will contain 1000's of files. Anyone know the best way I can tie this to recordset?
Appreciate any direction/help you provide.
Sub UpdateDirectory()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim rs As DAO.Recordset
Dim db As DAO.Database
Dim mysource As String
Dim mydes As String
Dim mysql As String
mysql = "SELECT * from tblTest where ACCTNUM = 123456"
mysource = "C:\Test"
mydes = "D:\Test\FromCode\"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(mysource)
Set db = CurrentDb
Set rs = db.OpenRecordSet(mysql)
'I want to tie mysql recordset to below to move all files/records based
'on ACCTNUM. This could result in moving 1000's of files to
'destination.
For Each objFile In objFolder.Files
If InStr(1, objFile.Name, "abc123") > 0 Then
Debug.Print "yes"
objFSO.CopyFile objFile, mydes
Else
Debug.Print "no"
End If
Next
'clean
Set objFolder = Nothing
Set objFile = Nothing
Set objFSO = Nothing
End Sub
Here is your code with a working answer: To be sure, #June7 is correct, but putting the answer in your context:
Sub UpdateDirectory()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim rs As DAO.Recordset
Dim db As DAO.Database
Dim mysource As String
Dim mydes As String
Dim mysql As String
mysql = "SELECT * from tblTest where ACCTNUM = 123456"
mysource = "C:\Test" 'Missing backslash - added later
mydes = "D:\Test\FromCode\"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(mysource)
Set db = CurrentDb
Set rs = db.OpenRecordSet(mysql)
'I want to tie mysql recordset to below to move all files/records based
'on ACCTNUM. This could result in moving 1000's of files to
'destination.
While Not rs.EOF
sourceFile = mysource & "\" & rs!Filename 'Added a missing backslash
destFile = mydes & rs!Filename
If Dir(sourceFile) <> "" Then 'If sourceFile exists, then Dir(sourceFile) will return it's name, otherwise empty string
FileCopy sourceFile, destFile
End If
rs.MoveNext
Wend
End Sub
That SQL is not sorting records, it is filtering.
The recordset serves no purpose if you do not loop through records. If you do not want to simply copy all files and the complete account number is not part of filename, then need to loop through recordset, not folder. Use filename from field in the file copy. FSO is not needed - Access FileCopy method can serve.
...
mysource = "C:\Test\"
...
Do While Not rs.EOF
If Dir(mysource & rs!Filename) <> "" Then FileCopy mysource & rs!Filename, mydes & rs!Filename
Loop

How to Get UTC of file DateLastModified in VBA?

I need to find from an Access DB if any file in a folder got changed. For this reason I created a table containing the file information (name and DateLastModified). However there is the problem, that Windows always adjusts the DateLastModified to the local time zone and this value will even change on daylight savings (means: DateLastModified will change when DST activates/deactivates)!
To overcome this and to find the files true 'DateLastModified'-date I use FileSystemObject to get 'DateLastModified' and convert the returned value to UTC by means of Function GetUTC. Then I store this value in the database. I carefully tested GetUTC - it will return a value not depending on DST (tested for time zones CET and CEST).
Re-querying the folder and comparing a newly calculated 'DateLastModified' against the stored 'DateLastModified' will fail for approximately 15%-35% of the files - is seems random which files fail! Could it be that DT.GetVarDate(False) in GetUTC does not always return the same binary value?
However using debug.print always shows the same Date & Time for the failing files and the value stored in the database! MS specs says the resolution of datatype 'Date' is one second. So I do not understand how 2 Dates that show the same value will result to false when compared! Sample output of a failed file:
1477 493 18.12.2013 19:03:26 18.12.2013 19:03:26 scanColor0010.pdf
How can I make this work?
Option Compare Database
Option Explicit
Public ws As Workspace
Public db As Database
Function GetUTC(dLocalTimeDate As Date) As Date
Dim DT As Object
Dim curTime As Date
curTime = Now()
Set DT = CreateObject("WbemScripting.SWbemDateTime")
DT.SetVarDate curTime
GetUTC = dLocalTimeDate - curTime + DT.GetVarDate(False)
End Function
'------------------------------------------------------------
' Test_UTC_Click
'
'------------------------------------------------------------
Private Sub Test_UTC_Click()
Dim colFiles As New Collection
Dim vFile As Variant
Dim rst As Recordset
Dim fso As FileSystemObject
Dim f As File
Dim lngCountWrong As Long
Dim lngCount As Long
Set ws = DBEngine.Workspaces(0)
Set db = CurrentDb()
RecursiveDir colFiles, "Y:\", "*.pdf", False
Set fso = CreateObject("Scripting.FileSystemObject")
For Each vFile In colFiles
Set f = fso.GetFile(vFile)
Set rst = db.OpenRecordset("SELECT tblFiles.*, tblFiles.fileName FROM tblFiles WHERE (((tblFiles.fileName)=""" & f.Name & """));")
rst.MoveFirst
lngCount = lngCount + 1
If (rst!fileDateModified = GetUTC(f.DateLastModified)) Then
'Ok, this is always expected
Else
'Uuuups - what went wrong?
lngCountWrong = lngCountWrong + 1
Debug.Print lngCount, lngCountWrong, rst!fileDateModified, GetUTC(f.DateLastModified), f.Name
End If
rst.Close
Set f = Nothing
DoEvents
Next vFile
Debug.Print "finished", lngCount
Set fso = Nothing
End Sub
'------------------------------------------------------------
' CreateTestdata_Click
'
'------------------------------------------------------------
Private Sub CreateTestdata_Click()
Dim colFiles As New Collection
Dim vFile As Variant
Dim rst As Recordset
Dim fso As FileSystemObject
Dim f As File
Set ws = DBEngine.Workspaces(0)
Set db = CurrentDb()
db.Execute "DELETE tblFiles.* FROM tblFiles;"
Set rst = db.OpenRecordset("SELECT tblFiles.* FROM tblFiles;")
RecursiveDir colFiles, "Y:\", "*.pdf", False
Set fso = CreateObject("Scripting.FileSystemObject")
For Each vFile In colFiles
Set f = fso.GetFile(vFile)
rst.AddNew
rst!filename = f.Name
Debug.Print f.Name
rst!fileDateModified = GetUTC(f.DateLastModified)
rst.Update
Set f = Nothing
DoEvents
Next vFile
Set fso = Nothing
rst.Close
Debug.Print "Finished creating"
MsgBox "Finished creating"
End Sub
Rewrite
If (rst!fileDateModified = GetUTC(f.DateLastModified)) Then
'Ok, this is always expected
Else
to
If Datediff("s",rst!fileDateModified,GetUTC(f.DateLastModified)) = 0 Then
'Ok, this is always expected
Else
Further reading on Datediff
Further reading on How to store, calculate, and compare Date/Time data in Microsoft Access. Although this article is on Access it should be similar in Excel

MS Access, Linking backend tables using VB code over network

I have made a Tracking Database for work that has a front end interface and backend tables. Right now I have a section of code which upon start up will link the front end with the back end tables so people can copy and paste the front end from "G:\Sections\MEO\DDPO\Active Issues\Tracking Database" to their desktop and it will still be able to access the backend file.
The Issue I want to avoid is if someone changes the file path of where the back end is located the front end will not be able to find the backend.
example:
Original Path G:\Sections\MEO\DDPO\Active Issues\Tracking Database\Database Backend\tracking Database_be.accdb
If someone Modified the Path
G:\New Sections\MEO\DDPO\Active Issues\Tracking Database\Database Backend\tracking Database_be.accdb
This is the current code I use for the front end to ensure it links to the backend upon start up.
Private Function ReconnectTables() As Boolean
On Error Resume Next
Dim tdf As DAO.TableDef
Dim dbs As DAO.Database
Dim strPath As String
Dim strConnect As String
Set dbs = CurrentDb
strPath = dbs.Name
strPath = "G:\Sections\MEO\DDPO\Active Issues\Tracking Database\Database Backend\tracking Database_be.accdb"
strConnect = strPath
For Each tdf In dbs.TableDefs
If tdf.Connect <> "" Then
tdf.Connect = ";DATABASE=" & strConnect
tdf.RefreshLink
End If
Next
Set dbs = Nothing
If Err.Number = 0 Then ReconnectTables = True
End Function
I ended up removing the hard written path from the code and added a line where it checks a value in a table. That value stored in the table is the path which the user can select in a separate form which now gets called if it detects the tables are not linked.
My new code looks like this:
Private Function ReconnectTables() As Boolean
On Error Resume Next
Dim tdf As DAO.TableDef
Dim dbs As DAO.Database
Dim strPath As String
Dim strConnect As String
Set dbs = CurrentDb
strPath = dbs.Name
strPath = me.BackEndPath.Value
strConnect = strPath
For Each tdf In dbs.TableDefs
If tdf.Connect <> "" Then
tdf.Connect = ";DATABASE=" & strConnect
tdf.RefreshLink
End If
Next
Set dbs = Nothing
If Err.Number = 0 Then ReconnectTables = True
End Function
I used to just have a message box that would pop up if the tables were not linked but now I added 2 lines of code to open a form (frmBEpath) if the tables are not linked.
Private Sub Form_Load()
On Error Resume Next
'DoCmd.ShowToolbar "Ribbon", acToolbarNo
If ReconnectTables() = True Then
strVerClient = Nz(DLookup("[VersionNumber]", "[tblVersionClient]"), "")
strVerServer = Nz(DLookup("[VersionNumber]", "[tblVersionServer]"), "")
Me.Repaint
Else
'MsgBox "Couldn't Find Data Tables. Exiting.", vbCritical, "Error"
DoCmd.Close
DoCmd.OpenForm "frmBEpath"
End If
End Sub
This new form tells the user the tables are not linked and it gives them a button to browse for the backend files. When they select the backend file and click the yes button it stores the path name in a table.
Option Compare Database
Public Function FolderSelection() As String
Dim objFD As Object
Dim strOut As String
strOut = vbNullString
Set objFD = Application.FileDialog(3)
If objFD.Show = -1 Then
strOut = objFD.SelectedItems(1)
End If
Set objFD = Nothing
FolderSelection = strOut
End Function
Private Sub btnBrowse_Click()
Dim strChoice As String
strChoice = FolderSelection
If Len(strChoice) > 0 Then
Me.txtPath = strChoice
End If
End Sub
Private Sub btnConfirmYes_Click()
Me.BackEndPath.Value = Me.txtPath.Value
DoCmd.Close
DoCmd.OpenForm "frmsplash"
End Sub

How to read values from one of custom Queries in MS Access VBA?

I have a MS ACCESS query called Query11 that sums up amounts.
Let's say it's got SUM_WEEKLY and SUM_MONTHLY as a field in Query11
In VBA, how could I get each value?
I have tried to get them with the codes below and it did not work.
Dim dbMyDB As Database
Dim rsMyRS As Recordset
Set dbMyDB = CurrentDb
Set rsMyRS = dbMyDB.OpenRecordset("Query11")
MsgBox rsMyRS("SUM_WEEKLY")
MsgBox rsMyRS("SUM_MONTHLY")
Your code should work, and you don't tell the error, but try to be a bit more explicit:
Dim dbMyDB As DAO.Database
Dim rsMyRS As DAO.Recordset
Set dbMyDB = CurrentDb
Set rsMyRS = dbMyDB.OpenRecordset("Query11")
' Check that a record exists.
MsgBox CStr(rsMyRS.RecordCount)
MsgBox rsMyRS("SUM_WEEKLY").Value
MsgBox rsMyRS("SUM_MONTHLY").Value
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("Query11")
' Check a record exists
If rs.RecordCount > 0 Then
MsgBox "SUM_WEEKLY = " & rs!SUM_WEEKLY
MsgBox "SUM_MONTHLY = " & rs!SUM_MONTHLY
Else
MsgBox "Recordset has no records"
End If