How to avoid self reference with IsWorkBookOpen - vba

I wanted to use shared excel workbooks on server which would be variously connected to each other.
I worked on it for many days to only find out it has many problems to cope with as shared workbooks do not support many features.
I wanted the macro to check on opening the file to find out whether the file is opened by someone else and if "yes", it would tell the person to come back later.
Unfortunately, I am now referencing to myself and this creates a loop.
I open the file, it checks itself it is opened already and then after the message it closes.
Could you please help me to evade the loop so it would suit the purpose?
I believe only the upper part of the code relates to my problem, so I do not post it in full:
Option Explicit
Private Sub Workbook_Open()
Dim Ret
Ret = IsWorkBookOpen(ThisWorkbook.FullName)
If Ret = True Then
MsgBox "Come back later."
ThisWorkbook.Close savechanges:=False
End If
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

I don't think you need to use that function for your purpose.
You can actually check the status of the file using:
ThisWorkbook.ReadOnly
which returns a boolean; true if file is readonly.
Now, before opening the file you really cannot suppress the pop up asking if you want to open it as read only or not. But you can still try putting this code which will trigger once the user opened the file as read only.
Private Sub Workbook_Open()
If ThisWorkbook.ReadOnly Then
MsgBox "Comeback some other time. File in use"
ThisWorkbook.Close False
End If
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Application.Quit
End Sub

The beauty of the IsWorkBookOpen approach is the speed compared to opening the workbook over a network compared to a ReadOnly test.
I would recommend you change your approach to load the check from a vbs, or a separate Excel file - as this is the best method.
The code below can be save in NotePad on your network drive as a vbs, say check.vbs. The code checks if the file is open, if not then it launches the file in a new instance of Excel. If it is, a message is provided.
vbs code
Dim objExcel
FileName = "C:\temp\file.xlsm"
If Not IsWorkBookOpen(FileName) Then
Set objExcel = CreateObject("Excel.Application")
Set ojbWb = objExcel.Workbooks.Open(FileName)
objExcel.Visible = True
Else
wscript.echo FileName & " already opened"
End If
Function IsWorkBookOpen(FileName)
Set oFSO = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
Set objFile = oFSO.OpenTextFile(filename, 8, False)
ErrNo = Err
ObjFile.Close
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
set objFSO = Nothing
End Function
If you want to run the check from the actual file then you will need to change to the ReadOnly test
code
Private Sub Workbook_Open()
If Me.ReadOnly Then MsgBox "file already opened", vbCritical
End Sub

I post this as I have seen this question unanswered on other forums, too.
I wanted to use many shared excel workbooks on server which would be variously connected to each other.
I wanted to use the so called shared workbook so everyone could access it and especially for the reason that it would track the changes within (users, time,...)
I wanted the macro to check on opening the file to find out whether the file is opened by someone else and if "yes", it would tell the person to come back later.
Suggested replies here could not help and I believe there is no chance to create a macro for path of the file itself so that IsWorkBookOpen(ThisWorkbook.FullName)
could be used the way I tried.
I decided to write macro that would track changes within the Workbook as if it was "shared". This way I was able to "unshare" it and use the ReadOnly approach discussed here.

Related

MSAccess crashes opening a file

I am using Office 365 on a windows 10 PC.
I have some VBA code in Access to check whether a file is open or locked (the file is local to this PC).
On one computer this code runs for most files, but consistently crashes when it reaches a particular set of files. It is the same set of files each time if I manually step through the code to move on from the first file. I tried rebooting the PC to clear any locks, but the result is the same.
When I say crash, I mean that I loose control of Access and windows reports that it is no longer responding.
If I run the same code on a different PC, referring to the same files, it reports the file is locked, but does not crash.
The file is not locked, or not in the way I understand file locking. From the user interface, I can rename, move or delete the files at will.
I am fairly certain there is nothing wrong with the VBA code as written and am beginning to think there may be a corrupt DLL somewhere.
VBA references
My code crashes at the line Open my_source For Input Lock Read As #ff
Function copyormovemyfiles(my_source As String, my_dest As String, mycontrol As Integer) As Boolean
Dim fso As Scripting.FileSystemObject
Dim ff As Long, ErrNo As Long
''''''''''''''''
' mycontrol = 1 for a move
' mycontrol = 2 for a copy. It will not overwrite files
''''''''''''''''
On Error GoTo error_control
Set fso = New Scripting.FileSystemObject
If Not fso.FileExists(my_source) Then
Err.Raise 1000, , my_source & " does not exist!" & vbExclamation & "Source File Missing"
ElseIf Not fso.FileExists(my_dest) Then
fso.CopyFile my_source, my_dest, True
Else
Err.Raise 1000, my_dest & " already exists!" & vbExclamation
End If
Select Case mycontrol
Case 1
On Error Resume Next
ff = FreeFile()
Open my_source For Input Lock Read As #ff
Close ff
ErrNo = Err
'On Error GoTo 0
If ErrNo > 0 Then Stop
Err.Clear
'Select Case ErrNo
'Case 0: IsWorkBookOpen = False
'Case 70: IsWorkBookOpen = True
'Case Else: Error ErrNo
'End Select
On Error GoTo error_control
It's best to just do an action and then deal with the fail case instead of testing beforehand. The reason is that the state could change between your test and the action. Also, you are raising errors manually when you can just let your code raise it's errors organically.
So you say your copy won't overwrite but then you tell the copy command to overwrite. If we tell it not to overwrite then we no longer have to test if the source or destination exist, they both result in clear errors.
NOTE: Don't use underscore "_" in variable or function names because those are used for event definitions in the VBA event handler.
Function copyormovemyfiles(my_source As String, my_dest As String, mycontrol As Integer) As Boolean
''''''''''''''''
' mycontrol = 1 for a move
' mycontrol = 2 for a copy. It will not overwrite files
''''''''''''''''
On Error GoTo error_control
Dim fso As Scripting.FileSystemObject
fso.CopyFile my_source, my_dest, overwrite:=False
If mycontrol = 1 Then
SetAttr my_source, vbNormal
fso.DeleteFile my_source
End If
copyormovemyfiles = True
error_control:
If Err.Number <> 0 Then
' You can select case here and handle the error
copyormovemyfiles = False
End If
End Function

Issues with detecting open file

I have literally copied and pasted and tested every bit of code from may BBs and the same thing happens with all of them. they all either tell me that that the file is open if it's open or closed or they tell me that the file is closed when it is open or closed. The code never gets it correct. Here is the last thing I tried and it was telling me it was not open when it was and when it wasn't
Can someone tell me if this is due to the file being located on the network
Sub Is_WorkBook_Open()
Dim wBook As Workbook
On Error Resume Next
Set wBook = Workbooks("X:\Audit Tracking\Team_Larry\DailyReports\Larry_Blank.xlsm")
'Not open
If wBook Is Nothing Then
MsgBox "Larry's Workbook is not open, Proceed to posting", vbCritical
Set wBook = Nothing
On Error GoTo 0
'It is open
Else
MsgBox "Yes it is open, Notify Supervisor to close file", vbInformation
Set wBook = Nothing
On Error GoTo 0
End If
End Sub
it was telling me it was not open when it was and when it wasn't
The Application.Workbooks collection contains all the workbooks opened in this instance of Excel.Application; if the workbook is opened by someone else on another machine, it's not in the collection and you can't use that method to know this.
If you're using the latest & greatest Excel 2016 on Office 365, see how you can dismiss that concern altogether using co-authoring features.
Otherwise, you can try sharing the workbook and then Excel can tell you exactly who has it opened, but then shared workbooks has a number of issues, including but not limited to, the inability to edit VBA code.
Using a hard-coded path is a good way to get false negatives, too. Open the file, verify its actual FullName and use that.
Dim i As Long
For i = 1 To Application.Workbooks.Count
Debug.Print Application.Workbooks(i).FullName
Next
If the file's location doesn't really matter, only its file name, you can iterate the opened files and see if one has a matching file name:
Dim i As Long
For i = 1 To Application.Workbooks.Count
If Application.Workbooks(i).Name = "Larry_Blank.xlsm" Then
MsgBox "File is opened."
Exit For
End If
Next
When you open an Excel workbook a hidden temporary copy of the workbook will be created. This is presumably used to recovery crashed files. Notice that the temporary workbook's name and path is the same as the actual workbook but has ~$ prefixed to the filename. Since the file path remains the same, we can assume that the ↓`isWorkbookOpen()↓ will work even with mapped and shared folders.
Function isWorkbookOpen(Path As String) As Boolean
Dim values() As String
values = Split(Path, "\")
values(UBound(values)) = "~$" & values(UBound(values))
Path = Join(values, "\")
isWorkbookOpen = Len(Dir(Path, vbHidden)) > 0
End Function
I believe your code will test if you have it open, on the computer your running the code from.
This code will open the workbook, if it opens in a read only state then someone else has it open. Note: If you open it on your computer, and then run this code on the same computer it will report that it's not in a read only state.
Sub Test()
Dim oWB As Workbook
Set oWB = Application.Workbooks.Open("C:\Temp\test.xlsx")
If oWB.ReadOnly Then
MsgBox "Open"
Else
MsgBox "Closed"
End If
oWB.Close
End Sub

Excel vba, Opening new Application: Microsoft Excel is waiting for another application to complete an OLE action

I have the following vba code. It creates new Excel application and uses it to open a file. Then it MsgBoxes some cell's value in this file.
Sub TestInvis()
Dim ExcelApp As Object
Set ExcelApp = CreateObject("Excel.Application")
Dim WB As Workbook
Set WB = ExcelApp.Application.Workbooks.Open("Y:\vba\test_reserves\test_data\0503317-3_FO_001-2582480.XLS")
Dim title As String
title = WB.Worksheets(1).Cells(5, 4).Value
MsgBox (title)
WB.Save
WB.Close
ExcelApp.Quit
Set ExcelApp = Nothing
End Sub
The problem is that after MsgBoxing it slows down and eventually gives a Microsoft Excel is waiting for another application to complete an OLE action window. Why does it do this? It's not like there are any hard commands being implemented. And how should I deal with it?
This happens because the Excel instance in ExcelApp is waiting for User Input, most likely.
You can try to add ExcelApp.DisplayAlerts = False to skip any pop-ups that might be there.
Also, while troubleshooting add the line ExcelApp.Visible = True so you can see what's going on in the second instance and troubleshoot there.
I encountered this problem in the following situations:
An alert was opened by the Application Instance and it was awaiting user input.
While opening a file, it was coming up with some message about a crash when the file was previously opened and whether I wanted to open the saved version or the in memory version (although this should happen before the msgBox)
If you run the code multiple times and it crashes, it might have the file open as read only since there's another hidden instance of Excel that locked it (check your task manager for other Excel processes)
Rest assured that in any case the problem is not with your code itself - It runs fine here.
Code that works for me.
You can select the file from FileDialog. In comments You have code that close the workbook without saving changes. Hope it helps.
Option Explicit
Sub Import(Control As IRibbonControl)
Dim fPath As Variant
Dim WB As Workbook
Dim CW As Workbook
On Error GoTo ErrorHandl
Set CW = Application.ActiveWorkbook
fPath = Application.GetOpenFilename(FileFilter:="Excel file, *.xl; *.xlsx; *.xlsm; *.xlsb; *.xlam; *.xltx; *.xls; *.xlt ", Title:="Choose file You want to openn")
If fPath = False Then Exit Sub
Application.ScreenUpdating = False
Set WB = Workbooks.Open(FileName:=fPath, UpdateLinks:=0, IgnoreReadOnlyRecommended:=True)
Set WB = ActiveWorkbook
MsgBox("File was opened.")
'Application.DisplayAlerts = False
'WB.Close SaveChanges:=False
'Application.DisplayAlerts = True
'MsgBox ("File was closed")
Exit Sub
ErrorHandl:
MsgBox ("Error occured. It is probable that the file that You want to open is already opened.")
Exit Sub
End Sub
None of these methods worked for me. I was calling a DLL for MATLAB from VBA and a long simulation would pop up that Excel was waiting on another application OLE action, requiring me to click it off for the routine to continue, sometimes quite a few times. Finally this code worked (saved in a new module): https://techisours.com/microsoft-excel-is-waiting-for-another-application-to-complete-an-ole-action/
The way I used it is a little tricky, as the directions don't tell you (here and elsewhere) which causes various VBA errors, so I add to the description for what works in Excel 365:
Create a new module called "ToggleOLEWarning" (or in any new module, important!) which only contains the following code:
Private Declare Function CoRegisterMessageFilter Lib "ole32" (ByVal IFilterIn As Long, ByRef PreviousFilter) As Long
Public Sub KillOLEWaitMsg()
Dim IMsgFilter As Long
CoRegisterMessageFilter 0&, IMsgFilter
End Sub
Public Sub RestoreOLEwaitMsg()
Dim IMsgFilter As Long
CoRegisterMessageFilter IMsgFilter, IMsgFilter
End Sub
Then in your main function, just decorate the long running OLE action with a couple lines:
Call KillOLEWaitMsg
'call your OLE function here'
Call RestoreOLEwaitMsg
And it finally worked. Hope I can save someone the hour or two it took for me to get it working on my project.

How to check if a workbook is open and use it

I've made a macro to open two workbooks and do some stuff with them. This macro runs from a third workbook that calls any other two user selected workbooks for which, before they're opened, I don't know their name.
So! I know Excel 2010 doesn't have a built in function to check if a workbook is open so, I've been trying to compare the workbook against Nothing but it doesn't work and every workaround I find in different sites tend to use the name of the workbook.
Is there another way of doing this?
The idea is to run a macro with the two user defined workbooks and then, maybe, re-running it in the same workbooks but Excel warms me of discarding changes.
Maybe a workaround could be to tell excel when it prompts for reopening, not to reopen and handle that error to just use the same workbooks, for which at least, I know how part or the names will be. For example, one will have the text "cluster" in it, and the other the word "translation" so, maybe in a loop like the next one, I could find and use the workbook I need but just If I already checked if it's open. Or, does this way works to see if it's opened already?
For each wbk in Application.Workbooks
If wbk.Name Like "*cluster*" Then
WorkingWorkbook = wbk.Name
End If
next
My code is as follows:
Sub structure()
Application.ScreenUpdating = False
Dim translationWorkbook As Worksheet
Dim clusterWorkbook As Workbook
If Not clusterWorkbook Is Nothing Then
Set clusterWorkbook = Application.Workbooks.Open(ThisWorkbook.Sheets(1).Range("E5").Value2)
Else
Set clusterWorkbook = Application.Workbooks(parseFilePath(ThisWorkbook.Sheets(1).Range("E5")))
End If
Set translationWorkbook = Application.Workbooks.Open(ThisWorkbook.Sheets(1).Range("E6").Value2).Worksheets("String_IDs_Cluster") 'Translation table target for completing
End Sub
The parameter passed to Workbooks.Open is the one written in the sheet by my next function:
Private Sub MS_Select_Click()
Dim File As Variant
Dim Filt As String
Filt = "Excel 97-2003 File(*.xls), *.xls," & "Excel File(*.xlsx),*.xlsx," & "Excel Macro File (*.xlsm),*.xlsm"
File = Application.GetOpenFilename(FileFilter:=Filt, FilterIndex:=2, Title:="Select Menu Structure File")
If File = False Or File = "" Then
MsgBox "No File Selected"
Exit Sub
End If
ThisWorkbook.ActiveSheet.Range("E5").Value2 = File
End Sub
Same for translationWorkbook but in a different cell and also, I was trying to create a function to parse and use the filename in a full path(Then I discovered the command Dir lol) but when I pass the filename, without the xls extension to Application.Workbooks(file) it sends me a "subscript range error". Why could that be?
Basically my questions are:
How can I check for an open workbook and use it? Either by handling the
error for excel's prompt or by not trying to reopen the same file.
Why does trying to open a workbook with Application.Workbooks() with the return of my function fails? And here my question splits in two... First: with my function, wouldn't it work if I give a string as an argument? Or maybe, before passing it as an argument, I need to assign the result of my function to a variable?
Second: If I try to open a workbook like this Application.Workbooks("clusterworkbook") it sends me another "subscript error" but, before I used the File Dialog prompt, I made it this way and worked fine.
Any help will be appreciated.
EDIT
Function ParseFilePath added:
Function parseFilePath(fullpath As Range) As String
Dim found As Boolean
Dim contStart As Integer
Dim contEnd As Integer
contEnd = InStr(fullpath, ".") - 1
contStart = contEnd
found = False
Do While found = False
If fullpath.Characters(contStart, 1).Text = "\" Then
found = True
Else
contStart = contStart - 1
End If
Loop
parseFilePath = fullpath.Characters(contStart + 1, (contEnd - contStart)).Text
End Function
How can I check for an open workbook and use it? Either by handling the error for excel's prompt or by not trying to reopen the same file.
Have done some small modifications to your procedure structure. Similar to what you were trying testing for the workbook variable to be nothing, only that you have to first attempt to set the variable, the way you were doing it will always return empty as you did not try to set it before. I have also tested for the translation workbook, as it mightt be open as well.
I'm assuming the values in E5 and E6 contain the FullName of the workbook (i.e. path + filename) and that parseFilePath is a function to extract the filename from the FullName.
Sub structure()
Application.ScreenUpdating = False
Dim clusterWorkbook As Workbook
Dim translationWorkbook As Workbook
Dim translationWorksheet As Worksheet
With ThisWorkbook.Sheets(1)
On Error Resume Next
Set clusterWorkbook = Application.Workbooks(parseFilePath(.Range("E5").Value2))
On Error GoTo 0
If clusterWorkbook Is Nothing Then Set clusterWorkbook = Application.Workbooks.Open(.Range("E5").Value2)
'Set Translation table target for completing
On Error Resume Next
Set translationWorkbook = Application.Workbooks(parseFilePath(.Range("E6").Value2))
On Error GoTo 0
If translationWorkbook Is Nothing Then
Set translationWorksheet = Application.Workbooks.Open(.Range("E6").Value2).Sheets("String_IDs_Cluster")
Else
Set translationWorksheet = translationWorkbook.Sheets("String_IDs_Cluster")
End If
End With
End Sub
Why does trying to open a workbook with Application.Workbooks() with
the return of my function fails? And here my question splits in two...
First: with my function, wouldn't it work if I give a string as an
argument? Or maybe, before passing it as an argument, I need to assign
the result of my function to a variable?
Not sure why it did not work, change the prodedure as indicated.
I tested the procedure above using this function to extract the Filename from the Fullname and it worked:
Function parseFilePath(sFullName As String) As String
parseFilePath = Right(sFullName, Len(sFullName) - InStrRev(sFullName, "\"))
End Function
Second: If I try to open a workbook like this Application.Workbooks("clusterworkbook") it sends me another
"subscript error" but, before I used the File Dialog prompt, I made it
this way and worked fine.
Bear in mind that you did not used that line alone, it most probably has something like:
set Workbook = Application.Workbooks("clusterworkbook")
So the command was to set a variable, not to open the workbook, as such the only situation in which this works is that the workbook is already open so the variable gets set. The times when it failed was when the workbook was not open and you tried to set the variable, given you an error.
Suggest to visit these pages
Excel Objects, On Error Statement
I have been using the below code to identify if the excel workbook is open. If yes, then i activate it and do some stuff. If not, i open it and do some stuff.
sub test()
Dim Ret
Ret = IsWorkBookOpen("Your excel workbook full path")
If Ret = False Then
Workbooks.Open FileName:="Your excel workbook full path", UpdateLinks:=False
Else
Workbooks("Workbook name").Activate
End If
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

Verify if workbook is open access vba Error53

Hi I'm using a code to verify if a workbook is open and if it is I message the user to close the workbook. This is for an MS ACCESS Form. My current code is as follows:
Option Explicit
Sub Sample()
Dim Ret
Dim strPath as string
strpath = "C:\myWork.xlsx"
Ret = IsWorkBookOpen(strpath)
If Ret = True Then
MsgBox "File is open"
Else
MsgBox "File is Closed"
End If
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
I use it in different forms and the Directory changes depending on where the user wants it. It sometimes work but most of the time I get a Run Time Error. When I verify the Err variable it's alaway Err = 53 if its open or not. Sometimes Err = 70 or Err = 0 and the code runs smoothly but it is never the case.
Error #53 is File Not Found.
This site has code that does exactly what you're trying to do:
http://accessexperts.com/blog/2012/03/06/checking-if-files-are-locked/
The error you are getting means "file not found" (you can use Err.Description to get something more informative than "53"). Maybe there is a problem with the name, or the path, or some stray characters crept in... Or maybe it has to do with the way you generated the names in the first place. See http://www.excelforum.com/excel-programming-vba-macros/727403-runtime-error-53-file-not-found.html for a possible scenario and workaround. Without more information about the file name you passed to your routine when it failed it is hard to be more specific.
I was having the exact same problem. In my case, I was able to fix the issue when I realized I was sending the IsWorkbookOpen Function just the name of the file, but without the path. When I modified my initial code to send the file an argument in the form of a string that contained both the path and the filename, concatenated together, it worked.