【问题标题】:How can I automatically populate the VBA Editor with line numbers?如何使用行号自动填充 VBA 编辑器?
【发布时间】:2017-04-21 10:15:48
【问题描述】:

出于调试原因,我想在我的 VBA 代码中包含行号。这将让我知道发生特定错误的位置。

是否有自动功能(例如设置中的选项)?还是我需要自己写宏?

如果我需要编写自己的宏来完成这项任务,我将如何去做这样的事情?

【问题讨论】:

  • 你可以在VBE中输入行号,编译器会自动忽略它们作为行号,这样就不会报错
  • 当然可以,但是我有很多代码,并且想要一个工具,让我可以在我想要的时候执行它。不得不手动管理它的想法不是我的乐趣。
  • MZ-Tools 具有向单个函数、模块或整个项目添加和删除行号的功能。编辑:mztools.com/v8/onlinehelp/add_remove_line_numbers.htm
  • MZ-Tools - 可以完美完成这项工作。理想情况下,我想自己写,但这可能不是对我时间的最佳利用。由于我是这个论坛的新手,如何分配积分等(抱歉问),
  • 您为什么要这样做?行号在 VB 中已经过时了 几十年。如果您的代码结构正确,您将永远不需要它们。请注意,主要的线路报告工具 (Erl) 也存在问题,以至于 MS 停止记录它。

标签: vba basic


【解决方案1】:

任何体面的错误处理程序都会报告的不仅仅是行号。它将报告错误代码、描述和发生的模块。无论 ERL 是否在您的应用程序中重复行号,如果您在报告的其他线索中找不到问题,也许您需要休假。或者,地狱,添加一个增加模块级 alpha 代码的变量,作为行号的附属物,例如“newERL = strProcLevel & ERL”,以将“A12345”作为行号。

【讨论】:

    【解决方案2】:

    你不想要行号。

    不是出于“调试原因”,也不是出于任何原因。不推荐使用行号是有原因的:它们是在程序存在之前的远古时代的遗迹,而GOTO 是到达任何地方的唯一途径。

    Erl 仅返回在引发错误之前最后遇到的行号。如果您正在记录错误,这可能意味着误导性错误日志:

    Sub DoSomething()
    10 On Error GoTo 50
       Debug.Print 42 / 0
    30 Exit Sub
    
    50 Debug.Print "Error raised on line " & Erl 'returns 10
    End Sub
    

    此外,行号只有Integer 分辨率(VBA 模块最多可以有 65,535 行,是分辨率的两倍),如果您不牢记这一点,它将默默地失败并愉快地报告错误的行号:

    Sub DoSomething()
    99997 On Error GoTo 99999
    99998 Debug.Print 42 / 0
    99999
          Debug.Print Erl   'Prints 34462 - and which line is that?
    End Sub
    

    任何严重的 VBA 应用程序都将改用结构化错误处理

    编写小型、专门的程序(即编写遵循现代最佳实践的代码),行号变得毫无意义。

    维护行号是一件令人头疼的事情;它们使代码混乱,使其整体更难阅读(因此也更难调试)。


    也就是说,IIRC MZ-Tools 3 具有这样的功能。

    请记住,当行号是一个东西时,BASIC 看起来像这样:

    10 GOSUB 100
    20 GOSUB 1000
    99 END
    100 REM CLEAR SCREEN
    110 PRINT CHR$(147)
    120 RETURN
    200 REM MODULO
    210 LET MOD% = V%-INT(V%/FB%)*FB%
    220 RETURN
    1000 REM INIT VARIABLES
    1010 LET FIZZ$ = "FIZZ"
    1011 LET BUZZ$ = "BUZZ"
    1020 LET FIZZ% = 3
    1021 LET BUZZ% = 5
    1030 LET MIN% = 1
    1031 LET MAX% = 15
    1100 PRINT FIZZ$ + ":" + STR$(FIZZ%)
    1101 PRINT BUZZ$ + ":" + STR(BUZZ%)
    1102 PRINT FIZZ$ + BUZZ$ + ":" + STR$(FIZZ%*BUZZ%)
    1105 PRINT
    2000 REM ACTUAL FIZZBUZZ LOOP
    2010 FOR X = MIN% TO MAX%
    2015 LET RESULT$ = STR$(X)
    2020 LET FB% = FIZZ%*BUZZ%
    2021 LET V% = X
    2024 GOSUB 200
    2025 IF MOD%=0 THEN LET RESULT$=FIZZ$+BUZZ$ : GOTO 2050
    2030 LET FB% = FIZZ%
    2031 GOSUB 200
    2035 IF MOD%=0 THEN LET RESULT$=FIZZ$ : GOTO 2050
    2040 LET FB% = BUZZ%
    2041 GOSUB 200
    2045 IF MOD%=0 THEN LET RESULT$=BUZZ$ : GOTO 2050
    2050 PRINT RESULT$
    2090 NEXT X
    2099 RETURN
    

    以上是一个有效的 Commodore 64 BASIC 2.0 程序。 VBA 具有极好的向后兼容性。只需稍作修改,即可在 VBA 中运行:

    Sub Main()
    10     GoSub 100
    20     GoSub 1000
    99     End
    100     Rem CLEAR SCREEN
    110     'Debug.Print Chr$(147) 'Chr$(147) was a special character on C64
    120     Return
    200     Rem MODULO
    210     Let Modulo% = V% - Int(V% / FB%) * FB%
    220     Return
    1000     Rem INIT VARIABLES
    1010     Let FIZZ$ = "FIZZ"
    1011     Let BUZZ$ = "BUZZ"
    1020     Let FZZ% = 3
    1021     Let BZZ% = 5
    1030     Let Min% = 1
    1031     Let Max% = 15
    1100     Debug.Print FIZZ$ + ":" + Str$(FZZ%)
    1101     Debug.Print BUZZ$ + ":" + Str(BZZ%)
    1102     Debug.Print FIZZ$ + BUZZ$ + ":" + Str$(FZZ% * BZZ%)
    1105     Debug.Print
    2000     Rem ACTUAL FIZZBUZZ LOOP
    2010     For X = Min% To Max%
    2015     Let RESULT$ = Str$(X)
    2020     Let FB% = FZZ% * BZZ%
    2021     Let V% = X
    2024     GoSub 200
    2025     If Modulo% = 0 Then Let RESULT$ = FIZZ$ + BUZZ$: GoTo 2050
    2030     Let FB% = FZZ%
    2031     GoSub 200
    2035     If Modulo% = 0 Then Let RESULT$ = FIZZ$: GoTo 2050
    2040     Let FB% = BZZ%
    2041     GoSub 200
    2045     If Modulo% = 0 Then Let RESULT$ = BUZZ$: GoTo 2050
    2050     Debug.Print RESULT$
    2090     Next X
    2099     Return
    End Sub
    

    不要编写 1980 年的代码,我们已经过了 40 年。

    【讨论】:

      【解决方案3】:

      Arich 的答案就像一个单独模块的魅力。如果您想为整个工作簿提供(更新的)行号,请应用以下步骤*^:

      做一次:

      1. Module2 中的大代码粘贴到您的工作簿中。
      2. Module3 的代码粘贴到您的工作簿中。
      3. Module4 的代码粘贴到您的工作簿中。
      4. 然后粘贴Global allow_for_line_addition As String这行,这样你就可以自动在每行的上方/第一行添加行号 模块。
      5. 删除每个模块末尾的所有空行(因此在模块的最后一个end subend functionEnd Property 之后没有输)。
      6. 在 VBA 编辑器中,未运行代码且未处于“中断”模式时:单击工具>参考>标记:`Microsoft Visual Basic for Applications Extensibility 5.3"

      每次修改代码时执行:

      1. °运行Module3 的代码以删除工作簿中所有模块的行号。
      2. °运行Module4 的代码,将行号添加到工作簿中的所有模块。

      Module2

          Public Enum vbLineNumbers_LabelTypes
              vbLabelColon    ' 0
              vbLabelTab      ' 1
          End Enum
      
          Public Enum vbLineNumbers_ScopeToAddLineNumbersTo
              vbScopeAllProc  ' 1
              vbScopeThisProc ' 2
          End Enum
                    Sub AddLineNumbers(ByVal wbName As String, _
                                                                ByVal vbCompName As String, _
                                                                ByVal LabelType As vbLineNumbers_LabelTypes, _
                                                                ByVal AddLineNumbersToEmptyLines As Boolean, _
                                                                ByVal AddLineNumbersToEndOfProc As Boolean, _
                                                                ByVal Scope As vbLineNumbers_ScopeToAddLineNumbersTo, _
                                                                Optional ByVal thisProcName As String)
      
          ' USAGE RULES
          ' DO NOT MIX LABEL TYPES FOR LINE NUMBERS! IF ADDING LINE NUMBERS AS COLON TYPE, ANY LINE NUMBERS AS VBTAB TYPE MUST BE REMOVE BEFORE, AND RECIPROCALLY ADDING LINE NUMBERS AS VBTAB TYPE
      
              Dim i As Long
              Dim j As Long
              Dim procName As String
              Dim startOfProcedure As Long
              Dim lengthOfProcedure As Long
              Dim endOfProcedure As Long
              Dim strLine As String
      
              With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
                  .CodePane.Window.Visible = False
      
          If Scope = vbScopeAllProc Then
      
                  For i = 1 To .CountOfLines - 1
      
                      strLine = .Lines(i, 1)
                      procName = .ProcOfLine(i, vbext_pk_Proc) ' Type d'argument ByRef incompatible ~~> Requires VBIDE library as a Reference for the VBA Project
      
                      If procName <> vbNullString Then
                          startOfProcedure = .ProcStartLine(procName, vbext_pk_Proc)
                          bodyOfProcedure = .ProcBodyLine(procName, vbext_pk_Proc)
                          countOfProcedure = .ProcCountLines(procName, vbext_pk_Proc)
      
                          prelinesOfProcedure = bodyOfProcedure - startOfProcedure
                          'postlineOfProcedure = ??? not directly available since endOfProcedure is itself not directly available.
      
                          lengthOfProcedure = countOfProcedure - prelinesOfProcedure ' includes postlinesOfProcedure !
                          'endOfProcedure = ??? not directly available, each line of the proc must be tested until the End statement is reached. See below.
      
                          If endOfProcedure <> 0 And startOfProcedure < endOfProcedure And i > endOfProcedure Then
                              GoTo NextLine
                          End If
      
                          If i = bodyOfProcedure Then inprocbodylines = True
      
                          If bodyOfProcedure < i And i < startOfProcedure + countOfProcedure Then
                              If Not (.Lines(i - 1, 1) Like "* _") Then
      
                                  inprocbodylines = False
      
                                  PreviousIndentAdded = 0
      
                                  If Trim(strLine) = "" And Not AddLineNumbersToEmptyLines Then GoTo NextLine
      
                                  If IsProcEndLine(wbName, vbCompName, i) Then
                                      endOfProcedure = i
                                      If AddLineNumbersToEndOfProc Then
                                          Call IndentProcBodyLinesAsProcEndLine(wbName, vbCompName, LabelType, endOfProcedure)
                                      Else
                                          GoTo NextLine
                                      End If
                                  End If
      
                                  If LabelType = vbLabelColon Then
                                      If HasLabel(strLine, vbLabelColon) Then strLine = RemoveOneLineNumber(.Lines(i, 1), vbLabelColon)
                                      If Not HasLabel(strLine, vbLabelColon) Then
                                          temp_strLine = strLine
                                          .ReplaceLine i, CStr(i) & ":" & strLine
                                          new_strLine = .Lines(i, 1)
                                          If Len(new_strLine) = Len(CStr(i) & ":" & temp_strLine) Then
                                              PreviousIndentAdded = Len(CStr(i) & ":")
                                          Else
                                              PreviousIndentAdded = Len(CStr(i) & ": ")
                                          End If
                                      End If
                                  ElseIf LabelType = vbLabelTab Then
                                      If Not HasLabel(strLine, vbLabelTab) Then strLine = RemoveOneLineNumber(.Lines(i, 1), vbLabelTab)
                                      If Not HasLabel(strLine, vbLabelColon) Then
                                          temp_strLine = strLine
                                          .ReplaceLine i, CStr(i) & vbTab & strLine
                                          PreviousIndentAdded = Len(strLine) - Len(temp_strLine)
                                      End If
                                  End If
      
                              Else
                                  If Not inprocbodylines Then
                                      If LabelType = vbLabelColon Then
                                          .ReplaceLine i, Space(PreviousIndentAdded) & strLine
                                      ElseIf LabelType = vbLabelTab Then
                                          .ReplaceLine i, Space(4) & strLine
                                      End If
                                  Else
                                  End If
                              End If
      
                          End If
      
                      End If
      
          NextLine:
                  Next i
      
          ElseIf AddLineNumbersToEmptyLines And Scope = vbScopeThisProc Then
      
          End If
      
                  .CodePane.Window.Visible = True
              End With
      
          End Sub
                    Function IsProcEndLine(ByVal wbName As String, _
                        ByVal vbCompName As String, _
                        ByVal Line As Long) As Boolean
      
          With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
          If Trim(.Lines(Line, 1)) Like "End Sub*" _
                      Or Trim(.Lines(Line, 1)) Like "End Function*" _
                      Or Trim(.Lines(Line, 1)) Like "End Property*" _
                      Then IsProcEndLine = True
          End With
      
          End Function
                    Sub IndentProcBodyLinesAsProcEndLine(ByVal wbName As String, ByVal vbCompName As String, ByVal LabelType As vbLineNumbers_LabelTypes, ByVal ProcEndLine As Long)
              Dim procName As String
              Dim startOfProcedure As Long
              Dim endOfProcedure As Long
      
              With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
      
                  procName = .ProcOfLine(ProcEndLine, vbext_pk_Proc)
                  bodyOfProcedure = .ProcBodyLine(procName, vbext_pk_Proc)
                  endOfProcedure = ProcEndLine
                  strEnd = .Lines(endOfProcedure, 1)
      
                  j = bodyOfProcedure
                  Do Until Not .Lines(j - 1, 1) Like "* _" And j <> bodyOfProcedure
      
                      strLine = .Lines(j, 1)
      
                      If LabelType = vbLabelColon Then
                          If Mid(strEnd, Len(CStr(endOfProcedure)) + 1 + 1 + 1, 1) = " " Then
                              .ReplaceLine j, Space(Len(CStr(endOfProcedure)) + 1) & strLine
                          Else
                              .ReplaceLine j, Space(Len(CStr(endOfProcedure)) + 2) & strLine
                          End If
                      ElseIf LabelType = vbLabelTab Then
                          If endOfProcedure < 1000 Then
                              .ReplaceLine j, Space(4) & strLine
                          Else
                              Debug.Print "This tool is limited to 999 lines of code to work properly."
                          End If
                      End If
      
                      j = j + 1
                  Loop
      
              End With
          End Sub
                    Sub RemoveLineNumbers(ByVal wbName As String, ByVal vbCompName As String, ByVal LabelType As vbLineNumbers_LabelTypes)
              Dim i As Long
              With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
                  'MsgBox ("nr of lines = " & .CountOfLines & vbNewLine & "Procname = " & procName)
                      'MsgBox ("nr of lines REMEMBER MUST BE LARGER THAN 7! = " & .CountOfLines)
                  For i = 1 To .CountOfLines
                      procName = .ProcOfLine(i, vbext_pk_Proc)
                      If procName <> vbNullString Then
                          If i > 1 Then
                                  'MsgBox ("Line " & i & " is a body line " & .ProcBodyLine(procName, vbext_pk_Proc))
                              If i = .ProcBodyLine(procName, vbext_pk_Proc) Then inprocbodylines = True
                                  If .Lines(i - 1, 1) <> "" Then
                                      'MsgBox (.Lines(i - 1, 1))
                                  End If
                              If Not .Lines(i - 1, 1) Like "* _" Then
                                  'MsgBox (inprocbodylines)
                                  inprocbodylines = False
                                      'MsgBox ("recoginized a line that should be substituted: " & i)
                                  'MsgBox ("about to replace " & .Lines(i, 1) & vbNewLine & " with: " & RemoveOneLineNumber(.Lines(i, 1), LabelType) & vbNewLine & " with label type: " & LabelType)
                                  .ReplaceLine i, RemoveOneLineNumber(.Lines(i, 1), LabelType)
                              Else
                                  If IsInProcBodyLines Then
                                      ' do nothing
                                          'MsgBox (i)
                                  Else
                                      .ReplaceLine i, Mid(.Lines(i, 1), RemovedChars_previous_i + 1)
                                  End If
                              End If
                          End If
                      Else
                      ' GoTo NextLine
                      End If
          NextLine:
                  Next i
              End With
          End Sub
                    Function RemoveOneLineNumber(ByVal aString As String, ByVal LabelType As vbLineNumbers_LabelTypes)
              RemoveOneLineNumber = aString
              If LabelType = vbLabelColon Then
                  If aString Like "#:*" Or aString Like "##:*" Or aString Like "###:*" Or aString Like "####:*" Then
                      RemoveOneLineNumber = Mid(aString, 1 + InStr(1, aString, ":", vbTextCompare))
                      If Left(RemoveOneLineNumber, 2) Like " [! ]*" Then RemoveOneLineNumber = Mid(RemoveOneLineNumber, 2)
                  End If
              ElseIf LabelType = vbLabelTab Then
                  If aString Like "#   *" Or aString Like "##  *" Or aString Like "### *" Or aString Like "#### *" Then RemoveOneLineNumber = Mid(aString, 5)
                  If aString Like "#" Or aString Like "##" Or aString Like "###" Or aString Like "####" Then RemoveOneLineNumber = ""
              End If
          End Function
                    Function HasLabel(ByVal aString As String, ByVal LabelType As vbLineNumbers_LabelTypes) As Boolean
              If LabelType = vbLabelColon Then HasLabel = InStr(1, aString & ":", ":") < InStr(1, aString & " ", " ")
              If LabelType = vbLabelTab Then
                  HasLabel = Mid(aString, 1, 4) Like "#   " Or Mid(aString, 1, 4) Like "##  " Or Mid(aString, 1, 4) Like "### " Or Mid(aString, 1, 5) Like "#### "
              End If
          End Function
                    Function RemoveLeadingSpaces(ByVal aString As String) As String
              Do Until Left(aString, 1) <> " "
                  aString = Mid(aString, 2)
              Loop
              RemoveLeadingSpaces = aString
          End Function
                    Function WhatIsLineIndent(ByVal aString As String) As String
              i = 1
              Do Until Mid(aString, i, 1) <> " "
                  i = i + 1
              Loop
              WhatIsLineIndent = i
          End Function
      
                    Function HowManyLeadingSpaces(ByVal aString As String) As String
              HowManyLeadingSpaces = WhatIsLineIndent(aString) - 1
          End Function
      

      Module3

          Global allow_for_line_addition As String 'this is just so that you can automatically add linenumbers
                  Sub remove_line_numbering_all_modules()
          'source: https://stackoverflow.com/questions/36791473/vba-getting-the-modules-in-workbook
          'This code numbers all the modules in your .xlsm
              Dim vbcomp As VBComponent
              Dim modules As Collection
          Set modules = New Collection
              For Each vbcomp In ThisWorkbook.VBProject.VBComponents
                  'if normal or class module
                  If ((vbcomp.Type = vbext_ct_StdModule) Or (vbcomp.Type = vbext_ct_ClassModule)) Then
                         'V0:
                         RemoveLineNumbers wbName:=ThisWorkbook.name, vbCompName:=vbcomp.name, LabelType:=vbLabelColon
                         'V1:
                         'Call RemoveLineNumbers(ThisWorkbook.name, vbcomp.name)
                  End If
              Next vbcomp
          End Sub
      

      Module4

          Global allow_for_line_addition As String 'this is just so that you can automatically add linenumbers
          'This sub adds line numbers to all the modules after you have added the following line to every module
          'add tools references microsoft visual basic for applications (5.3) as checked
          'Source httpsstackoverflow.comquestions40731182excel-vba-how-to-turn-on-line-numbers-in-code-editor50368332#50368332
                  Sub add_line_numbering_all_modules()
          'source: https://www.stackoverflow.com/questions/36791473/vba-getting-the-modules-in-workbook
          'This code numbers all the modules in your .xlsm
              Dim vbcomp As VBComponent
              Dim modules As Collection
              Set modules = New Collection
              For Each vbcomp In ThisWorkbook.VBProject.VBComponents
                  'if normal or class module
                  If ((vbcomp.Type = vbext_ct_StdModule) Or (vbcomp.Type = vbext_ct_ClassModule)) Then
                         'V0:
                         Call AddLineNumbers(ThisWorkbook.name, vbcomp.name, vbLabelColon, True, True, vbScopeAllProc)
                         'v1
                         'Call AddLineNumbers(ThisWorkbook.name, vbcomp.name)
                  End If
              Next vbcomp
          End Sub
      

      您可以将"Book1.xlsm" 替换为您自己的工作簿的名称,或使用thisworkbook(注意没有“”),反之亦然。

      • *注意这在 excel 2016 中有效,我在 2013 年还没有尝试过。
      • ^它是 Hemced 的回答 here. 的修改版本,它又看起来很像 Arich 的回答。
      • °因为有时如果你剪掉或移动它们会出错(例如将line 2440:放在line 2303:上方)。通过删除并重新添加它们,行号会自动再次正确。

      【讨论】:

        【解决方案4】:

        这不是 100% 测试的,但是使用 VBA 可扩展性您可以执行以下操作

        Sub line_number(strModuleName As String)
        
        Dim vbProj As VBProject
        Dim vbComp As VBComponent
        Dim cmCode As CodeModule
        Dim intLine As Integer
        
        Set vbProj = Application.VBE.ActiveVBProject
        Set vbComp = vbProj.VBComponents(strModuleName)
        Set cmCode = vbComp.CodeModule
        
        For intLine = 2 To cmCode.CountOfLines - 1
           cmCode.InsertLines intLine, intLine - 1 &  cmCode.Lines(intLine, 1)
           cmCode.DeleteLines intLine + 1, 1
        Next intLine
        
        End Sub
        

        这给出了前后的结果如下,但不建议以这种方式改变。

        【讨论】:

          【解决方案5】:

          VBA 编辑器在“标准”工具栏下内置了查看行号的方法:

          当您选择一行代码时,行号将显示在此处的“Ln”旁边。

          【讨论】:

          • 出于调试原因,我想在编辑器中显示行号。
          【解决方案6】:

          这对我有用...将其添加到它自己的模块中。调用代码将打开或关闭行号。在引号中添加模块标题和/或过程标题将仅更新命名的模块或过程。

              Option Compare Database
              Option Explicit
          
              Sub AddLineNumbers(Optional vbCompName As String, Optional vbCompSubName As String)
              On Error Resume Next
          
                  DoCmd.Hourglass True
                  Application.VBE.ActiveVBProject.References.AddFromGuid "{0002E157-0000-0000-C000-000000000046}", 5, 0
                  Call ExecuteAddLineNumbers(vbCompName, vbCompSubName)
                  DoCmd.Hourglass False
          
              End Sub
          
              Sub ExecuteAddLineNumbers(Optional vbCompName As String, Optional vbCompSubName As String)
              On Error GoTo Err_Handler
          
                  'create a reference to the Microsoft Visual Basic for Applications Extensibility library
                  Dim i As Long, j As Long, lineN As Long
                  Dim procName As String
                  Dim startOfProceedure As Long
                  Dim lengthOfProceedure As Long
                  Dim newLine As String
                  Dim objComponent As Object
                  Dim lineNumber As Long
                  Dim HasLineNumbers As Boolean
          
                  For Each objComponent In Application.VBE.ActiveVBProject.VBComponents
                      If (vbCompName = vbNullString Or objComponent.Name = vbCompName) And objComponent.Name <> _
                      Application.VBE.ActiveCodePane.CodeModule.Name) Then
                          Debug.Print objComponent.Name
                          With objComponent.CodeModule
                              .CodePane.Window.Visible = False
                              For i = 1 To .CountOfLines
                                  'Debug.Print .ProcOfLine(i, vbext_pk_Proc)
                                  If procName = "" And .ProcOfLine(i, vbext_pk_Proc) <> "" Then
                                      procName = .ProcOfLine(i, vbext_pk_Proc)
                                      'vbext_pk_Get    Specifies a procedure that returns the value of a property.
                                      'vbext_pk_Let    Specifies a procedure that assigns a value to a property.
                                      'vbext_pk_Set    Specifies a procedure that sets a reference to an object.
                                      'vbext_pk_Proc   Specifies all procedures other than property procedures.
                                      'type=vbext_ct_ClassModule
                                      'type=vbext_ct_StdModule
                                      'type=vbext_ct_Document
                                      If objComponent.Type = vbext_ct_ClassModule Then
                                          If InStr(.Lines(i + 1, 1), " Let ") > 0 Then
                                              startOfProceedure = .ProcStartLine(procName, vbext_pk_Let)
                                              lengthOfProceedure = .ProcCountLines(procName, vbext_pk_Let)
                                          ElseIf InStr(.Lines(i + 1, 1), " Get ") > 0 Then
                                              startOfProceedure = .ProcStartLine(procName, vbext_pk_Get)
                                              lengthOfProceedure = .ProcCountLines(procName, vbext_pk_Get)
                                          ElseIf InStr(.Lines(i + 1, 1), " Set ") > 0 Then
                                              startOfProceedure = .ProcStartLine(procName, vbext_pk_Set)
                                              lengthOfProceedure = .ProcCountLines(procName, vbext_pk_Set)
                                          Else
                                              startOfProceedure = .ProcStartLine(procName, vbext_pk_Proc)
                                              lengthOfProceedure = .ProcCountLines(procName, vbext_pk_Proc)
                                          End If
                                      Else
                                          startOfProceedure = .ProcStartLine(procName, vbext_pk_Proc)
                                          lengthOfProceedure = .ProcCountLines(procName, vbext_pk_Proc)
                                      End If
                                      lineNumber = 10
                                      HasLineNumbers = .Find("##  ", startOfProceedure + 1, 1, startOfProceedure + lengthOfProceedure - 1, 1, _
                                      False, False, True)
                                  End If
          
                                  If (vbCompSubName = vbNullString And procName <> vbNullString) Or _
                                     (vbCompSubName <> vbNullString And procName = vbCompSubName) Then
          
                                      If startOfProceedure + 1 < i And i < startOfProceedure + lengthOfProceedure - 1 Then
                                          newLine = RemoveOneLineNumber(.Lines(i, 1), HasLineNumbers)
                                          If Trim(newLine) <> vbNullString Then
                                              If Not HasLabel(newLine) And Not (.Lines(i - 1, 1) Like "* _") Then
                                                  If HasLineNumbers = False Then newLine = CStr(lineNumber) & vbTab & newLine
                                                  .ReplaceLine i, newLine
                                                  lineNumber = lineNumber + 10
                                              ElseIf Not HasLineNumbers Then
                                                  .ReplaceLine i, vbTab & newLine
                                              Else
                                                  .ReplaceLine i, newLine
                                              End If
                                          End If
                                      ElseIf i = startOfProceedure + lengthOfProceedure - 1 Then
                                          procName = ""
                                      End If
                                  Else
                                      procName = ""
                                  End If
          
                              Next i
                              .CodePane.Window.Visible = True
                          End With
                      End If
                  Next objComponent
          
              Exit Sub
          
              Err_Handler:
                  MsgBox (Err.Number & ": " & Err.Description)
          
              End Sub
          
              Function RemoveOneLineNumber(aString As String, HasLineNumbers As Boolean)
                  Dim i As Double
                  RemoveOneLineNumber = aString
                  i = ((Len(Trim(Str(Val(aString)))) / 4) - Int(Len(Trim(Str(Val(aString)))) / 4)) * 4
                  If aString Like "#*" Then
                      RemoveOneLineNumber = Space(i) & Mid(aString, InStr(1, aString, " ", vbTextCompare))
                      RemoveOneLineNumber = Right(aString, Len(aString) - 4)
                  ElseIf HasLineNumbers And aString Like "    *" Then
                      RemoveOneLineNumber = Right(aString, Len(aString) - 4)
                  End If
              End Function
          
              Function HasLabel(ByVal aString As String) As Boolean
                  HasLabel = False
                  If Right(Trim(aString), 1) = ":" Or _
                      Left(Trim(aString), 3) = "Dim" Or _
                      Left(Trim(aString), 3) = "ReDim" Or _
                      Left(Trim(aString), 1) = "'" Or _
                      Left(Trim(aString), 6) = "Option" Or _
                      Left(Trim(aString), 5) = "Debug" Or _
                      Left(Trim(aString), 3) = "Sub" Or _
                      Left(Trim(aString), 11) = "Private Sub" Or _
                      Left(Trim(aString), 10) = "Public Sub" Or _
                      Left(Trim(aString), 8) = "Function" Or _
                      Left(Trim(aString), 12) = "End Function" Or _
                      Left(Trim(aString), 8) = "Property" Or _
                      Left(Trim(aString), 12) = "End Property" Or _
                      Left(Trim(aString), 7) = "End Sub" Then HasLabel = True
          
              End Function
          

          【讨论】:

          • 请注意,Erl 语句会悄悄地溢出超过 32,767 的行号,这使得 lineNumber As Long 成为一条危险且易滑的误导性道路。一个程序可以长达 10,000 行; +10 增量 溢出 IntegerErl 将报告错误的行号。行号是远古时代的遗物,仅支持向后兼容。将它们添加到新代码中毫无意义。
          【解决方案7】:

          MZ-Tools for VBA 具有向单个函数、模块或整个项目添加和删除行号的功能。

          http://www.mztools.com/v8/onlinehelp/index.html?add_remove_line_numbers.htm

          注 1:我发现将行号增量配置为 1 而不是 10 更好。您永远不会在其间手动添加行号 - 每当您编辑代码时,您首先删除行号,然后在您添加它们时完成了。

          注 2:直到几年前,MZ-Tools 还是有免费的 3.0 版本,但要找到一个副本却出奇的难。但这是一项不错的投资 - 还有许多其他有用的功能(例如自动添加错误处理程序)。

          【讨论】:

            【解决方案8】:

            我使用此代码将行号添加到我的 Excel 项目中。不久前我在网上找到了它,但我不记得我在哪里得到它,所以感谢最初写这篇文章的人:

            Sub AddLineNumbers(wbName As String, vbCompName As String)
                'See MakeUF
                Dim i As Long, j As Long, lineN As Long
                Dim procName As String
                Dim startOfProceedure As Long
                Dim lengthOfProceedure As Long
                Dim newLine As String
            
                With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
                    .CodePane.Window.Visible = False
            
                    For i = 1 To .CountOfLines
                        procName = .ProcOfLine(i, vbext_pk_Proc)
            
                        If procName <> vbNullString Then
                            startOfProceedure = .ProcStartLine(procName, vbext_pk_Proc)
                            lengthOfProceedure = .ProcCountLines(procName, vbext_pk_Proc)
            
                            If startOfProceedure + 1 < i And i < startOfProceedure + lengthOfProceedure - 1 Then
                                newLine = RemoveOneLineNumber(.Lines(i, 1))
                                If Not HasLabel(newLine) And Not (.Lines(i - 1, 1) Like "* _") Then
                                    .ReplaceLine i, CStr(i) & ":" & newLine
                                End If
                            End If
                        End If
            
                    Next i
                    .CodePane.Window.Visible = True
                End With
            End Sub
            
            Sub RemoveLineNumbers(wbName As String, vbCompName As String)
                'See MakeUF
                Dim i As Long
                With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
                    For i = 1 To .CountOfLines
                        .ReplaceLine i, RemoveOneLineNumber(.Lines(i, 1))
                    Next i
                End With
            End Sub
            
            Function RemoveOneLineNumber(aString)
                RemoveOneLineNumber = aString
                If aString Like "#:*" Or aString Like "##:*" Or aString Like "###:*" Then
                    RemoveOneLineNumber = Mid(aString, 1 + InStr(1, aString, ":", vbTextCompare))
                End If
            End Function
            
            Function HasLabel(ByVal aString As String) As Boolean
                HasLabel = InStr(1, aString & ":", ":") < InStr(1, aString & " ", " ")
            End Function
            

            由于您在 Access 中工作,因此您必须对其进行修改以满足您的需要,但我确信它的主要内容仍然适用。在 Excel 中,有一个用户表单用于启动您指定的模块的代码,但您应该能够只传递模块名称 (vbCompName) 来指定模块。我不精通 Access VBA,所以我不确定你会在代码中用什么替换 Workbooks(wbName)

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2022-06-25
              • 1970-01-01
              • 2017-05-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-08-06
              • 2019-06-09
              相关资源
              最近更新 更多