VBA快速提取引用工程的代码

时间:2022-07-24
本文章向大家介绍VBA快速提取引用工程的代码,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

利用VBAProject来共用VBA代码里介绍了使用VBAProject管理代码的方法,但是有一个不方便的地方,如果想把一个做好的功能(引用了一些其他工程代码)发送给其他人使用,就需要把所引用的工程代码复制到一起,再发给其他人,这样手动处理有些麻烦。

VBA操作VBA——VBA工程对象中介绍过,VBA是可以去操作VBA工程对象的,所以,只要能够正确找到某个文件所直接引用以及间接引用的工程,把所引用的工程代码复制就可以。

我在实现这个功能的时候,有一个前提(这个可以看个人习惯):

  • 每个被引用的功能都有个模块MAPI,里面主要是写一些对外公开的函数
  • MTest模块、ThisWorkbook模块以及以Sheet开头的会被忽略

程序主要的逻辑就是递归的查找某个VBProject所引用的工程,将工程对象的FullPath记录到一个字典中,并用bRemove记录是否是直接引用的,只有直接引用的工程在复制完代码后才需要断开引用。

找到所有引用的工程之后,将每个工程的代码复制过来就可以了:

Private Type RefInfo
    r As Reference
    bRemove As Boolean '是否需要断开引用,有的可能是递归间接引用的
End Type

Private Type RefsInfo
    refs(100) As RefInfo
    
    dic As Object
    Count As Long
End Type

Sub GetReferencesModule()
    Dim ref As RefsInfo
    
    Set ref.dic = VBA.CreateObject("Scripting.Dictionary")

    '记录引用的工程
    RGetReferences ActiveWorkbook.VBProject, ref, True
    If ref.Count = 0 Then
        MsgBox "没有引用的工程。"
        Exit Sub
    End If
    
    On Error Resume Next
    ActiveWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule).Name = "MAPI"
    On Error GoTo 0
    
    Dim i As Long
    For i = 0 To ref.Count - 1
        GetAllModules ActiveWorkbook.VBProject, ref.refs(i).r, ActiveWorkbook.VBProject.VBComponents("MAPI")
        
        '断开引用
        If ref.refs(i).bRemove Then ActiveWorkbook.VBProject.References.Remove ref.refs(i).r
    Next
End Sub

'递归查找,引用的工程可能还会引用其他,只记录引用的工程名称
Function RGetReferences(p As VBProject, ref As RefsInfo, bRemove As Boolean) As Long
    Dim r As Reference
    Dim i As Long
    
    For Each r In p.References
        If r.Type = vbext_rk_Project Then
            If Not ref.dic.Exists(r.FullPath) Then
                Set ref.refs(ref.Count).r = r
                ref.refs(ref.Count).bRemove = bRemove
                
                ref.dic(r.FullPath) = ref.Count
                ref.Count = ref.Count + 1
                '递归
                RGetReferences Application.VBE.VBProjects(r.Name), ref, False
            End If
        End If
    Next
    
End Function

'VBP        目标VBProject
'r          引用
Function GetAllModules(VBP As VBProject, r As Reference, MAPI As VBComponent)
    Dim p As VBProject
    Set p = Application.VBE.VBProjects(r.Name)
    
    Dim cadd As VBComponent
    Dim c As VBComponent
    Dim cs As VBComponents
    
    Set cs = p.VBComponents
    Dim str As String
    For Each c In cs
        If c.Name <> "ThisWorkbook" And c.Name <> "MTest" And VBA.Left$(c.Name, 5) <> "Sheet" Then
            '获取组件的代码
            If c.Name = "MAPI" Then
                '声明部分
                str = c.CodeModule.Lines(1 + 1, c.CodeModule.CountOfDeclarationLines) '不需要第一行的Option Explicit
                MAPI.CodeModule.InsertLines 1 + 1, str
                
                '代码部分
                str = c.CodeModule.Lines(c.CodeModule.CountOfDeclarationLines + 1, c.CodeModule.CountOfLines) '不需要第一行的Option Explicit
                MAPI.CodeModule.InsertLines MAPI.CodeModule.CountOfDeclarationLines + 1, str
            Else
                str = c.CodeModule.Lines(1 + 1, c.CodeModule.CountOfLines) '不需要第一行的Option Explicit
                Set cadd = VBP.VBComponents.Add(c.Type)
                cadd.Name = c.Name
                cadd.CodeModule.InsertLines 1 + 1, str
            End If
        End If
    Next
End Function