VBA Code to Update Links if Linked-to Workbook Closed - vba

I have a workbook within which there are links to number of other workbooks. Ideally I'd like to code: When I open the workbook containing the links, will update the links, but only if the workbook that is being linked to is not open (either on my PC or another user's PC on the network, that can access it).
This is because:
a) if the workbook is already open the link should update anyway,
b) updating links to workbooks that are already open seems to cause errors.
I hope I'm making sense, please tell me if I'm not.
Thanks.

Try this
Sub UpdateLinks()
Dim v As Variant, i As Long
v = ThisWorkbook.LinkSources(XlLink.xlExcelLinks)
For i = 1 To UBound(v)
If Not FileInUse(v(i)) Then
Workbooks.Open (v(i))
End If
Next i
ActiveWorkbook.UpdateLink Name:=ActiveWorkbook.LinkSources
End Sub
Public Function FileInUse(sFileName) As Boolean 'Checks if a workbook is open
On Error Resume Next
Open sFileName For Binary Access Read Lock Read As #1
Close #1
FileInUse = IIf(Err.Number > 0, True, False)
On Error GoTo 0
End Function

Related

When two workbooks are opened in visual basic, workbook variables do not get assigned correctly

When running the code below, I get different results depending on which workbooks are open. The sub is in a module associated with Master Sheet.xlsm
If just the Master Sheet.xlsm open then the code runs correctly, i.e. the Message Boxes say (where comma separates the first and second message box): Master Sheet, transferred cases 03-09-18
If both Master Sheet.xlsm and transferred cases 03-09-18.xlsx are open but transferred cases 03-09-18.xlsx was open second then the message boxes say: transferred cases 03-09-18, transferred cases 03-09-18
If both Master Sheet.xlsm and transferred cases 03-09-18.xlsx are open but Master Sheet.xlsm was open second then the message boxes say: Master Sheet, Master Sheet
Sub foo()
Dim x As Workbook
Dim y As Workbook
'## Open both workbooks first:
Set x = Workbooks.Open("C:\Users\owner\Documents\ExelatecOutput\Master Sheet.xlsm")
Set y = Workbooks.Open("C:\Users\owner\Documents\ExelatecOutput\transferred cases 03-09-18.xlsx")
'Now, copy what you want from x:
MsgBox x.Name
MsgBox y.Name
End Sub
Why do the variables x and y not get assigned correctly.
Workbooks.Open always return the last opened file (even if it is not the one passed in parameter). This is either bad documentation or bug in excel IMO.
You don't need to check if the file is opened, since opening an already open file does not raise an error, but you need to set the variable later:
Workbooks.Open "C:\Users\owner\Documents\ExelatecOutput\Master Sheet.xlsm": Set x = Workbooks("Master Sheet.xlsm") ' or Set x = ActiveWorkbook since Open will activate it
Workbooks.Open "C:\Users\owner\Documents\ExelatecOutput\transferred cases 03-09-18.xlsx": Set y = Workbooks("transferred cases 03-09-18.xlsx") ' or Set y = ActiveWorkbook since Open will activate it
Just a note, you can check if the workbooks are already open with a function like this where you pass a workbook name.
Public Function BookOpen(strBookName As String) As Boolean
Dim oBk As Workbook
On Error Resume Next
Set oBk = Workbooks(strBookName)
On Error GoTo 0
If oBk Is Nothing Then
BookOpen = False
Else
BookOpen = True
End If
End Function
If it returns true, you can set x = Workbooks("your workbook name")
When assigning to a Workbook variable with Workbooks.Open() it is implied that the workbook, which is to be opened is closed. Otherwise, it takes either the last opened workbook with Workbooks.Open() or the workbook where the code is, if all the workbooks are opened already.
Thus, make sure that you close them before trying to open the workbooks. You need to do the following two actions before opening:
Detect whether Excel workbook is already open
If there is an opened workbook, then close it:
Closing Excel Application using VBA
Another faster option is to assign the variable explicitly, as mentioned by Vincent G, which is faster, because you are not going to close an already opened Excel file:
Sub TestMe()
Dim x As Workbook
Dim y As Workbook
Dim xPath As String: xPath = "C:\Book1.xlsx"
Dim yPath As String: yPath = "C:\Book2.xlsx"
Workbooks.Open xPath
Set x = Workbooks(Split(xPath, "\")(UBound(Split(xPath, "\"))))
Workbooks.Open yPath
Set y = Workbooks(Split(yPath, "\")(UBound(Split(yPath, "\"))))
Debug.Print x.Name
Debug.Print y.Name
End Sub
The part of the code Split(xPath, "\")(UBound(Split(xPath, "\"))) takes the last element of the splitted array by \.

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

Open workbook if not already open, if already, then get that reference

Ive a scenario to do some changes in a workbook in another workbook path. But the question is I need to check whether the workbook already open or not. If not I need to get that opened instance to a workbook variable.
Here is the code Im using for checking whether workbook open or not and then the code for opening
Function IsFileOpen(fileFullName As String)
Dim FileNumber As Integer
Dim errorNum As Integer
On Error Resume Next
FileNumber = FreeFile() ' Assign a free file number.
' Attempt to open the file and lock it.
Open fileFullName For Input Lock Read As #FileNumber
Close FileNumber ' Close the file.
errorNum = Err ' Assign the Error Number which occured
On Error GoTo 0 ' Turn error checking on.
' Now Check and see which error occurred and based
' on that you can decide whether file is already
' open
Select Case errorNum
' No error occurred so ErroNum is Zero (0)
' File is NOT already open by another user.
Case 0
IsFileOpen = False
' Error number for "Permission Denied." is 70
' File is already opened by another user.
Case 70
IsFileOpen = True
' For any other Error occurred
Case Else
Error errorNum
End Select
End Function
Public Function getConsolidatedDataFile() As Workbook
Dim p As String
p = ActiveWorkbook.Path
Dim cf As String
cf = printf("{0}\ConsolidatedData.xlsx", p)
Dim wb As Workbook
Dim fo As Boolean
fo = IsFileOpen(cf)
If fo = False Then wb = Workbooks.Open(filename:=cf)
''I need to get the code for this place of fo is true
getConsolidatedDataFile wb
End Function
So if file open I need to get that workbook in to that wb variable.
Ive got a solution
If fo = False Then
Set wb = Workbooks.Open(filename:=cf)
Else
Dim w As Workbook
For Each w In Workbooks
If w.FullName = cf Then
Set wb = w
End If
Next
End If
Here is in the loop its traversing through all workbook and if its there take that reference..
I hope this help
Dim dict As Dictionary
Function OpenFile(fileFullName As String) As Workbook
If (dict.Exists(fileFullName)) Then
OpenFile = dict.Item(fileFullName)
End If
dict.Add "fileFullName", Workbooks.Open(Filename:=fileFullName)
OpenFile = dict.Item(fileFullName)
End Function
Application.ActiveWorkbook = OpenFile(fileFullName)
To reference a workbook to the workbook collection it should be opened -
The Workbook object is a member of the Workbooks collection. The Workbooks collection contains all the Workbook objects currently open in Microsoft Excel.
MSDN Wrokbook Object
Thus, if your workbook is in the same Excel instance, then try like this:
Public Sub TestMe()
Dim wb As Workbook
Set wb = Workbooks("12.xlsx")
End Sub
If it is not in the same instance, then GetObject should work:
Public Sub TestMe()
Dim wb As Workbook
Set wb = GetObject("C:\path\12.xlsx")
Debug.Print wb.Worksheets(1).Name
End Sub
GetObject MSDN
This is how 3 workbooks in the same instance look like:
This is how 2 workbooks look like in 2 different instances:
Pros and Cons for using multiple instances (Source answers.microsoft.com):
Pros
If you have 32-bit Excel, each instance can use up to 3 GB memory. If you have a powerful computer, very heavy files, and 32-bit Excel, each instance of Excel can use 3 GB. So with e.g. 2 instances of Excel.exe, you could say that the total memory Excel could use triples. (Please note that this is not needed with 64-bit Excel as it is not limited by 3 GB memory per instance)
If you want to have a separate Undo chain, so that each Undo only undos in the currently active workbook, then separate instances will indeed achieve this.
Cons
If you want to have a common Undo chain shared by all open files, then using multiple instances will not achieve this.
If you want to be able to e.g. press Ctrl+F6 to jump between your open files quickly, then using multiple instances will not achieve this.
Paste Special will not work between instances. See this for more info.
Making workbook links between 2 files in separate running instances cannot be made by clicking, and will not update in real-time.
The code looks ok, simply use the Set keyword:
If fo = False Then set wb = Workbooks.Open(filename:=cf)
Here is a quick function that will open the workbook if it's not already open:
Function GetWorkBook(ByVal sFullName As String, Optional ReadOnly As Boolean) As Workbook
Dim sFile As String: sFile = Dir(sFullName)
On Error Resume Next
Set GetWorkBook = Workbooks(sFile)
If GetWorkBook Is Nothing Then Set GetWorkBook = Workbooks.Open(sFullName, ReadOnly:=ReadOnly)
On Error GoTo 0
End Function

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