如何在 VBA 中使用唯一标识符引用 Excel Link

How to Refer to Excel Link Using Unique Identifier in VBA

如何使用文件打开时不会改变的唯一标识符来引用 VBA 中的外部工作簿?当包含完整文件路径并且没有打开同名文件时,它工作正常。但是,当打开文件时,带有文件路径的完整表单不起作用,单独的文件名也不起作用。

我想创建一个更新 Sub 来更新所有引用,但如果电子表格打开(请参阅下面的第 2 点),这会把自己弄得一团糟。

这些是我认为应该可行的一些原因:

  1. 好像在手册link更新菜单中只有文件名可以参考;
  2. 也无法打开两个同名的工作簿,因此如果您打开一个源文件 link,那么单元格引用将从文件路径更改为文件名(这就是这件事这导致了一个问题。

这是我目前拥有的代码 updCellRef 是对文件路径的单元格引用(我只想在其中使用文件名):

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

为了澄清这个问题是在我使用上述函数更新值时出现的,但是当源电子表格打开时它仅通过其文件名引用。当它关闭时,它由其完整文件路径引用。

我正在使用 Excel Professional 2010 v14 和 VBA v7.0

注意:我不想使用任何其他软件,包括 Power Query,因为没有管理员权限无法安装它。

我并不是说这是唯一的方法,但我能想到的最简单的方法是使用如下方式实际打开工作簿:

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

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

我理解您的意思,如果电子表格与您打开的电子表格同名,它就会呕吐。也许一个简单的 hack 是捕获活动工作簿的文件名,将其保存为临时文件,然后在最后保存回来。我确实说过这是黑客攻击。

我知道您可以通过 C# 或 MS Access 使用 ADO 像数据库一样访问电子表格数据,所以我猜也可以直接从 Excel 执行此操作。就是说,与上面的建议相比,它似乎更像是黑客。我认为即使是处理单个单元格,ADO 也必须读取整个电子表格,所以我不认为这会为您节省任何东西。

有两种方法可以将信息添加到文件名以使其唯一,一种是在 Excel 中打开文件,发现没有打开的文件共享相同的名称,要么包括整个路径。所以你不能 "refer to external workbooks in VBA using just the filename" 除非它们是打开的,因为这样你就无法确定共享相同名称的所有文件中的哪些。

这是 MS Office 支持的消息来源说 "When the source is not open, the external reference includes the entire path"

更新:鉴于对原始问题的评论,我想我们在这里:

  1. 我们对打开的文件以及任何 link 感到满意,因为它们是打开的,所以应该已经更新了
  2. 我们有一个文件列表,如果我们可以通过给定路径找到它们并且如果没有另一个具有相同文件名的文件打开

现在试试这个:

 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

我可以想到您可能遇到的两种情况:


1.根据标题,我可以猜到问题在于,您尝试引用的工作簿位于 parent 工作簿的子文件夹中;如果是这样,我注意到即使您提供了完整路径,它也会工作一段时间,然后它会错过引导它的路径-这似乎是一个错误(尽管我不知道是什么触发了它)-。链接仅适用于 excel 界面,但是,当您在 vba 中尝试使用 hyperlink 时,它会出错,因为完整路径已被切断,这会导致路径不完整-因此要验证它,它说不再有效-。我没有其他解决方案,当发生这种情况时,再次询问用户路径(对依赖于此的所有进程使用主单元格以使其更容易 fix/workaround)。这可能会解决它,以便通过 VBA 检索它。只需确保单元格值在询问时具有工作簿的全名-

    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。如果 (1) 不是这种情况,这应该通过检索给定 WB 的完整路径来解决它(这只是要更新 link,它是否已经打开无关紧要)

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

你可以试试下面的方法

  1. 测试 link 是否来自打开的工作簿
  2. 如果使用了,则使用ChangeLink欺骗Excel进行更新
  3. 如果没有,运行 现有代码适用于已关闭的书。

代码

 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

这是引用链接的另一种方式。

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

不过link和之前一样是文件路径的字符串

更新

您能否post 数据 > 编辑链接的屏幕截图以使其更加清晰?

在我的测试中,前 3 个链接都很好,但最后一个有问题。