Calling the Value of a Function from within a VBA Sub - vba

Asking users to identify the folder where they want certain files to be saved, then save new workbooks to that folder. Sub calls the Function that sets this folder path to JobFolder. When calling JobFolder folder path from sub, the sub runs the Function all over again. Just need the value of JobFolder. Thank you.
Sub ExportJobFiles()
'Ask user to set job folder
Dim JobFolder As String
JobFolder = GetFolder()
'Ask user to set suffix for file names
Dim FileNameSuffix As Variant
Dim Default As String
Default = Worksheets("Summary").Range("E2").Value
FileNameSuffix = InputBox("Input suffix for job files", , Default)
'Creates job files in job folder
Dim SummaryFileName As String
Dim AFileName As String
Dim BFileName As String
Dim CFileName As String
SummaryFileName = JobFolder & "/Summary_" & FileNameSuffix & ".xls"
AFileName = JobFolder & "/A_" & FileNameSuffix & ".xls"
BFileName = JobFolder & "/B_" & FileNameSuffix & ".xls"
CFileName = JobFolder & "/C_" & FileNameSuffix & ".xls"
Workbooks.Add.SaveAs FileName:=SummaryFileName
Workbooks.Add.SaveAs FileName:=AFileName
Workbooks.Add.SaveAs FileName:=BFileName
Workbooks.Add.SaveAs FileName:=CFileName
End Sub
Function GetFolder() As String
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "Select a Folder"
.AllowMultiSelect = False
.InitialFileName = ""
.Show
GetFolder = .SelectedItems(1)
End With
End Function

Call JobFolder
That's calling the function (with an obsolete explicit call syntax) and discarding its return value.
Basically, that line does nothing useful.
Instead, declare a local variable:
Dim folder As String
And assign it with the function's return value:
folder = JobFolder
As noted in comments, you should also avoid GoTo jumps in or out of With blocks.
If the With block was written like this:
With Application.FileDialog(msoFileDialogFolderPicker)
'...
End With
Then jumping out of it would leave the object reference held by the With block in limbo. The only reason that's not happening with your code is because you already have a reference to the With block variable outside the With block, and you're destroying it manually... which isn't necessary if you let the With block handle the reference.

Related

vba loop through files in folder and copy names if multiple conditions are met/not met

I would like to loop through a folder and copy all the names of the excelfiles which does not contain "string1" in A6, "string2" in B6, "string3" in C6, "string4" in D6. Note all the conditions should be true (a AND statement).
The cells which should be tested are located in sheet 3, which is called "ProjectOperation".
The following code copy pase the filenames of all excel in a specific folder, however I have a hard time implementing the conditions. Please help.
Option Explicit
Sub SubDirList() 'Excel VBA process to loop through directories listing files
Dim sname As Variant
Dim sfil(1 To 1) As String
sfil(1) = "C:\Users\test" 'Change this path to suit.
For Each sname In sfil()
SelectFiles sname
Next sname
End Sub
Private Sub SelectFiles(sPath) 'Excel VBA to show file path name.
Dim Folder As Object
Dim file As Object
Dim fldr
Dim oFSO As Object
Dim i As Integer
'For Each file In Folder
' If checknameExistens(Folder.Files) Then
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set Folder = oFSO.GetFolder(sPath)
i = 1
For Each fldr In Folder.SubFolders
SelectFiles fldr.Path
Next fldr
For Each file In Folder.Files
'If checknameExistens(Folder.Files) Then
Range("A6536").End(xlUp)(2).Value = file
i = i + 1
Next file
Set oFSO = Nothing
End Sub
The original code is from the following link: http://www.thesmallman.com/list-files-in-subdirectory/
First of all I changed the code which retrieves the files because it collects all file regardless if it is a excel file or not. I also changed it to a function which gives all the files back in a collection
Function SelectFiles(ByVal sPath As String, ByVal pattern As String) As Collection
Dim Folder As Object
Dim file As Object
Dim fldr
Dim oFSO As Object
Dim coll As New Collection
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set Folder = oFSO.GetFolder(sPath)
For Each fldr In Folder.SubFolders
SelectFiles fldr.path, pattern
Next fldr
For Each file In Folder.Files
If file.Name Like pattern Then
coll.Add file
End If
Next file
Set SelectFiles = coll
End Function
Then I used the following function to retrieve the contents of the files which you can find here resp. here
Private Function GetValue(path, file, sheet, ref)
' Retrieves a value from a closed workbook
Dim arg As String
' Make sure the file exists
If Right(path, 1) <> "\" Then path = path & "\"
If Dir(path & file) = "" Then
GetValue = "File Not Found"
Exit Function
End If
' Create the argument
arg = "'" & path & "[" & file & "]" & sheet & "'!" & _
Range(ref).Range("A1").Address(, , xlR1C1)
' Execute an XLM macro
GetValue = ExecuteExcel4Macro(arg)
If IsError(GetValue) Then GetValue = ""
End Function
And this is the final result
Sub TestList()
Const SH_NAME = "ProjectOperation"
Dim sname As Variant
Dim coll As Collection
Dim s1 As String
Dim s2 As String
Dim s3 As String
Dim s4 As String
Dim i As Long
sname = "...." 'Change this path to suit.
Set coll = SelectFiles(sname, "*.xls*")
For i = 1 To coll.Count
s1 = GetValue(coll.Item(i).parentfolder, coll.Item(i).Name, SH_NAME, "A6")
s2 = GetValue(coll.Item(i).parentfolder, coll.Item(i).Name, SH_NAME, "B6")
s3 = GetValue(coll.Item(i).parentfolder, coll.Item(i).Name, SH_NAME, "C6")
s4 = GetValue(coll.Item(i).parentfolder, coll.Item(i).Name, SH_NAME, "D6")
If s1 = "string1" And s2 = "string2" And s3 = "string3" And s4 = "string4" Then
Debug.Print coll.Item(i).path
End If
Next
End Sub
I worked with your existing code and have just added an If statement inside your loop (as well as a couple of declarations of new variables). Because you are now working with two files you need to properly reference the workbook and sheet whenever you refer to a range.
'...
Dim wb As Workbook, ws As Worksheet
Application.ScreenUpdating = False
For Each file In Folder.Files
Set wb = Workbooks.Open(file)
Set ws = wb.Sheets("ProjectOperation")
If ws.Range("A6").Value = "string1" And ws.Range("B6").Value = "string2" And _
ws.Range("c6").Value = "string3" And ws.Range("D6").Value = "string4" Then
ThisWorkbook.Sheets(1).Range("A" & Rows.Count).End(xlUp)(2).Value = file 'workbook/sheet references may need changing
i = i + 1
End If
wb.Close False
Next file
Application.ScreenUpdating = True
'...

VBA Word - Nested IF Function, A Macro Running Other Macros in all Documents in a Folder

I'm a beginner at coding. So I would like to know how I could use nesting to code a macro (for VBA Word) that runs multiple other macros in all documents in a specified folder. I am trying to employ nesting by having the outer loop open all the documents in a folder (a user will input the location of the folder using InputBox), and within this loop, all the macros will be applied.
So far I know that this is what works perfectly (the code opens all documents in the specified folder);
Sub nestingMacro()
Dim currentFile As String
Dim location As String
location = InputBox("Location of folder")
If Right(location, 1) <> "\" Then location = location + "\"
currentFile = Dir(location & "*.doc*")
Do While (currentFile <> "")
Documents.Open FileName:=location & currentFile
currentFile = Dir()
Loop
End Sub
I tried adding the following;
Sub nestingMacro()
Dim currentFile As String
Dim location As String
location = InputBox("Location of folder")
If Right(location, 1) <> "\" Then location = location + "\"
currentFile = Dir(location & "*.doc*")
Do While (currentFile <> "")
Documents.Open FileName:=location & currentFile
currentFile = Dir()
If currentFile <> "" Then
'the name of the macros below
Call findReplaceStyle
Call countErrorsQuality
Call saveClose
End If
Loop
End Sub
Yes, it opens all documents in a folder, however, it runs the macros only on two of the documents then nothing happens to the others. How can I solve this?
Is there a better way to write the function IF, in order to run the macros on all documents using nesting?
Also is there a way to run the macros without actually calling their names?
Thank you!
You don't need if statement.
Sub nestingMacro()
Dim currentFile As String
Dim location As String
location = InputBox("Location of folder")
If Right(location, 1) <> "\" Then location = location + "\"
currentFile = Dir(location & "*.doc*")
Do While (currentFile <> "")
Documents.Open FileName:=location & currentFile
Call findReplaceStyle
Call countErrorsQuality
Call saveClose
currentFile = Dir()
Loop
End Sub
You don't need nesting for what you've described so far. If the code is only running on a few files in the folder, that's most likely because you're running the code from a document stored in the same folder and, as soon as it processes itself, it gets closed and that kills the macro. Try something along the lines of the following.
Sub Demo()
Application.ScreenUpdating = False
Dim strFolder As String, strFile As String, strDocNm As String, wdDoc as Document
strDocNm = ActiveDocument.Fullname
strFolder = GetFolder
If strFolder = "" Then Exit Sub
strFile = Dir(strFolder & "\*.doc*", vbNormal)
While strFile <> ""
If strFolder & "\" & strFile <> strDocNm Then
Set wdDoc = Documents.Open(FileName:=strFolder & "\" & strFile, AddToRecentFiles:=False, Visible:=False)
Call findReplaceStyle
Call countErrorsQuality
Call saveClose
End If
strFile = Dir()
Wend
Set wdDoc = Nothing
Application.ScreenUpdating = True
End Sub
Function GetFolder() As String
Dim oFolder As Object
GetFolder = ""
Set oFolder = CreateObject("Shell.Application").BrowseForFolder(0, "Choose a folder", 0)
If (Not oFolder Is Nothing) Then GetFolder = oFolder.Items.Item.Path
Set oFolder = Nothing
End Function
A potential problem with your
Call findReplaceStyle
Call countErrorsQuality
lines is that you're not passing the document you've just opened as a parameter. If anything in those subs changes the activedocument, you could run into problems. It's good coding practice to pass the document you want to process as a parameter, as in:
Call findReplaceStyle(wdDoc)
Call countErrorsQuality(wdDoc)
Drawing on your previous thread on a related topic, to accept and use such a parameter, the latter sub might be coded along the lines of:
Sub countErrorsQuality(wdDoc As Document)
Dim Rng As Range
With wdDoc
Set Rng = .Range(0, 0)
If .SpellingErrors.Count > 0 Then
With Rng
.Text "REJECTED" & vbCr
.Font.Size = 14
.Font.ColorIndex = wdRed
.Font.Bold = True
End With
End If
End With
Set Rng = Nothing
End Sub
Note that nothing gets selected. This reduces screen flicker and makes the code run faster.
I likewise suspect you don't need your
Call saveClose
code and all you really need is:
wdDoc.Close SaveChanges:=True

Using VBA to unzip file without prompting me once (choose "Yes to All" for any dialog box)

There is an unzipping code I'd like to adjust 4 my needs.
Sub Unzip()
Dim FSO As Object
Dim oApp As Object
Dim Fname As Variant
Dim FileNameFolder As Variant
Dim DefinePath As String
' Fname = Application.GetOpenFilename(filefilter:="Zip Files (*.zip), *.zip", MultiSelect:=False)
Fname = "C:\Users\michal\SkyDrive\csv\bossa\mstcgl.zip"
If Fname = False Then
'Do nothing
Else
'Destination folder
DefinePath = "C:\Users\michal\SkyDrive\csv\bossa\mstcgl_mst\" ' Change to your path / variable
If Right(DefinePath, 1) <> "\" Then
DefinePath = DefinePath & "\"
End If
FileNameFolder = DefinePath
' Delete all the files in the folder DefPath first if you want.
' On Error Resume Next
' Kill DefPath & "*.*"
' On Error GoTo 0
'Extract the files into the Destination folder
Set oApp = CreateObject("Shell.Application")
oApp.Namespace(FileNameFolder).CopyHere oApp.Namespace(Fname).items
' MsgBox "You find the files here: " & FileNameFolder
On Error Resume Next
Set FSO = CreateObject("scripting.filesystemobject")
FSO.deletefolder Environ("Temp") & "\Temporary Directory*", True
End If
End Sub
Somewhere here:
`Set oApp = CreateObject("Shell.Application")
oApp.Namespace(FileNameFolder).CopyHere`
a dialog box appears asking me if I want to overwrite the file that have the same names - and Yes I do want to overwrite them, but without answering the dialog box - I would like to hardcode it into the code, please.
I've found this page https://msdn.microsoft.com/en-us/library/windows/desktop/bb787866(v=vs.85).aspx but I just don't know how to add this parameter #16 which is "Respond with "Yes to All" for any dialog box that is displayed."
Can U help me with that?
And the last thing:
can You explain oApp.Namespace(Fname).items line for me.
I've really tried to guess it myself, but I thing I'm to short 4 this.
the code that results in no questions or no prompting of any kind is as follows:
Option Explicit
Sub Bossa_Unzip()
Dim FSO As Object
Dim oApp As Object ' oApp is the object which has the methods you're using in your code to unzip the zip file:
'you need to create that object before you can use it.
Dim Fname As Variant
Dim FileNameFolder As Variant ' previously Dim FileNameFolder As Variant
Dim DefinePath As String
' Fname = Application.GetOpenFilename(filefilter:="Zip Files (*.zip), *.zip", MultiSelect:=False)
Fname = "C:\Users\michal\SkyDrive\csv\bossa\mstcgl.zip"
If Fname = False Then
'Do nothing
Else
'Destination folder
DefinePath = "C:\Users\michal\SkyDrive\csv\bossa\mstcgl_mst\" ' Change to your path / variable
If Right(DefinePath, 1) <> "\" Then
DefinePath = DefinePath & "\"
End If
FileNameFolder = DefinePath
' Delete all the files in the folder DefPath first if you want.
' On Error Resume Next
' Kill DefPath & "*.*"
' On Error GoTo 0
'Extract the files into the Destination folder
Set oApp = CreateObject("Shell.Application") ' you need to create oApp object before you can use it.
oApp.Namespace(FileNameFolder).CopyHere oApp.Namespace(Fname).items, 16
'MsgBox "You'll find the files here: " & DefinePath
On Error Resume Next
Set FSO = CreateObject("scripting.filesystemobject")
FSO.deletefolder Environ("Temp") & "\Temporary Directory*", True
End If
End Sub
Of course this site helped me a lot - its CpyHere explanation site.
One thing I don't understand is why Fname and FileNumberFolder need to be declared as variant. In my opinion, they should be declared as String. Just look at this screenshot.
But when I declare them that way, the code gives me error.
Just look here, when the variables already have their values (first picture). The FileNameVariable and DefinePath variable have the exact same value, and it looks like a string 4 me. How is that necessary, that I need to declare another variable - FileNameVariable in that case (in 17th line) with the same value, but variant type.
please explain that to me, someone.

VBA Open file with wildcard knowing only extension

I am trying to get Excel to open any file in the a given folder
(ThisWorkbook.Path\Peach\Apple) that has .xlsm extension (there is always only 1 file). Is it possible to open it with wildcard character? I do not know the name of the file, just the extension.
If not, is there a way to do it?
Just ask the file system for the first matching file:
Dim path As String: path = ThisWorkbook.path & "\Peach\Apple\"
FindFirstFile = Dir$(path & "*.xlsm")
If (FindFirstFile <> "") Then
Workbooks.Open path & FindFirstFile
Else
'// not found
End If
(This will not search sub-directories)
You mentioned that it would be nice addition to open last modified file or file with shortest name, so let's start - there's a code example how you can grab all three files (first finded, last modified, with shortest name). You can modify this as you wish (add some parameters, add error handling, return only specified, etc).
Sub Test()
'declarations
Dim fso As Object
Dim folder As Object
Dim file As Object
Dim path As String
Dim first_finded As Object
Dim recently_modified As Object
Dim shortest_name As Object
Dim recently As Date
Dim shortest As Long
Dim firstFinded As Boolean
'setting default recently date(24 hours from now) and path
recently = DateAdd("h", -24, Now)
path = ThisWorkbook.path & "\Peach\Apple\"
Set fso = CreateObject("Scripting.FileSystemObject")
Set folder = fso.GetFolder(path)
'iteration over folder
For Each file In folder.Files
If file.Name Like "*.xlsm" Then
'grab first finded .xlsm
If Not firstFinded Then
firstFinded = Not firstFinded
Set first_finded = file
End If
'grab lastmodified .xlsm
If file.DateLastModified > recently Then
recently = file.DateLastModified
Set recently_modified = file
End If
'grab short named .xlsm
If shortest = 0 Or shortest > Len(file.Name) Then
shortest = Len(file.Name)
Set shortest_name = file
End If
End If
Next
'debug-print names
Debug.Print first_finded.Name
Debug.Print recently_modified.Name
Debug.Print shortest_name.Name
'so now you can uncomment this and open what you want
'Call Workbooks.Open(path & recently_modified.Name)
End Sub
Try the code below, it will open your "*.xlsm" file, in the path you've requested.
Sub OpenXLSMWildcardfile()
Dim Path As String
Path = ThisWorkbook.Path & "\Peach\Apple\"
Workbooks.Open (Path & "*.xlsm")
End Sub
PFB for the code required for opening the macro file with extension(.xlsm).
Sub OpeningFile()
'Declaring variables
Dim FileName, FolderPath As String
'Initializing folder path
FolderPath = ThisWorkbook.Path & "\Peach\Apple\"
'Finding the file name using wildcard
FileName = Dir(FolderPath & "*.xlsm")
'Looping through the workbook which are saved as macro enabled workbooks
While FileName <> ""
Workbooks.Open FolderPath & FileName
FileName = Dir()
Wend
End Sub

Browsing Main excel file and Save As directory path with use of Excel VBA

I have below Excel procedure I gather up and I am using it for couple of different calculations under different workbooks. So I was thinking instead changing the procedure for main and outcome files each time, I should be able to pick the file I want to carry out calculations in and the file path for outcomes files.
But I could not find anything for saving directory, I appreciate if you could help
Sub AsBuiltForm()
Dim SaveName As String
Dim mainBook As Workbook
a = InputBox("ENTER FIRST NUMBER ")
b = InputBox("ENTER LAST NUMBER ")
Workbooks.Open Filename:="C:\" 'main file can be browsed?
Set mainBook = Excel.Workbooks("CP.xlsx")
For i = a - 1 To b - 1
mainBook.Sheets(1).Range("bi1") = i + 1
SaveName = Sheets(1).Range("bi1").value & ".xlsx"
mainBook.SaveCopyAs "C:\" & SaveName 'save directory?
Workbooks.Open Filename:="C:\" & SaveName 'save directory?
With Excel.ActiveWorkbook
.Sheets("1 of 2").Range("A1:CT103").value = Sheets("1 of 2").Range("A1:CT103").value
.Sheets("2 of 2").Range("A1:CT103").value = Sheets("2 of 2").Range("A1:CT103").value
Excel.Application.DisplayAlerts = False
.Sheets("Sheet1").Delete
.Sheets("il oufall").Delete
.Sheets("1 of 2").Select
Columns("Bh:BZ").Select
Selection.Delete Shift:=xlToLeft
.Sheets("2 of 2").Select
Columns("Bn:BZ").Select
Selection.Delete Shift:=xlToLeft
.Close True
End With
Next
mainBook.Close False
Set mainBook = Nothing
End Sub
You can use Application.GetOpenFileName to pick files that you want to open at Run-Time.
You can use the function below to browse for a folder where you wish to save a file.
Sub FindFolder()
Dim myFolder as String
myFolder = BrowseFolder("Pick a Folder Where to Save")
End Sub
Function BrowseFolder(Optional Caption As String, Optional InitialFolder As String) As String
' based on Browse For Folder from:
' http://www.cpearson.com/excel/BrowseFolder.aspx
' this functions requires that the following Reference is active:
'Microsoft Shell Controls and Automation
Const BIF_RETURNONLYFSDIRS As Long = &H1
Dim wsh As Object
Dim SH As Shell32.Shell
Dim F As Shell32.Folder
Set wsh = CreateObject("Wscript.Shell")
Set SH = New Shell32.Shell
Set F = SH.BrowseForFolder(0&, Caption, BIF_RETURNONLYFSDIRS, InitialFolder)
If Not F Is Nothing Then
If F = "Desktop" Then
BrowseFolder = wsh.Specialfolders(F)
Else
BrowseFolder = F.Items.Item.path
End If
End If
End Function
The following is not really an answer to your question, but a few tips to improve your code, and too long to add as a comment.
Workbooks.Open returns a Workbook object you can save the reference, so you don't have to rely on ActiveWorkbook:
Dim oWorkbook As Workbook
Set oWorkbook = Workbooks.Open(Filename:="C:\" & SaveName)
'***** Do something with oWorkbook
Debug.Print oWorkbook.FullName
Set oWorkbook = Nothing
A few other hints:
Use Option Explicit at the top of every module to force explicit declaration of all variables in order to find typos and other errors earlier.
Avoid selecting cells
Yes, browsing file works now; all the ins and outs aside, the problem i face with naming the file due to the variable "bi1" and saving as many loop as i asked for. I check several times before i bother you but i do not think i have the sufficient info to address "fn" as file in the use of Application.GetOpenFileName .
Option Explicit
Sub AsBuiltForm()
Dim fn
Dim myFolder As String
Dim SaveName As String, a As Integer, b As Integer, i As Integer
myFolder = BrowseFolder("Pick a Folder Where to Save")
MsgBox "Choose Calculation File "
fn = Application.GetOpenFilename
Workbooks.Open fn
a = InputBox("ENTER FIRST NUMBER ")
b = InputBox("ENTER LAST NUMBER ")
For i = a - 1 To b - 1 Step 1
Application.DisplayAlerts = False
Workbooks.Open Filename:=fn
Range("bi1") = i + 1
SaveName = ActiveWorkbook.Sheets(1).Range("bi1").value
Sheets(1).Range("A1:CT103").value = Sheets(1).Range("A1:CT103").value
Sheets(2).Range("A1:CT103").value = Sheets(2).Range("A1:CT103").value
Application.ActiveWorkbook.SaveAs myFolder & SaveName
ActiveWorkbook.Close True
Next
End Sub
Function BrowseFolder(Optional Caption As String, Optional InitialFolder As String) As String
' based on Browse For Folder from:
' http://www.cpearson.com/excel/BrowseFolder.aspx
' this functions requires that the following Reference is active:
'Microsoft Shell Controls and Automation
Const BIF_RETURNONLYFSDIRS As Long = &H1
Dim wsh As Object
Dim SH As Shell32.Shell
Dim F As Shell32.Folder
Set wsh = CreateObject("Wscript.Shell")
Set SH = New Shell32.Shell
Set F = SH.BrowseForFolder(0&, Caption, BIF_RETURNONLYFSDIRS, InitialFolder)
If Not F Is Nothing Then
If F = "Desktop" Then
BrowseFolder = wsh.Specialfolders(F)
Else
BrowseFolder = F.Items.Item.Path
End If
End If
End Function