4

How do I refer to external workbooks in VBA using a unique identifier which doesn't change when the file is open? It works fine when the full filepath is included and no file with the same name is open. However when a file is open the complete form with the filepath does not work and the filename alone does not work.

I wanted to create an update Sub to update all the references and this mucks itself up if the spreadsheet is open (refer to point 2 below).

These are some reasons why I feel it should be possible:

  1. It seems that in the manual link update menu there is only the filename to refer to;
  2. Also one can't open two workbooks with the same name, and thus if you open a source link then the cell references change from a file path to a file name (and it is this very thing which is causing an issue.

This is the code I currently has the updCellRef is a cell reference to the file-path (where I just want to use the file-name):

    Sub updateValues(updCellRef)
        updFilePath = ActiveWorkbook.Sheets("INPUTS").Range(updCellRef).Value
        ActiveWorkbook.updateLink Name:=updFilePath, Type:=xlExcelLinks
    End Sub

To clarify this problem arose when I was using the above function to update values however when the source spreadsheet was open it is referenced by its file name alone. When it is closed it is referenced by its full file-path.

I'm using Excel Professional 2010 v14 with VBA v7.0

Note: I don't want to use any other software including Power Query as it can't be installed without admin rights.

AER
  • 1,549
  • 19
  • 37
  • If you refer to a file using just the filename, I guess you have pre-assumed a location or other qualifier to choose among all the possible files with that name accessible from your application. Is it all open files in excel, files in the current directory, or what? – Mats Lind Aug 15 '16 at 09:35
  • 1
    as alternative, you can query data from the same or different workbook with Power Query or Insert > From Access https://stackoverflow.com/questions/38694891/excel-pivot-depending-upon-header-values/38699161#38699161 – Slai Aug 16 '16 at 01:38
  • Is there anyway I can make this question clearer? Please offer suggestions. I suspect I've approached it convolutedly. – AER Aug 16 '16 at 03:33
  • @Slai is there a way to put this in a solution which SQL references a range of cells if you know the range and sheet name of said sheet? – AER Aug 16 '16 at 03:35
  • 1
    Yes, `"SELECT * FROM [Sheet1$B2:C3]"` or `"SELECT * FROM [named Range]"`. The file name will not be changed by DDE/OLE link, and can be changed with VBA. – Slai Aug 16 '16 at 09:25
  • 1
    @AER The open files don't need updating, updating is automatic from open files. – Mats Lind Aug 16 '16 at 18:38
  • 2
    @AER you have a function that you expect to do other things than return a result, I thought Excel VBA functions had no side-effects, did precisely no other thing, than returning a result? – Mats Lind Aug 16 '16 at 18:46
  • @MatsLind You are right. I've changed it to a sub. I didn't have the sub syntax right when I called it so I used a function instead which is a bad way of doing it. However I still need to update the function without creating a complicated nest of if open statements. Cheers! – AER Aug 17 '16 at 00:11
  • @AER OK, I gave it one more try in an edit to my original answer below, did I get you right this time? – Mats Lind Aug 17 '16 at 10:26

5 Answers5

2

This is an alternate way of referencing links.

Dim linkName As String, fileName As String, i As Integer

For Each link In ActiveWorkbook.LinkSources
    On Error GoTo tryName
    ActiveWorkbook.UpdateLink linkName

    If False Then
  tryName:
        i = InStrRev(linkName, "\") ' 0 if no "\" found
        If i > 0 Then
            On Error Resume Next ' to ignore error if fileName does not work too
            fileName = Mid(linkName, i + 1)
            ActiveWorkbook.UpdateLink fileName 
        End If
    End If
    On Error GoTo 0 ' reset the error handling        
Next

However link as before is a string of the filepath

Update

Can you post a screenshot of the Data > Edit Links to make it a bit more clear?

In my tests the first 3 links were fine, but the last one had problems.

Slai
  • 22,144
  • 5
  • 45
  • 53
  • I think this may be exactly what I'm looking for. Referring to files as Excel does internally. I'll try it out. Thank you so much. I won't be able to for 2hrs though as my boss is using the sheet :| – AER Aug 16 '16 at 23:18
  • This would work if you wanted to update all links, from your question I thought you were targeting a specific link? – brettdj Aug 17 '16 at 01:48
  • @Slai Same issue. `link` is effectively a string of the filepath unfortunately – AER Aug 17 '16 at 05:23
  • @AER are any of your links array formulas like `{=Excel.Sheet.12|\\path\Book2.xlsx!'!Sheet1!R1C1:R2C2'}` ? – Slai Aug 17 '16 at 13:33
  • @Slai I can't really do that unless I blur part of the names because of confidentiality – AER Aug 18 '16 at 03:40
  • All file names are different if that is relevant – AER Aug 18 '16 at 04:06
  • My conclusion is that VBA is clunky and if you are to create a language for coding macros it should at least follow the structure of what the program is doing. This is why I avoid VBA like the plague. – AER Aug 18 '16 at 07:47
  • OT: @AER You are not watching the real potential of it, yeah, it has its bugs but, nothing is perfect -I've had more random errors at eclipse and android studio which some of them solve by rebooting several times and the error messages are not that friendly either- Excel interface may be buggy as well. There is no perfect software, just the one that we are available to make it work for our needs. – Sgdva Aug 18 '16 at 14:56
  • @AER I updated my example to try the filename if the path fails (assuming it gives error when it fails). If that does not work either, then another alternative is to open the file in a new separate Excel instance so that the file path links are not turned into file names with DDE (Dynamic Data Exchange) – Slai Aug 21 '16 at 15:32
1

There are two ways to add info to the filename to make it unique is either to open the file in Excel where it is seen to that no open files are sharing the same name, or to include the entire path. So you can not "refer to external workbooks in VBA using just the filename" unless they are open since there would then be uncertainty to which of all files sharing the same names you are refering to.

Here is the source at MS Office Support saying that "When the source is not open, the external reference includes the entire path"

Update: given the comments to the original question, I guess we are here:

  1. We are happy with the open files and any link to them which should be already updated since they are open
  2. We have a list of files which we like to force update to if we can find them through given paths and if there is not another file with the same filename open

Now try this:

 Sub updateValues(updFilepath As String)
    If Not FileInUse(updFilepath) Then
        ActiveWorkbook.UpdateLink Name:=updFilepath, Type:=xlExcelLinks
    'else workbook is open and Excel have automatically updated linke
    End If
End Sub

Public Function FileInUse(sFileName As String) As Boolean
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

The file test function is courtesey of user2267971 answering this question also on how to test if a file is open

Community
  • 1
  • 1
Mats Lind
  • 914
  • 7
  • 19
  • Well yes, precisely. I'll set it up to refer to it by filepath when it is closed and if it is open I wish to refer to it by filename alone. – AER Aug 16 '16 at 00:08
  • Is seems to be the case that you know for sure which files to look in? If so, start with a new clean run of Excel and see to that the workbook with the macro do not have the same filename as the files you will look in. Only have one (1) other file open at a time. – Mats Lind Aug 16 '16 at 12:53
  • Did you try Slai's suggestion, it looks the cleanest. – Mats Lind Aug 16 '16 at 18:55
  • I like what you've said. It turns out it is right. What I need is a sub to check if it is open then don't update using the link reference it is. Else then update using the filepath. Can you put this in an answer for me. As there is no answer which should get a bounty for answering the question. – AER Aug 17 '16 at 06:00
  • Any thoughts if I want to change the source of said spreadsheet while it is open? – AER Aug 18 '16 at 04:06
  • Ok, so you have links to an open workbook. You want to keep that book open while changing the links to a book on file with the same name, and then you want to refresh those links? (I think we need to distinguish "update" meaning refresh values from the sources, from "change" meaning point the links to new sources) – Mats Lind Aug 18 '16 at 06:26
  • I'm doing both, I'll start a new question. I just want a unique identifier with which to identify a linked to sheet to perform operations on it. – AER Aug 18 '16 at 06:39
  • For the life of me I cannot get this code to work I'm afraid. `updFilePath` is just a string of the filepath and the filename yes? – AER Aug 18 '16 at 07:54
1

I can think about 2 scenarios that you may have here:


1. By the title I can guess the problem relies in the fact that, the workbooks that you are trying to refer to are within a sub folder in the parent workbook; if so, I have noticed that even when you give the full path, it works for a time and then it miss leads the path for it -it seems this is a bug (I don't know what triggers it though)-. Links only works in excel interface but, when you are tying to play with the hyperlink in vba it gives error because the full path has been cut off and this leads to an incomplete path -hence to verify it, it says is not longer valid-. I have no other solution that, when this happens, ask the user again for the path (use a master cell for all the process that rely on this to make it easier to fix/workaround). This may solve it in order to retrieve it by VBA. Just make sure the cell value has the full name for the workbook when asking for it-

    Sub Test()
    Dim HLToTest As String
        HLToTest = RetriveWBLink(Range("B2").Value)
    End Sub
    Function RetriveWBLink(WBName As String) As String
    Dim FileSystemLibrary As Object: Set FileSystemLibrary = CreateObject("Scripting.FileSystemObject")
        On Error GoTo Err01RetriveWBLink
        RetriveWBLink = FileSystemLibrary.GetFile(ThisWorkbook.Path & "\" & WBName)
        If 1 = 2 Then '99. If error
Err01RetriveWBLink:
        'this may happen for new workbooks that aren't saved yet
        RetriveWBLink = "False"
        End If '99. If error
        On Error GoTo -1
        Set FileSystemLibrary = Nothing
    End Function


2. If (1) is not the case, this should solve it by retrieving the full path of the given WB (this is just going to update the link, doesn't matter if it's already open or not)

Sub Test()
Dim HLToTest As String
    HLToTest = RetriveWBLink(ThisWorkbook)
End Sub
Function RetriveWBLink(WBName As Workbook) As String
Dim FileSystemLibrary As New Scripting.FileSystemObject
    On Error GoTo Err01RetriveWBLink
    RetriveWBLink = FileSystemLibrary.GetFile(WBName.Path & "\" & WBName.Name)
    If 1 = 2 Then '99. If error
Err01RetriveWBLink:
    'this may happen for new workbooks that aren't saved yet
    RetriveWBLink = "False"
    End If '99. If error
    On Error GoTo -1
    Set FileSystemLibrary = Nothing
End Function
Sgdva
  • 2,800
  • 3
  • 17
  • 28
  • Thanks, 1 is what I was asking. I'm also wondering why Excel changes the way it is referenced, and secondly how to use this other filepath only way that Excel must use to refer to the spreadsheet when it is open. – AER Aug 16 '16 at 00:12
  • 2 is not the problem though. – AER Aug 16 '16 at 00:13
  • Probably with second you can fix the first scenario -if the cell value is the wb name and instead of asking a workbook in the function, it asks for a string, check my updated response. – Sgdva Aug 16 '16 at 14:02
1

You could try something like below

  1. Test if the link is from an open workbook
  2. If it used, then use ChangeLink to fool Excel into making the update
  3. If not, run the existing code that works on closed book.

code

 Sub updateValues()
 Dim updFilePath As String
 Dim Wb As Workbook
 Dim bFound As Boolean

 updFilePath = ActiveWorkbook.Sheets("INPUTS").Range(updCellRef).Value

 For Each Wb In Application.Workbooks
 If Wb.FullName = updFilePath Then
    ActiveWorkbook.ChangeLink Wb.Name, Wb.Name
    bfound = True
    Exit For
 End If
 Next

 If Not bfound Then ActiveWorkbook.UpdateLink Name:=updFilePath, Type:=xlExcelLinks
End Sub
brettdj
  • 54,857
  • 16
  • 114
  • 177
  • 1
    Excel doesn't need to be tricked into updating values from open files, that's automatic. – Mats Lind Aug 16 '16 at 18:54
  • I presume OP is running in Manual calculation mode, hence the request for code – brettdj Aug 16 '16 at 23:59
  • @brettdj It's more I need to update a set of spreadsheets. It may turn out that I need to create a large nest of if statements. – AER Aug 18 '16 at 03:42
  • 1
    No probs. Will leave my code here in case its applicable for another party (though probably unlikely). – brettdj Aug 18 '16 at 04:49
-1

I'm not saying this is the only way, but the easiest way I can think of is to actually open the workbook using something like this:

Dim wb as Workbook
Set wb = Excel.Workbooks.Open(Filename)

updFilePath = wb.Sheets("INPUTS").Range(updCellRef).Value
wb.Close

I understand your point, that if the spreadsheet is the same name as your open spreadsheet, it will puke. Maybe a simple hack is to capture the filename of the active workbook, save it as a temp file, and then save it back at the very end. I did say it was a hack.

I know you can access Spreadsheet data like a database using ADO through C# or MS Access, so I'm guessing it's also possible to do this directly from Excel. That said, it hardly seems like less of a hack than the suggestion above. I think ADO also has to read the entire spreadsheet even to process a single cell, so I don't think this saves you anything anyway.

Hambone
  • 15,600
  • 8
  • 46
  • 69
  • To clarify my spreadsheet being updated is not of the same name. I want to refer to the file in the update function with something that is independent of the filepath (or at least independent of the reference changing when you open that spreadsheet). – AER Aug 12 '16 at 04:22
  • It's not really the answer unfortunately. I've clarified my answer to help with this I hope. – AER Aug 12 '16 at 04:25
  • 1
    I read your update... yes, you are quite right. I misunderstood your question. I'll play around and see if I can repeat your issue on my end – Hambone Aug 12 '16 at 11:47