文档中链接对象的文件路径的VBA列表

时间:2013-12-30 11:25:52

标签: excel vba excel-vba ms-word word-vba

我有许多大型 Microsoft Word 文档,其中包含来自许多 Microsoft Excel 电子表格的许多链接文件。打开Word文档时,即使未选中“打开时更新链接文件”选项:

enter image description here

通过打开和关闭每个链接的相关Excel电子表格,

Word仍然检查源链接的每个链接(因此,对于x个链接,即使来自同一个电子表格,Word也会打开和关闭电子表格x次)。这意味着打开文档需要很长时间。

我发现如果包含链接对象源的电子表格已经打开,文档打开得更快,因此Word不会保持打开,关闭,重新打开它们。

到目前为止,我所拥有的解决方案的开头是创建链接对象的所有文件路径的列表,通过以下 VBA 代码完成:

Sub TypeArray()

Dim List(), Path As String
Dim i, x As Integer
Dim s As InlineShape
Dim fso As FileSystemObject, ts As TextStream

Set fso = New FileSystemObject
Set ts = fso.OpenTextFile("C:\MyFolder\List.txt", 8, True)

With ts
    .WriteLine (ActiveDocument.InlineShapes.Count)
End With

For Each s In ActiveDocument.InlineShapes
        Path = s.LinkFormat.SourcePath & "\" _
        & s.LinkFormat.SourceName
        With ts
            .WriteLine (Path)
        End With
Next s
End Sub

'--------------------------------------------------------------------------------------
Private Sub WriteStringToFile(pFileName As String, pString As String)

    Dim intFileNum As Integer

    intFileNum = FreeFile

    Open pFileName For Append As intFileNum
    Print #intFileNum, pString
    Close intFileNum

End Sub
'--------------------------------------------------------------------------------------
Private Sub SendFileToNotePad(pFileName As String)

    Dim lngReturn As Long

    lngReturn = Shell("NOTEPAD.EXE " & pFileName, vbNormalFocus)

End Sub

效果很好,但只能在 文档已经打开后使用,这会破坏其目的。

所以,最后,我的问题是这些:

1)是否有办法运行此代码(或任何更好,更有效的代码 - 欢迎提出建议)之前打开Word文档并等待检查其中每个链接的漫长过程源?

2)有没有办法避免这一切,只需要Word 在我打开文档时检查链接?

很抱歉这个问题很长,谢谢你的帮助!

4 个答案:

答案 0 :(得分:1)

如果我没有错,根据msdn应该有Document_Open事件。这实际上应该是一个打开文档之前,应该在更新链接之前触发(至少在excel中它会在计算之前被触发)。

尝试打开文档打开文件。然后你将面临另一个问题,所以何时关闭文件,但这是一件容易的事情。 (可能是document_close事件......)

EDITTED:

正如评论所说,现在为时已晚。您可以创建一个单词应用程序(作为单个应用程序或作为插件)。逻辑基本上是:

'1) on something_open run GetOpenFileName dialog
'2) before opening the real thing, open all files accompanied
'3) open the document itself
'4) close all files
'5) close the opener itself

这不是最简单的方法,但是我使用这个逻辑来确保我的应用程序总是以excel等的新副本运行。但我知道这是一种解决方法而不是解决方案。

答案 1 :(得分:1)

如果您仍在寻找这方面的内容,我在VBA和VB.NET(在VS 2010中)的组合中创建了以下内容,以显示使用该系统可以轻松完成的任务。如果VB.NET对你没用,抱歉,但有理由说我不想花时间在纯VBA方法上。

目前,它是一个“控制台”应用程序,这意味着你可能会看到一个盒子在运行时闪烁,但也意味着你更有可能在没有VS的情况下创建这个应用程序,如果你绝对不得不(AFAICR VB.NET /编译器/实际上是免费的)。它只是获取链接信息。 (即目前没有修改链接的工具)。

概述是您有一小块VBA(例如,在您的Normal模板中)并且您需要一个打开的文档。 VBA启动Windows Shell,运行VB.NET程序并将其传递给您要打开的文档的完整路径名。

VB.NET程序打开.docx(或其他)并查看从主文档部分引用的所有类型“oleObject”的关系(所以现在,代码忽略页眉,页脚,脚注,尾注以及其他任何你可能有链接的地方)

VB.NET程序自动化Word(我们知道它正在运行)并将每个链接URL写入活动文档中的一系列文档变量。这些变量称为“Link1”,“Link2”等。如果没有链接(我实际上没有正确测试该路径)或程序找不到该文件,“Link0”应设置为“0” 。否则应将其设置为链接计数。

shell同步执行,因此VBA在完成后会恢复。然后你要么有0个链接,要么你可以处理一组链接。

VBA是这样的:

Sub getLinkInfo()
' the full path name of the program, quoted if there are any spaces in it
' You would need to modify this
Const theProgram As String = """C:\VBNET\getmaindocumentolelinks.exe"""
' You will need a VBA reference to the "Windows Script Host Object Model"
Dim oShell As WshShell

Set oShell = CreateObject("WScript.Shell")
' plug your document name in here (again, notice the double quotes)
If oShell.Run(theProgram & " ""c:\a\testdocexplorer.docx""", , True) = 0 Then
  With ActiveDocument.Variables
    For i = 1 To CInt(.Item("Link0").Value)
      Debug.Print .Item("Link" & CStr(i))
    Next
  End With
Else
  MsgBox "Attempt to retrieve links failed"
End If
End Sub

对于VB.NET,您需要Office Open XML SDK(我认为它是2.5版本)。您需要引用它,以及Microsoft.Office.Interop.Word。

代码如下:

Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.IO
Imports System.Xml
Imports System.Xml.Linq
Imports DocumentFormat.OpenXml.Packaging
Imports Word = Microsoft.Office.Interop.Word


Module Module1
  Const OLEOBJECT As String = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject"

  Sub Main()
    Dim s() As String = System.Environment.GetCommandLineArgs()
    If UBound(s) > 0 Then
      Dim wordApp As Word.Application
      Try
        wordApp = GetObject(, "Word.Application")
        Dim targetDoc As Word.Document = wordApp.ActiveDocument
        Try
          Dim OOXMLDoc As WordprocessingDocument = WordprocessingDocument.Open(path:=s(1), isEditable:=False)
          Dim linkUris As IEnumerable(Of System.Uri) = From rel In OOXMLDoc.MainDocumentPart.ExternalRelationships _
                                                       Where rel.RelationshipType = OLEOBJECT _
                                                       Select rel.Uri
          For link As Integer = 0 To linkUris.Count - 1
            targetDoc.Variables("Link" & CStr(link + 1)).Value = linkUris(link).ToString
          Next
          targetDoc.Variables("Link0").Value = CStr(linkUris.Count)
          OOXMLDoc.Close()
        Catch ex As Exception
          targetDoc.Variables("Link0").Value = "0"
        End Try
      Finally
        wordApp = Nothing
      End Try
    End If
  End Sub

End Module

我最初将.NET代码编写为COM对象,这在VBA中使用起来会稍微容易些,但在.NET端设置起来要困难得多,而且(坦率地说)更难以修改。调试,因为你不断关闭Word以释放对COM DLL的引用。

如果您确实想要修复LINK路径,据我所知,在关系记录中修改它们就足以让Word在打开Word时更新相关的LINK字段,从而节省了修改XML的时间LINK字段的代码也是如此。但那是另一个故事......

答案 2 :(得分:0)

我刚刚发现您可以设置/修改DelayOleSrvParseDisplayName注册表项和NoActivateOleLinkObjAtOpen注册表项来修改全局行为:

请参阅http://support.microsoft.com/kb/970154

答案 3 :(得分:0)

我还发现 activedocument.fields 可以包含指向外部对象的链接(在我的例子中是Excel表格)。

使用此代码解析它们:

for each f in activedocument.fields
  debug.print f.code
next

使用 activedocument.fields(FIELDNUMBER)来选择每个对象,以确定它在文档中的位置。

也许 activedocument.Variables activedocument.Hyperlinks 可以包含指向外部对象的链接? (不是我的情况)。