【问题标题】:Excel VBA - Programmatically InsertLine of code to a specific sub macro in a worksheet's CodeModuleExcel VBA - 以编程方式将代码行插入工作表的 CodeModule 中的特定子宏
【发布时间】:2016-08-15 19:29:06
【问题描述】:

这是一个(据我所知)CPearson's resource site 没有涉及的主题(对于那些希望在 Excel 中使用宏以编程方式将代码插入新工作表/工作簿或现有工作表的人来说,这是一个非常好的资源工作表/工作簿)。

我目前有 Sheet1,其中包含一系列四到五个单独的子宏,它们执行当前不相互影响的功能。我编写了一个宏,当用户按下按钮激活它时,它会创建一个新的工作表并将几行代码插入到该工作表上(此任务已经完成并正常运行)。

我的目标是在此过程中添加一行,以便在执行上述操作后,将“代码行 xyz #1”添加到此 CodeModule 中的特定过程中,即:“Sub MacroMain() ”。

我目前能够(几乎)使用以下方法实现此结果,其中“AddCode”是一个文本字符串:

ActiveWorkbook.VBProject.VBComponents(Sheets("Sheet1").CodeName).CodeModule.AddFromString AddCode

然而,这个解决方案的问题是它只将新的代码行添加到 Sheet1 的 CodeModule 的最上面部分,而不是特定的子宏 Sub MacroMain。不幸的是,尝试在上述行中引用 SubMacroMain 会产生“预期函数或变量”错误。我认为这是我的用户格式错误。

我想知道是否应该使用上面列出的代码行来遵循某种语法,以便将插入此过程中的代码放入 Sheet1 的 CodeModule 中的特定宏中。

谢谢。

【问题讨论】:

    标签: excel vba


    【解决方案1】:

    您有几个选择。如果您知道确切的行号,则可以使用 InsertLines 在特定行号处添加一行。例如:

    Sub AddLineToModule(LineNum as Long, StrLineText as String)
        Dim VBProj As VBIDE.VBProject
        Dim VBComp As VBIDE.VBComponent
        Dim CodeMod As VBIDE.CodeModule
        Const DQUOTE = """" ' one " character
    
        Set VBProj = ActiveWorkbook.VBProject
        Set VBComp = VBProj.VBComponents("Module1")
        Set CodeMod = VBComp.CodeModule
    
        CodeMod.InsertLines LineNum, StrLineText
    
        Set VBProj = Nothing
        Set VBComp = Nothing
        Set CodeMod = Nothing
    End Sub
    

    原始解决方案斜体:如果您不知道确切的行号,您可以将整个模块读取到一个字符串数组,然后循环遍历它并在您想要的行之后添加一行。我手头没有示例,但如果您需要,可以稍后添加。 编辑:感谢Mikegrann,一个可能更好的解决方案是使用Module.Find;但是,下面的 cmets 对于这是否是一个可取的解决方案存在一些争论(请参阅来自Mat's Mug 的更新)。根据 OP 的具体问题,这可能是一种可行的方法。 编辑 2:对于每个 cmets,Module.Find 不应在大多数(如果不是全部)场景中使用。使用 VBIDE API 或自定义数组搜索功能可靠地找到一行代码。

    最后,如果您不知道模块中的确切行号,但知道相对于特定过程开始的确切行号,您可以使用我上面的第一个示例和ProcStarLine 的组合。

    Sub AddLineToProcedure(StrProcName as String, LineNum as Long, StrLineText as String)
        Dim VBProj As VBIDE.VBProject
        Dim VBComp As VBIDE.VBComponent
        Dim CodeMod As VBIDE.CodeModule
        Const DQUOTE = """" ' one " character
    
        Set VBProj = ActiveWorkbook.VBProject
        Set VBComp = VBProj.VBComponents("Module1")
        Set CodeMod = VBComp.CodeModule
    
        With CodeMod
            LineNum = LineNum + .ProcStartLine(ProcName, vbext_pk_Proc)
            .InsertLines LineNum, StrLineText
        End With
    
        Set VBProj = Nothing
        Set VBComp = Nothing
        Set CodeMod = Nothing
    End Sub
    
    
        ProcName = "DeleteThisProc"
        With CodeMod
            StartLine = .ProcStartLine(ProcName, vbext_pk_Proc)
            NumLines = .ProcCountLines(ProcName, vbext_pk_Proc)
            .DeleteLines StartLine:=StartLine, Count:=NumLines
        End With
    

    【讨论】:

    • 在情况 2 中,与其自己编写所有内容来构建强大的搜索工具来查找所需的行,不如使用Module.Find? (msdn.microsoft.com/en-us/library/office/ff195471.aspx)
    • 哦,这是一个更好的解决方案!我对使用 VBProject 对象不是很熟悉,很惊讶我找不到“查找”功能或类似的东西。我会更新我的答案 - 谢谢!!
    • @Mikegrann 因为Module.Find 处理纯文本 - 盲字符串搜索是完全危险的。使用 VBIDE API 检索特定过程开始的行是定位过程的唯一安全方法,而不是使用正式语法实际解析代码。 Module.Find 无法从过程或关键字中区分变量:它最终中断,即使设置了“仅全字”标志。我知道,我在过去两年的大部分时间里都在以编程方式解析 VBA 代码。
    • 基本上,中断文本搜索所需要的只是模块顶部的一个很小的无害注释'Sub MacroMain will be modified by external code, do not rename!,然后你就可以了,你想要的实际搜索结果现在是第二个结果,你没有程序化的方式知道。如果您使用 API“手动”搜索,则该评论不会破坏任何内容。
    • 然而,通过将模块文本读取到数组中,您可以验证该行是否与您期望的完全匹配。我会相应地更新选项 2 - 我今天学到了一些新东西,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多