Check which file is open VBA - vba

All,
I have a large module which in the earlier part checks whether a files is in use (Readonly) format and if it is in use to open the next file. I.e. if file one is in use open file two etc..
In a later part of the module I wish to use the file which has been opened. However I am struggling to identify the file which is opened in the earlier part of the automation and set is as WB.
The code I am currently using is;
Dim wb As Object
On Error Resume Next
Workbooks("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions1.csv").Activate
If Err.Number = 0 Then
wb = GetObject("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions1.csv")
GoTo skipline
End If
On Error GoTo 0
On Error Resume Next
Workbooks("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions2.csv").Activate
If Err.Number = 0 Then
wb = GetObject("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions2.csv")
GoTo skipline
End If
On Error GoTo 0
On Error Resume Next
Workbooks("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions3.csv").Activate
If Err.Number = 0 Then
wb = GetObject("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions3.csv")
GoTo skipline
End If
On Error GoTo 0
On Error Resume Next
Workbooks("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions4.csv").Activate
If Err.Number = 0 Then
wb = GetObject("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions4.csv")
GoTo skipline
End If
skipline:
On Error GoTo 0
Can anyone recommend how I can identify which file is open and set is as WB
Any help would be much appreciated.
Thanks

Don't try to match the path: mapped drives and aliases will spoof your matches.
Your match term is the file name, with the extension, and you can iterate the Excel workbooks collection to see if there's a matching name:
Option Explicit
Public Function WorkbookIsOpen(WorkBookName As String) As Boolean
' Returns TRUE if a workbook (or csv file open in Excel) is open
Dim wbk As Excel.Workbook
WorkbookIsOpen = False
If IsError(WorkBookName) Then
WorkbookIsOpen = False
ElseIf WorkBookName = "" Then
WorkbookIsOpen = False
Else
For Each wbk In Application.Workbooks
If wbk.Name = WorkBookName Then
WorkbookIsOpen = True
Exit For
End If
Next wbk
End If
End Function
Public Function FileName(FilePath As String) As String
' Returns the last element of a network path
' This is usually the file name, but it mat be a folder name if FilePath is a folder path:
' FileName("C:\Temp\Readme.txt") returns "ReadMe.txt"
' ?FileName("C:\Temp") returns "Temp"
' FileName("C:\Temp\") returns ""
' This function does not perform any file checking - the file need not exist, the path
' can be invali or inaccessible. All we're doing is String-handling.
Dim arr() As String
Dim i As Integer
If IsError(FilePath) Then
FileName = "#ERROR"
ElseIf FilePath = "" Then
FileName = ""
Else
arr = Split(Trim(FilePath), "\")
i = UBound(arr)
FileName = arr(i)
Erase arr
End If
End Function
Then it's just a matter of checking if the open workbook is open read-only:
Dim bReadOnly As Boolean
If WorkbookIsOpen("C:Temp\Brian.csv") Then
bReadOnly = Application.WorkBooks(FileName("C:Temp\Brian.csv")).ReadOnly
End If
Things get a lot more interesting if you need to check that the file isn't open in another session of Excel, or another application: this code won't test that for you.
I need to answer the other point in your question: opening the file in Excel if it isn't already open in this session.
I would recommend using Application.Workbooks.Open(FileName) for that, as it's smarter than GetObject() and will open the file - csv, xml, xls, xlsx - in Excel, as a workbook, with Excel guessing the necessary format parameters. Also,the native 'open' function allows you to specify additional parameters, like Read-Only.

Related

VBA Function to return the value only when File can be opened without any error

I'm writing an Excel VBA code to Check how many files in a folder are corrupt.
A folder named as 'Folder' in 'E' drive has these pdf files.
In my workbook; Column A of Sheet1 has fileNames from this folder.
I have a code which loops through the filesNames from column A and opens those.
My objective is : If the file can be opened; then don't print anything in the adjacent cell (of column B) else print as 'Corrupt'.
But, when I run this VBA code; as each time loop goes to the Function OpenPDFPage(), it do not print anything in the adjacent cell of column B.
(I want to print it only when the file is corrupt and I get message box saying "There was an error opening this document. The file is damaged and could not be repaired")
Can I know what change I've to make to the Function OpenPDFPage() so that, when there is a corrupt file (or the file which can't be opened) in folder; then only code will print "corrupt" in the adjacent cell of column B.
The code is as below:
Option Explicit
Function OpenPDFPage(PDFPath As String) As Boolean
On Error GoTo Error_OpenPDFPage
ThisWorkbook.FollowHyperlink PDFPath, NewWindow:=True
OpenPDFPage = True
Exit_OpenPDFPage:
Exit Function
Error_OpenPDFPage:
MsgBox Err & ": " & Err.Description
OpenPDFPage = False
Resume Exit_OpenPDFPage
End Function
Sub Test()
Dim MyFolder As String
Dim filename As Range
Dim MyFile As String
Dim lastRow As Long
lastRow = Sheets("Sheet1").UsedRange.Rows.Count
MyFolder = "E:\Folder"
For Each filename In Worksheets("Sheet1").Range("A2:A" & lastRow)
MyFile = MyFolder & "\" & filename
If OpenPDFPage(MyFile) = True Then
'Do Nothing
Else
filename.Offset(0, 1).Value = "Corrupt"
End If
Next
End Sub
Added another macro to the above; to check whether the file is opened;
If it's not opened through previous macro, then am assuming it's corrupt;
and printing in adjacent cell of column B as 'Corrupt'.
Sub IsFileOpened()
Dim MyFolder As String
Dim filename As Range
Dim MyFile As String
Dim lastRow As Long
lastRow = Sheets("Sheet1").UsedRange.Rows.Count
MyFolder = "E:\Folder"
For Each filename In Worksheets("Sheet1").Range("A1:A" & lastRow)
MyFile = MyFolder & "\" & filename
If IsFileOpen(MyFile) = True Then
' Do Nothing
Else
filename.Offset(0, 1).Value = "Corrupt"
End If
Next
End Sub
Function IsFileOpen(filename As String) As Boolean
Dim filenum As Integer, errnum As Integer
On Error Resume Next
filenum = FreeFile()
'Attempt to open the file and lock it.
Open filename For Input Lock Read As #filenum
Close filenum ' Close the file.
errnum = Err ' Save the error number that occurred.
On Error GoTo 0 ' Turn error checking back on.
' Check to see which error occurred.
Select Case errnum
Case 0
IsFileOpen = False
Case 70
IsFileOpen = True
' Another error occurred.
'Case Else
'Error errnum
End Select
End Function

Check if excel file is open, if yes close file,if no convert csv file to excel Visual Basic [duplicate]

This question already has answers here:
Detect whether Excel workbook is already open [closed]
(7 answers)
Closed 7 years ago.
I'm having a problem creating a condition. Please see pseudo code below. thanks in advance
Check if File A.xls is open
If File A.xls is Open
Close File A.xls
Else
Convert File A.csv to .xls
End If
Convert File A.csv to .xls
Dim DeleteEntries As Workbook
Dim WorksheetDeleteEntries As Worksheet
Dim WbOpen As Boolean
'Convert Acc_FR044_SAP.csv to excel
strDir = "C:\FR044 Automated Checker\"
strFile = Dir(strDir & "Acc_FR044_SAP.csv")
If Workbooks("Acc_FR044_SAP.xls") Is Nothing Then ' IM HAVING AN SUBSCRIPT ERROR IN THIS LINE
WbOpen = False
Else
Workbooks("Acc_FR044_SAP.xls").Close SaveChanges:=False
End If
Application.DisplayAlerts = False
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
Try with this solution which works for current instance of Excel:
On Error Resume Next
Dim tmpWB As Workbook
Set tmpWB = Workbooks("Acc_FR044_SAP.xls")
On Error GoTo 0
If tmpWB Is Nothing Then
WbOpen = False
Else
tmpWB .Close SaveChanges:=False
End If
Something like this to check if the file was open in any instance, on any machine
Sub Sample()
Dim bFileOpen As Boolean
bFileOpen = IsWorkBookOpen("C:\yourfilename.xlsx")
If bFileOpen Then
MsgBox "File is open"
Else
MsgBox "File is Closed"
End If
End Sub
testing function from Microsoft example here
Function IsWorkBookOpen(FileName As String)
Dim ff As Long
Dim ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0
IsWorkBookOpen = False
Case 70
IsWorkBookOpen = True
Case Else
End Select
End Function

Is there any way to make notification about current user with writing access in excel file

Everyone knows that default (in-build) Excel notification about current user with writing access to excel file (at server with access from multiple users) DOES NOT WORK properly. It means, when someone tries to open some excel file which is already opened by someone else, Excel sometimes only says that the file is opened by another user without his identification.
To correct this wrong behavior of excel, one can write code as follows:
Sub auto_open()
Dim fso As FileSystemObject: Set fso = New FileSystemObject
Dim strPath As String: strPath = ActiveWorkbook.Path & "\writer.txt"
If Not ActiveWorkbook.ReadOnly Then
Dim oFile As Object: Set oFile = fso.CreateTextFile(strPath)
oFile.WriteLine Environ("username")
oFile.Close
Set oFile = Nothing
ElseIf Dir(strPath) <> "" Then
Dim txtStream As Object: Set txtStream = fso.OpenTextFile(strPath, ForReading, False)
MsgBox "User " & txtStream.ReadLine & " has this file opened for writing."
txtStream.Close
End If
Set fso = Nothing
End Sub
Sub auto_close()
If Not ActiveWorkbook.ReadOnly Then
Dim strPath As String: strPath = ActiveWorkbook.Path & "\writer.txt"
If Dir(strPath) <> "" Then
Kill strPath
End If
End If
End Sub
(while using "Microsoft Scripting Runtime" reference)
This all works well until you use old good .xls file. BUT once you start to use .xlsm modern format file, the auto_open subroutine is unfortunately not triggered in case that the second (the reading) user choose in the "File in use" warning message the "Notify" option. And thus once the first person leave the excel, the second gain the option to get the file for writing, but the text file with his username is not created (see the attached code) and thus the another user, opening the same excel file, will not know about current writing user.
Does anyone have please any idea how to correct this behavior in the .xlsm file? I guess the only way to correct it is to find out some subroutine, which is triggered once the user is reopening excel file for writing.
Rather than using ActiveWorkbook.ReadOnly try this more direct approach - which from my experience over a network is much faster than opening the file to test it - code courtesy of Bob Phillips
Sub test()
If Not IsFileOpen("C:\yourfilehere.xls") Then
Workbooks.Open "C:\yourfilehere.xls"
End If
End Sub
Function IsFileOpen(FileName As String)
Dim iFilenum As Long
Dim iErr As Long
On Error Resume Next
iFilenum = FreeFile()
Open FileName For Input Lock Read As #iFilenum
Close iFilenum
iErr = Err
On Error Goto 0
Select Case iErr
Case 0: IsFileOpen = False
Case 70: IsFileOpen = True
Case Else: Error iErr
End Select
End Function

How to make Excel VBA variables available to multiple macros?

I have a string of macros that call upon each other and refer to workbooks A and B. I want the first macro to prompt the user to select document A and B and these Selections to become the workbook A and B variables I refer to in the various macros.
How do I make the selected documents the referred to variable throughout all the macros?
Thanks in advance!
Declare them outside the subroutines, like this:
Public wbA as Workbook
Public wbB as Workbook
Sub MySubRoutine()
Set wbA = Workbooks.Open("C:\file.xlsx")
Set wbB = Workbooks.Open("C:\file2.xlsx")
OtherSubRoutine
End Sub
Sub OtherSubRoutine()
MsgBox wbA.Name, vbInformation
End Sub
Alternately, you can pass variables between subroutines:
Sub MySubRoutine()
Dim wbA as Workbook
Dim wbB as Workbook
Set wbA = Workbooks.Open("C:\file.xlsx")
Set wbB = Workbooks.Open("C:\file2.xlsx")
OtherSubRoutine wbA, wbB
End Sub
Sub OtherSubRoutine(wb1 as Workbook, wb2 as Workbook)
MsgBox wb1.Name, vbInformation
MsgBox wb2.Name, vbInformation
End Sub
Or use Functions to return values:
Sub MySubroutine()
Dim i as Long
i = MyFunction()
MsgBox i
End Sub
Function MyFunction()
'Lots of code that does something
Dim x As Integer, y as Double
For x = 1 to 1000
'Lots of code that does something
Next
MyFunction = y
End Function
In the second method, within the scope of OtherSubRoutine you refer to them by their parameter names wb1 and wb2. Passed variables do not need to use the same names, just the same variable types. This allows you some freedom, for example you have a loop over several workbooks, and you can send each workbook to a subroutine to perform some action on that Workbook, without making all (or any) of the variables public in scope.
A Note About User Forms
Personally I would recommend keeping Option Explicit in all of your modules and forms (this prevents you from instantiating variables with typos in their names, like lCoutn when you meant lCount etc., among other reasons).
If you're using Option Explicit (which you should), then you should qualify module-scoped variables for style and to avoid ambiguity, and you must qualify user-form Public scoped variables, as these are not "public" in the same sense. For instance, i is undefined, though it's Public in the scope of UserForm1:
You can refer to it as UserForm1.i to avoid the compile error, or since forms are New-able, you can create a variable object to contain reference to your form, and refer to it that way:
NB: In the above screenshots x is declared Public x as Long in another standard code module, and will not raise the compilation error. It may be preferable to refer to this as Module2.x to avoid ambiguity and possible shadowing in case you re-use variable names...
You may consider declaring the variables with moudule level scope.
Module-level variable is available to all of the procedures in that module, but it is not available to procedures in other modules
For details on Scope of variables refer this link
Please copy the below code into any module, save the workbook and then run the code.
Here is what code does
The sample subroutine sets the folder path & later the file path. Kindly set them accordingly before you run the code.
I have added a function IsWorkBookOpen to check if workbook is already then set the workbook variable the workbook name
else open the workbook which will be assigned to workbook variable accordingly.
Dim wbA As Workbook
Dim wbB As Workbook
Sub MySubRoutine()
Dim folderPath As String, fileNm1 As String, fileNm2 As String, filePath1 As String, filePath2 As String
folderPath = ThisWorkbook.Path & "\"
fileNm1 = "file1.xlsx"
fileNm2 = "file2.xlsx"
filePath1 = folderPath & fileNm1
filePath2 = folderPath & fileNm2
If IsWorkBookOpen(filePath1) Then
Set wbA = Workbooks(fileNm1)
Else
Set wbA = Workbooks.Open(filePath1)
End If
If IsWorkBookOpen(filePath2) Then
Set wbB = Workbooks.Open(fileNm2)
Else
Set wbB = Workbooks.Open(filePath2)
End If
' your code here
End Sub
Function IsWorkBookOpen(FileName As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function
Using Prompt to select the file use below code.
Dim wbA As Workbook
Dim wbB As Workbook
Sub MySubRoutine()
Dim folderPath As String, fileNm1 As String, fileNm2 As String, filePath1 As String, filePath2 As String
Dim filePath As String
cmdBrowse_Click filePath, 1
filePath1 = filePath
'reset the variable
filePath = vbNullString
cmdBrowse_Click filePath, 2
filePath2 = filePath
fileNm1 = GetFileName(filePath1, "\")
fileNm2 = GetFileName(filePath2, "\")
If IsWorkBookOpen(filePath1) Then
Set wbA = Workbooks(fileNm1)
Else
Set wbA = Workbooks.Open(filePath1)
End If
If IsWorkBookOpen(filePath2) Then
Set wbB = Workbooks.Open(fileNm2)
Else
Set wbB = Workbooks.Open(filePath2)
End If
' your code here
End Sub
Function IsWorkBookOpen(FileName As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function
Private Sub cmdBrowse_Click(ByRef filePath As String, num As Integer)
Dim fd As FileDialog
Set fd = Application.FileDialog(msoFileDialogFilePicker)
fd.AllowMultiSelect = False
fd.Title = "Select workbook " & num
fd.InitialView = msoFileDialogViewSmallIcons
Dim FileChosen As Integer
FileChosen = fd.Show
fd.Filters.Clear
fd.Filters.Add "Excel macros", "*.xlsx"
fd.FilterIndex = 1
If FileChosen <> -1 Then
MsgBox "You chose cancel"
filePath = ""
Else
filePath = fd.SelectedItems(1)
End If
End Sub
Function GetFileName(fullName As String, pathSeparator As String) As String
Dim i As Integer
Dim iFNLenght As Integer
iFNLenght = Len(fullName)
For i = iFNLenght To 1 Step -1
If Mid(fullName, i, 1) = pathSeparator Then Exit For
Next
GetFileName = Right(fullName, iFNLenght - i)
End Function
Create a "module" object and declare variables in there. Unlike class-objects that have to be instantiated each time, the module objects are always available. Therefore, a public variable, function, or property in a "module" will be available to all the other objects in the VBA project, macro, Excel formula, or even within a MS Access JET-SQL query def.

VBScript - How do I get these workbooks to talk?

I posted earlier about getting my VBScript to wait until a process had finished before continuing (further info: VBScript - How to make program wait until process has finished?.
I was given an adequate answer after some discussion. However, it seems that I am now going in a new direction with the code as the solution presented another problem that I am hoping you may be able to help me with.
Basically I have some code which I have provided below. It takes in 4 arguments, one of which is a PATH to a folder containing many files which I want to use along with the other three in my VBA macro.
If WScript.Arguments.Count = 4 Then
' process input argument
Set args = WScript.Arguments
arg1 = args.Item(0)
arg2 = args.Item(1)
arg3 = args.Item(2)
arg4 = args.Item(3)
' Create a WshShell instance
Dim WShell
Set WShell = CreateObject("WScript.Shell")
' Create an Excel instance
Dim x1
Set x1 = CreateObject("Excel.Application")
' Disable Excel UI elements
x1.DisplayAlerts = False
x1.AskToUpdateLinks = False
'x1.AlertBeforeOverwriting = False
x1.FeatureInstall = msoFeatureInstallNone
' Open the Workbooks specified on the command-line
Dim x1WB
Dim x2WB
Dim x3WB
Dim x4WB
Dim strWB1
Dim strWB2
Dim strWB3
Dim strWB4
Dim FSO
Dim FLD
Dim FIL
Dim strFolder
strWB1 = arg1
Set x1WB = x1.Workbooks.Open(strWB1)
' Show the workbook/Excel program interface. Comment out for silent running.
x1WB.Application.Visible = True
strWB2 = arg2
Set x2WB = x1.Workbooks.Open(strWB2)
' Show the workbook/Excel program interface. Comment out for silent running.
x2WB.Application.Visible = True
strWB3 = arg3
Set x3WB = x1.Workbooks.Open(strWB3)
' Show the workbook/Excel program interface. Comment out for silent running.
x3WB.Application.Visible = True
'To hold the string of the PATH to the multiple files
strFolder = arg4
Set FSO = CreateObject("Scripting.FileSystemObject")
'Get a reference to the folder I want to search
set FLD = FSO.GetFolder(strFolder)
Dim strMyMacro
strMyMacro = "my_excel_sheet_with_vba_module.xlsm!Sheet1.my_vba_macro"
'loop through the folder and get the file names
For Each Fil In FLD.Files
WshShell.run """C:\Program Files\Microsoft Office\Office14\EXCEL.exe"" " & Fil, 1, true
x1.Run strMyMacro
'~~> Problem - How do I get the macro to run before opening the above file but run after it has opened (due to setting the bWaitOnReturn to true)
'~~> Problem - How do I get the file on current iteration to close after the macro has completed?
'~~> Problem - If this is not the issue, can you identify it?
Next
x1WB.close
x2WB.close
x3WB.close
'x4WB.close
' Clean up and shut down
Set x1WB = Nothing
Set x2WB = Nothing
Set x3WB = Nothing
Set x4WB = Nothing
Set FSO = Nothing
Set FLD = Nothing
x1.Quit
Set x1 = Nothing
Set WshShell = Nothing
WScript.Quit 0
Else
WScript.Quit 1
End If
The script works like this:
4 arguments are passed to the script. The 3rd argument is a .xlsm file which contains my VBA macro. The last argument is a PATH to a folder containing multiple files.
It then opens up the first three Excel files.
Then I run a loop to iterate through the files Fil in the folder that was specified as the 4th argument. AFAIK this has to be done via a WScript.shell using the .run method so that the rest of the script will hang until the Excel file it is processing finishes before closing it and opening up the next file in the folder.
After opening up file Fil, I then run the macro (albeit at this moment in time unsuccessfully).
I was tempted to simply open up all of the Excel files using the WScript.shell object however AFAIK I would not be able to run the macro this way.
Hopefully I have been able to define my aims of this piece of VBScript though if I haven't let me know and I shall clarify. Can you help?
Thanks,
QF.
Something along these lines might work for you (in Excel). A few things I'm not clear on though:
Where is your existing VBA macro - I'm guessing it's in one of the 3 files you're opening?
What types of files are in the folder you're looping through? I guessed Excel.
How is the vbscript being run? It looks like you're shelling out from your HTA, but why not include it directly in the HTA? That would save you from having to shell out and pass arguments...
Option Explicit
Dim wb1 As Workbook, wb2 As Workbook
Sub ProcessFolder(path1, path2, sFolder)
Dim wb As Workbook
Dim s
Set wb1 = Workbooks.Open(path1)
Set wb2 = Workbooks.Open(path2)
If Right(sFolder, 1) <> "\" Then sFolder = sFolder & "\"
s = Dir(sFolder & "*.xls*", vbNormal)
Do While Len(s) > 0
Set wb = Workbooks.Open(sFolder & s)
ProcessFile wb
wb.Close False
s = Dir()
Loop
wb1.Close False
wb2.Close False
End Sub
Sub YourExistingMacro(wb As Workbook)
'do stuff with wb and presumably the other 3 open files...
End Sub