在 MsgBox 中查找带有 VBA 正则表达式的字符串文字的文本行

Find line of text in MsgBox with a string literal with VBA regex

提问人:Noam Brand 提问时间:11/14/2023 最后编辑:GSergNoam Brand 更新时间:11/15/2023 访问量:142

问:

我想知道工作簿的模块中是否存在某种格式的代码行,在本例中为 VBA 正则表达式的带有字符串文字的 MsgBox。
已找到,但即使它存在于模块中也找不到。
pattern1pattern2

Pattern1将 MsgBox 与字符串文字匹配,并带有左括号和右括号( ),例如:

testString = MsgBox("Do you want to open our product page?", vbInformation + vbYesNoCancel, "myMessageBox")
testString = MsgBox("Do you want to open our product page?", vbYesNoCancel, "myMessageBox")
testString = MsgBox("Do you want to open our product page?", vbYesNoCancel)
testString = MsgBox("Do you want to open our product page?")

Pattern2将 MsgBox 与字符串文字匹配,不带左括号和右括号( ),例如:

MsgBox "Do you want to open our product page?", vbInformation + vbYesNoCancel, "myMessageBox"
MsgBox "Do you want to open our product page?", vbInformation + vbYesNoCancel
MsgBox "Do you want to open our product page?"
' Matches strings in all VBA modules of the specified workbook
' Add a reference to "Microsoft VBScript Regular Expressions 5.5" for early binding

Public Sub FindStringsInModules(ByVal objWorkbookToObfuscate As Workbook)
    Dim vbComp As Object
    Dim lineNum As Long
    Dim lineText As String
    Dim newLineText As String
    
    ' Iterate over all VBA modules in the provided workbook
    For Each vbComp In objWorkbookToObfuscate.VBProject.VBComponents
        ' Process each line in the module
        Dim lineCount As Long
        lineCount = vbComp.codeModule.CountOfLines
        For lineNum = 1 To lineCount
            lineText = vbComp.codeModule.Lines(lineNum, 1)
            newLineText = MatchLine(lineText)
            vbComp.codeModule.ReplaceLine lineNum, newLineText
        Next lineNum
    Next vbComp
End Sub

Function MatchLine(ByVal lineText As String) As String
    Dim regex As RegExp
    Set regex = New RegExp
    Dim pattern1 As String
    Dim pattern2 As String
    Dim pattern3 As String

    ' Pattern 1: Matches MsgBox with string literals, with opening and closing parenthesis ( )
    pattern1 = "(?:[a-zA-Z0-9]+\s*=\s*)?MsgBox\s*\(\s*(""[^""]*"")(?:\s*,\s*[^,)]+(?:\s*\+\s*[^,)]+)*)?(?:\s*,\s*""[^""]*"")?\s*\)"
    ' Pattern 2: Matches MsgBox without parentheses, including additional parameters and concatenations
    pattern2 = "MsgBox\s+""""[^""""]*""""(?:\s*&\s*""""[^""""]*""""|[^""""])*(?:\s*,\s*[^,""""]+)*"
    pattern3 = "MsgBox\(""[^""]*""\)\s*"

    ' Combine patterns using alternation operator
    regex.Pattern = pattern1 & "|" & pattern2 & "|" & pattern3
    regex.Global = True
    regex.MultiLine = True

    Dim matches As MatchCollection
    Set matches = regex.Execute(lineText)

    ' Process each match for potential transformation
    If matches.Count > 0 Then
        Dim match As match
        For Each match In matches
            Dim literal As String
            literal = match.SubMatches(0)
            If literal = "" Then
               Debug.Print "lineText not catched:" & lineText
               'Stop
            End If
        Next match
    End If

    MatchLine = lineText
End Function

match.SubMatches(0)即使 .literal = ""matches.Count > 0

P.S. 在 VBA 中针对字符串文字进行测试时,MsgBox 行 需要用双引号括起来,内部双引号加倍,例如 .
针对一行 VBA 代码(而不是字符串文本)进行测试时,MsgBox 行将显示为直接写入代码,例如 .
testString = "MsgBox ""Do you want to open our product page?"""MsgBox "Do you want to open our product page?"

正则表达式 VBA 代码分析

评论

1赞 GSerg 11/15/2023
正则表达式中有很多可选组件。为什么第一个子组是空匹配项会让您失望?什么?match.Value
0赞 Noam Brand 11/17/2023
谢谢,那就更合适了match.Valuematch.SubMatches(0)

答:

1赞 TinMan 11/15/2023 #1

Regex.Test(Text)如果存在匹配项,则返回 true。以下是我如何重构代码:

Sub ExampleUsage()
    Const Pattern1 As String = "(?:[a-zA-Z0-9]+\s*=\s*)?MsgBox\s*\(\s*(""[^""]*"")(?:\s*,\s*[^,)]+(?:\s*\+\s*[^,)]+)*)?(?:\s*,\s*""[^""]*"")?\s*\)"
    Const Pattern2 As String = "MsgBox\s+""""[^""""]*""""(?:\s*&\s*""""[^""""]*""""|[^""""])*(?:\s*,\s*[^,""""]+)*"
    Const Pattern3 As String = "MsgBox\(""[^""]*""\)\s*"
    Dim MasterPattern As String
    MasterPattern = Join(Pattern1, Pattern2, Pattern3)
    
    
    Const NewText As String = "MsgBox(""Sample Text"")"
    
    Dim Patterns() As Variant
    Patterns = Array(Pattern1, Pattern2, Pattern3)
    
    ReplaceMatchesInModule ThisWorkbook, NewText, MasterPattern
End Sub

Public Sub ReplaceMatchesInModule(ByVal objWorkbookToObfuscate As Workbook, NewText As String, Pattern As String)
    Rem Requires the reference to "Microsoft Visual Basic for Applications Extensibility #.#" library
    Dim Component As VBComponent
    Dim CodeModule As CodeModule
    Dim lineNum As Long
    Dim lineText As String

    ' Iterate over all VBA modules in the provided workbook
    For Each vbComp In objWorkbookToObfuscate.VBProject.VBComponents
        ' Process each line in the module
        Dim lineCount As Long
        lineCount = Component.CodeModule.CountOfLines
        For lineNum = 1 To lineCount
            Set CodeModule = Component.CodeModule
            lineText = CodeModule.Lines(lineNum, 1)
            
            If RegExMatch(Pattern) Then
                CodeModule.ReplaceLine lineNum, NewText
            End If
        Next lineNum
    Next vbComp
End Sub

Public Function RegExMatch(Text As String, GlobalMatch As Boolean, Multiline As Boolean, ByRef Patterns() As Variant)
    Rem Requires the reference to "Microsoft VBScript Regular Expressions 5.5" library
    Dim Regex As New RegExp
    Regex.Global = GlobalMatch
    Regex.Multiline = Multiline
    
    Dim Pattern As Variant
    For Each Pattern In Patterns
        If Regex.Test(Text) Then
            RegExMatch = True
            Exit Function
        End If
    Next
End Function

测试正则表达式很乏味。regex101.com 是一个受欢迎的测试网站。

表达:

(?:[a-zA-Z0-9]+\s*=\s*)?MsgBox\s*(\s*(“[^”]“)(?:\s,\s*[^,)]+(?:\s*+\s*[^,)]+))?(?:\s,\s*“[^”]“)?\s)|MsgBox\s+“”[^“”]“”(?:\s&\s*“”[^“”]“”|[^“”])(?:\s*,\s*[^,“”]+)|MsgBox(“[^”]“)\s*