Converting absolute directories to relative paths when referring to external files - ms-access

I'm designing a database that will incorporate many images so I've decided to link to external files by storing their paths and bounding an image control to that field. Here is the code that allows me to select the file and store it was a string:
Public Function ShowFileDialog() As String
Dim objFD As Object
Dim strOut As String
strOut = vbNullString
Set objFD = Application.FileDialog(msoFileDialogOpen)
If objFD.Show = -1 Then
strOut = objFD.SelectedItems(1)
End If
Set objFD = Nothing
ShowFileDialog = strOut
End Function
which is then called by a control button:
Private Sub Command128_Click()
Dim strChoice As String
strChoice = ShowFileDialog
If Len(strChoice) > 0 Then
Me.Path = strChoice
Else
'bleh
End If
End Sub
This stores the absolute directory of the selected file, however I recently realized that I need to store relative paths so that when the database and its associated directories are moved onto a new computer (which is very likely to happen) these links will be maintained.
UPDATE: The helpful tips provided by Hans Up enabled me to get this to work. Here is my revised and tidied up code.
Public Function GetPath()
Dim objFD As Object
Dim strOut As String
Dim strAbsolute As String
Dim strFolder As String
Dim strRelativePath As String
strOut = vbNullString
Set objFD = Application.FileDialog(msoFileDialogOpen)
If objFD.Show = -1 Then
strOut = objFD.SelectedItems(1)
End If
Set objFD = Nothing
strAbsolute = strOut
strFolder = CurrentProject.Path & "\"
strRelativePath = Mid(strAbsolute, Len(strFolder) + 1)
If Len(strRelativePath) > 0 Then
Me.Path = strRelativePath
Else
'bleh
End If
End Function
Private Sub Command128_Click()
GetPath
End Sub

Here is an Immediate window session which demonstrates techniques you can use to determine the relative path of your selected file ...
' folder where db file resides ...
? CurrentProject.Path
C:\share\Access
strFolder = CurrentProject.Path & Chr(92)
? strFolder
C:\share\Access\
' strChoice is the file path from your code sample;
' use Mid() to get the piece from that string which follows strFolder ...
strChoice = "C:\share\Access\image\foo.png"
strRelativePath = Mid(strChoice, Len(strFolder) + 1)
? strRelativePath
image\foo.png
' combine base folder and relative path again just to confirm we got the right pieces ...
? strFolder & strRelativePath
C:\share\Access\image\foo.png

Related

Error when looping through Image slideshow from pics in folder

I am trying to create a image slideshow by changing the Image control .picture property by looping through all images in a predefined folder
C:\Images
The code I am using:
Public pixpaths As Collection
Public pix_path As String
Public pixnum As Integer
Public fs As YtoFileSearch
Public k As Integer
Public Sub Image_set()
Set pixpaths = New Collection
pix_path = "C:\Images"
Set fs = New YtoFileSearch
With fs
.NewSearch
.LookIn = pix_path
.fileName = "*.jpg"
If fs.Execute() > 0 Then
For k = 1 To .FoundFiles.Count
pixpaths.Add Item:=.FoundFiles(k)
Next k
Else
MsgBox "No files found!"
DoCmd.OpenForm "Fr_Sketchpad" ' If no images found in folder the set image from another form 'Sketchpad' image control
Forms!Fr_Sketchpad.Visible = False
Forms!Fr_Main!imgPixHolder.Picture = "" 'Forms!Fr_Sketchpad!Img_Std.Picture Was getting another error here so commented this
pixnum = 0
Exit Sub
End If
End With
'load first pix
Forms!Fr_Main.imgPixHolder.Picture = pixpaths(1)
pixnum = 1
End Sub
Public Sub Image_loop()
If pixnum = pixpaths.Count Then
pixnum = 1
ElseIf pixnum = 0 Then
Exit Sub
Else
pixnum = pixnum + 1
Forms!Fr_Main!imgPixHolder.Picture = pixpaths(pixnum)
End If
End Sub
Private Sub Form_Open(Cancel As Integer)
Call Image_set
End Sub
Private Sub Form_Timer()
Call Image_loop
End Sub
The Image_Set(), Image_loop() and variables are in one module and are called in Form_open and Form_timer events
The code is working fine for one loop cycle but for the next loop cycle it is showing an error:
Error 91 object variable or with block variable not set
on
If pixnum = pixpaths.Count Then
In debug mode when I check value for pixnum it is 0
[Update]
Class Module YtoFileSearch
Option Compare Database
Option Explicit
' How this is not another proof that doing VBA is a bad idea?
' Nevertheless, we'll try to make the scripts relying on Application.FileSearch works again.
' The interface of this YtoFileSearch class aims to stick to the original
' Application.FileSearch class interface.
' Cf is https://msdn.microsoft.com/en-us/library/office/aa219847(v=office.11).aspx
' For now it do not handle recursive search and only search for files.
' More precisely the following filters are not implemented:
' * SearchSubFolders
' * MatchTextExactly
' * FileType
' If that's something you need, please create an issue so we have a look at it.
' Our class attributes.
Private pDirectoryPath As String
Private pFileNameFilter As String
Private pFoundFiles As Collection
' Set the directory in which we will search.
Public Property Let LookIn(directoryPath As String)
pDirectoryPath = directoryPath
End Property
' Allow to filter by file name.
Public Property Let fileName(fileName As String)
pFileNameFilter = fileName
End Property
'Property to get all the found files.
Public Property Get FoundFiles() As Collection
Set FoundFiles = pFoundFiles
End Property
' Reset the FileSearch object for a new search.
Public Sub NewSearch()
'Reset the found files object.
Set pFoundFiles = New Collection
' and the search criterions.
pDirectoryPath = ""
pFileNameFilter = ""
End Sub
' Launch the search and return the number of occurrences.
Public Function Execute() As Long
'Lance la recherche
doSearch
Execute = pFoundFiles.Count
End Function
' Do the nasty work here.
Private Sub doSearch()
Dim directoryPath As String
Dim currentFile As String
Dim filter As String
directoryPath = pDirectoryPath
If InStr(Len(pDirectoryPath), pDirectoryPath, "\") = 0 Then
directoryPath = directoryPath & "\"
End If
' If no directory is specified, abort the search.
If Len(directoryPath) = 0 Then
Exit Sub
End If
' Check that directoryPath is a valid directory path.
' http://stackoverflow.com/questions/15480389/excel-vba-check-if-directory-exists-error
If Dir(directoryPath, vbDirectory) = "" Then
Debug.Print "Directory " & directoryPath & " does not exists"
Exit Sub
Else
If (GetAttr(directoryPath) And vbDirectory) <> vbDirectory Then
Debug.Print directoryPath & " is not a directory"
Exit Sub
End If
End If
' We rely on the Dir() function for the search.
' cf https://msdn.microsoft.com/fr-fr/library/dk008ty4(v=vs.90).aspx
' Create the filter used with the Dir() function.
filter = directoryPath
If Len(pFileNameFilter) > 0 Then
' Add the file name filter.
filter = filter & "*" & pFileNameFilter & "*"
End If
' Start to search.
currentFile = Dir(filter)
Do While currentFile <> ""
' Use bitwise comparison to make sure currentFile is not a directory.
If (GetAttr(directoryPath & currentFile) And vbDirectory) <> vbDirectory Then
' Add the entry to the list of found files.
pFoundFiles.Add directoryPath & currentFile
End If
' Get next entry.
currentFile = Dir()
Loop
End Sub
Please advice how to resolve!
I have to answer your comment question you had for me here. This may not solve your issue, but it may help you find it, especially if the error is from you setting pixpaths = nothing in another function as #dbmitch suggested.
You would refer to .FoundFiles in Image_Set the same way you would pixpath, the collection gets populated by the doSearch sub from the .Execute function so the following code should work the same. Also, unless you are using your arguments in another module, you may want to consider making them Private like I did here.
Private pix_path As String
Private pixnum As Integer
Private fs As YtoFileSearch
Public Sub Image_set()
pix_path = "C:\Images"
Set fs = New YtoFileSearch
With fs
.NewSearch
.LookIn = pix_path
.fileName = "*.jpg"
If fs.Execute() > 0 Then
'load first pix
Forms!Fr_Main.imgPixHolder.Picture = .FoundFiles(1)
pixnum = 1
Else
MsgBox "No files found!"
DoCmd.OpenForm "Fr_Sketchpad" ' If no images found in folder the set image from another form 'Sketchpad' image control
Forms!Fr_Sketchpad.Visible = False
Forms!Fr_Main!imgPixHolder.Picture = ""
'Forms!Fr_Sketchpad!Img_Std.Picture Was getting another error here so commented this
pixnum = 0
End If
End With
End Sub
Public Sub Image_loop()
With fs
If pixnum = .FoundFiles.Count Then
pixnum = 1
ElseIf pixnum <> 0 Then
pixnum = pixnum + 1
Forms!Fr_Main!imgPixHolder.Picture = .FoundFiles(pixnum)
End If
End With
End Sub

Visual Basic - How to use a variable from one function in another

I have checked Google, and the suggested answers here, but have had no luck unfortunately.
The last thing I need to do is have an email read the rateNbr variable into the email body, but it just comes up empty.
I tried to make Public Function FuncRateCheckFile read as Public Function FuncRateCheckFile(ByVal rateNbr As String), to try and enable it to be called outside the function, but this then breaks the function when it is called elsewhere. :(
Here is the code, with comments as to where I am referring:
Public Function FuncRateCheckFile()
Dim blnContinue As Boolean
Dim strLine As String
Dim strSearchFor, strSearchWrd, LineCount, objFSO, objTextFile, arrLines
Dim dteNow As Date
Dim newDate As String
'//==============================================================================================
'// DECLARED
Dim rateNbr As String
'//==============================================================================================
FuncRateCheckFile = False
blnContinue = True
If blnContinue Then
Const ForReading = 1
'Get todays date and reformat it
dteNow = DateValue(Now)
newDate = Format(dteNow, "dd/MM/yy")
strSearchWrd = newDate
'Read the whole file
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile(m_RateCheckFile, ForReading)
LineCount = 0
Do Until objTextFile.AtEndOfStream
strLine = objTextFile.ReadLine()
If InStr(strLine, strSearchWrd) <> 0 Then
arrLines = Split(strLine, vbCrLf)
LineCount = LineCount + 1
End If
Loop
'Log a message to state how many lines have todays day, and if there are none, log an error
If LineCount <> 0 Then
'//==============================================================================================
'// "rateNbr" IS WHAT I AM TRYING TO GET TO PUT IN THE EMAIL
LogMessage "Rate file date is correct"
rateNbr = "Number of rates for " & newDate & " in the file recieved on " & newDate & " is " & LineCount
LogMessage rateNbr
EmailAdvice2
objTextFile.Close
'//==============================================================================================
Else
blnContinue = False
LogError "Failed to retrieve Current Rate date, please check rate file.."
EmailAdvice
objTextFile.Close
End If
End If
FuncRateCheckFile = blnContinue
LogMessage "Completed Check Rate file"
End Function
Private Function EmailAdvice2()
Dim strSMTPFrom As String
Dim strSMTPTo As String
Dim strSMTPRelay As String
Dim strTextBody As String
Dim strSubject As String
Dim oMessage As Object
'//==============================================================================================
'// DECLARED AGAIN
Dim rateNbr As String
'//==============================================================================================
Set oMessage = CreateObject("CDO.Message")
strSMTPFrom = "no-reply#work.com.au"
strSMTPTo = "me#work.com.au"
strSMTPRelay = "smtp.relay.com"
'//==============================================================================================
'// THIS MAKES THE TEXT BODY BLANK, BUT THE EMAIL STILL SENDS
strTextBody = rateNbr
'//==============================================================================================
strSubject = "Todays rates"
'strAttachment = "full UNC path of file"
oMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
oMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = strSMTPRelay
oMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
oMessage.Configuration.Fields.Update
oMessage.Subject = strSubject
oMessage.From = strSMTPFrom
oMessage.To = strSMTPTo
oMessage.textbody = strTextBody
'oMessage.AddAttachment strAttachment
oMessage.Send
End Function
I am positive that it is blank because I have declared rateNbr under EmailAdvice2() and then not given it anything to fill the variable with. But I don't know how to make it call the variable under FuncRateCheckFile().
Thanks to all for any assistance.
As Plutonix stated, this is a scope issue.
Move the declaration of your 'rateNbr' variable out to class level, and remove the local declarations inside your functions:
Dim rateNbr As String ' <-- out at class level it will be accessible from both functions
Public Function FuncRateCheckFile()
...
' REMOVE both the decalarations of "rateNbr" that are INSIDE your functions
...
End Function
Private Function EmailAdvice2()
...
' REMOVE both the decalarations of "rateNbr" that are INSIDE your functions
...
End Function

How to stop recursive search once the file is found in VBA

i have the below script , modify from this http://www.vbforums.com/showthread.php?613400-Loop-through-folders-subfolders
Private Sub Command1_Click()
Dim fld As Folder
Dim searchString As String
Dim ResultFilePath As String
Set fso = New FileSystemObject
Set fld = fso.GetFolder("C:\Users\janedoe\Desktop\jane")
searchString = "ClaimSheet.xlsx"
ResultFilePath = RecursiveSearch(fld, searchString)
Set fld = Nothing
Set fso = Nothing
If ResultFilePath = "" Then
MsgBox ("We could not find the file " & searchString)
Else
MsgBox ("We found it, its at " & ResultFilePath)
End If
End Sub
Function RecursiveSearch(fld As Folder, search As String) As String
Dim tfold As Folder
Dim tfil As File
For Each tfold In fld.SubFolders
Debug.Print "looking in the " & tfold & " folder"
RecursiveSearch tfold, search
If RecursiveSearch = search Then
Exit Function
End If
Next
Debug.Assert InStr(tfil, search) = 0
If InStr(tfil.Name, search) Then
RecursiveSearch = tfil.Path
Exit function
End If
Next
End Function
What I would like the RecursiveSearch function to do is, search the folder for the searchString file, once found, stop searching and return the file path.
The problem is, I can't exit function without losing the value at the line
RecursiveSearch = tfil.Path
I think it does that because the function might have gone out of scope when returning the upper level .
Any help would be appreciated,
Your recursion line should be:
RecursiveSearch = RecursiveSearch(tfold, search)
This will allow each level of the recursion to pass it's result back up the chain.
Also, the following line seems to be missing from the code in your question:
For Each tfil In fld.Files

Is it possible to batch convert csv to xls using a macro?

I have a large amount of csv files that I need in .xls format. Is it possible to run a batch conversion with a macro or best done with another language?
I have used this code http://www.ozgrid.com/forum/showthread.php?t=71409&p=369573#post369573 to reference my directory but I'm not sure of the command to open each file and save them. Here's what I have:
Sub batchconvertcsvxls()
Dim wb As Workbook
Dim CSVCount As Integer
Dim myVar As String
myVar = FileList("C:\Documents and Settings\alistairw\My Documents\csvxlstest")
For i = LBound(myVar) To UBound(myVar)
With wb
Application.Workbooks.OpenText 'How do I reference the myvar string ?
wb.SaveAs '....
End With
Next
End Sub
Function FileList(fldr As String, Optional fltr As String = "*.*") As Variant
Dim sTemp As String, sHldr As String
If Right$(fldr, 1) <> "\" Then fldr = fldr & "\"
sTemp = Dir(fldr & fltr)
If sTemp = "" Then
FileList = Split("No files found", "|") 'ensures an array is returned
Exit Function
End If
Do
sHldr = Dir
If sHldr = "" Then Exit Do
sTemp = sTemp & "|" & sHldr
Loop
FileList = Split(sTemp, "|")
End Function
Edit: The files are .txt files formatted as csv
By combining the code given by Scott Holtzman and 'ExcelFreak', the conversion works quite well. The final code looks something like this:
Sub CSV_to_XLS()
Dim wb As Workbook
Dim strFile As String, strDir As String
strDir = "U:\path\"
strFile = Dir(strDir & "*.csv")
Do While strFile <> ""
Set wb = Workbooks.Open(Filename:=strDir & strFile, Local:=True)
wb.SaveAs Replace(wb.FullName, ".csv", ".xls"), 50 'UPDATE:
wb.Close True
Set wb = Nothing
strFile = Dir
Loop
End Sub
Opening the converted .xls file throws a warning everytime:
"The file you are trying to open, 'filename', is in a different format than specified by the file extension. Verify that the file is not corrupted and is from a trusted source before opening the file. Do you want to open the file now?"
Clicking Yes then opens the .xls file.
Is there a way to get rid of this warning message? Excel throws a warning everytime the .xls file is opened.
In a lot less lines of code, this should get you what you want. However, I will say this may not be the fastest way to get it done, because you are opening, saving, and closing the workbook every time. I will look for a faster way, but I forget the method off the top of my head.
Sub batchconvertcsvxls()
Dim wb As Workbook
Dim strFile As String, strDir As String
strDir = "C:\"
strFile = Dir(strDir & "*.csv")
Do While strFile <> ""
Set wb = Workbooks.Open(strDir & strFile)
With wb
.SaveAs Replace(wb.FullName, ".csv", ".xls"), 50 'UPDATE:
.Close True
End With
Set wb = Nothing
Loop
End Sub
** UPDATE **
you need the proper fileformat enumeration for a .xls file. I think its 50, but you can check here Excel File Type Enumeration, if it's not.
The Code of Scott Holtzman nearly did it for me. I had to make two changes to get it to work:
He forgot to add the line that makes our loop continue with the next file. The last line before the Loop should read
strFile = Dir
The Workbooks.Open method did not read my CSV files as expected (the whole line ended up to be text in the first cell). When I added the parameter Local:=True it worked:
Set wb = Workbooks.Open(Filename:=strDir & strFile, Local:=True)
This works properly at least on Excel 2013. Using FileFormat:=xlExcel8 parameter instead of the filetype tag 50 creates files that open without security nags.
Sub CSV_to_XLS()
Dim wb As Workbook
Dim strFile As String, strDir As String
strDir = "C:\temp\"
strFile = Dir(strDir & "*.csv")
Do While strFile <> ""
Set wb = Workbooks.Open(Filename:=strDir & strFile, Local:=True)
wb.SaveAs Replace(wb.FullName, ".csv", ".xls"), FileFormat:=xlExcel8
wb.Close True
Set wb = Nothing
strFile = Dir
Loop
End Sub
This was a good question and I have found in the internet several answers. Just making very small changes (I couldn't edit any of the codes already published) I could make things work a bit better:
Sub CSV_to_XLSX()
Dim wb As Workbook
Dim strFile As String, strDir As String
strDir = "C:\Users\acer\OneDrive\Doctorado\Study 1\data\Retest Bkp\Day 1\Sart\"
strFile = Dir(strDir & "*.csv")
Do While strFile <> ""
Set wb = Workbooks.Open(Filename:=strDir & strFile, Local:=True)
With wb
.SaveAs Replace(wb.FullName, ".csv", ".xlsx"), 51
.Close True
End With
Set wb = Nothing
strFile = Dir
Loop
End Sub

Loop through for all shortcuts in a given location and return the target path

Is it possible to loop through for all shortcuts (.lnk) in a given location and return the .TargetPath. If a shortcuts target matches a criteria an action can then be peformed on the shortcut?
To delete all shortcuts I would use the following:
Public Sub deleteAllShortcuts()
Dim shortCutPath As String
' compName = Computer Name, recordDirShort = directory where the shortcut lnks are
shortCutPath = compName & recordDirShort
shortCutPath = shortCutPath & "*.lnk"
On Error Resume Next
Kill shortCutPath
On Error GoTo 0
End Sub
I cant figure out how I would loop through all shortcuts in the directory using the above loop.
Any help on the above would be greatly appreciated
Cheers
Noel
Hopefully this may be good to someone.
To delete shortcuts by the shorcut target I used the following:
Public Sub deleteShortcutByTarget(targetFolderName As String)
Dim strDocPath As String
Dim strTarget As String
Dim obj As Object
Dim shortcut As Object
Dim objFso As Object
Dim objFolder As Object
Dim objFile As Object
Set obj = CreateObject("WScript.Shell")
Set objFso = CreateObject("Scripting.FileSystemObject")
strDocPath = compName & recordDirShort
Set objFolder = objFso.GetFolder(strDocPath)
Set objFile = objFolder.Files
For Each objFile In objFolder.Files
If objFso.GetExtensionName(objFile.Path) = "lnk" Then
Set shortcut = obj.CreateShortcut(objFile.Path)
strTarget = shortcut.TargetPath
shortcut.Save
If strTarget = strDocPath & targetFolderName Then
Kill objFile.Path
End If
End If
Next
Set obj = Nothing
Set objFile = Nothing
Set objFso = Nothing
Set objFolder = Nothing
Set shortcut = Nothing
End Sub
Within Access you could use the Dir() function. It would be something like this:
Dim strLink As String
strLink = Dir(shortCutPath & "*.lnk")
Do Until Len(strLink)=0
Kill strLink
strLink = Dir()
Loop
Dir() doesn't play well with network paths in all cases, though, so you might want to use the File System Object, instead. It's much more versatile and works better with networks. I use it only occasionally, so don't have the code at my fingertips, but have a look at it -- you might have no trouble figuring it out as the object model is pretty clearly designed.