So a short while ago Mike Wolfe mentioned that Microsoft had announced they were going to phase out and retire VBScript

and the implications of this could be far reaching impacting all sorts of things, most notably the possible loss of:
- File System Object (FSO)
- Regular Expressions (RegEx)
- and more
All that has officially been stated is
Now let’s be clear here, we are all still waiting for clarifications to come from Microsoft on the subject so we can truly know 100% what will be lost when this does actually occurs, which libraries are going to disappear. Everything up until now is simply logical assumptions based on what we know of the libraries, but no official specifics have truly been given to date. (and yes, many people have asked for clarifications, but none have been given thus far)
After years of waiting on Microsoft to publicly provide information about various subjects and it never coming to bare or taking way too long, I decided not to wait on this subject matter. I wanted to try and prepare myself as best I could should such things occur. Plan now rather than panic later!
As such, I’ve decided to share my work here in the hopes it might help others.
Initially, I decided to concentrate on seeing if there wasn’t another approach that could be used in VBA to provided RegEx functionality without the use of the VBScript RegulareExpression library (CreateObject(“VBScript.RegExp”)). For this, I turned towards my web development experience.
RegEx is common in web development and I knew that I could probably do something through the web browser controls, but I didn’t want to have to require some hidden form … That just wouldn’t be ideal. Then I remembered having done some JS via code when I wanted to extract the base URL from a full URL which employed the used of the Script Control.
Now, I didn’t see the Script Control (msscript.ocx) mentioned in Mike’s article, so I’m hoping it isn’t part of the VB Script deprecation!
With this knowledge in hand, I was able to gain RegEx functionalities without the need of the libraries mentioned in Mike’s article, nor use any database objects to make it all happen. This approach should work in any VBA environment (Access, Excel, Outlook, PowerPoint, Word, …).
Validation
If we want to perform RegEx validations, we can now do something like:
'---------------------------------------------------------------------------------------
' Procedure : JSRegEx_Validate
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Performs RegEx validation via the Script Control ocx instead of the
' VBScript RegulareExpression.
' It returns True if it is valid, otherwise it returns False
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding -> None required
' Early Binding -> Micrsoft Script Control X.X
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern : The RegEx pattern to test the object against
' sPatternFlags : The RegEx pattern flags to use
' vInput : The Object to validate against the specified pattern
'
' Usage:
' ~~~~~~
' Validate URL
' ? JSRegEx_Validate("/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/", "igm", "www.google.ca")
' Returns => True
'
' ? JSRegEx_Validate("/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/", "igm", "www.googleca")
' Returns => False
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2024-03-02 Initial Release
'---------------------------------------------------------------------------------------
Public Function JSRegEx_Validate(ByVal sPattern As String, _
ByVal sPatternFlags As String, _
ByVal vInput As Variant) As String
On Error GoTo Error_Handler
#Const ScriptControl_EarlyBind = False 'Should normally be in the Module header
#If ScriptControl_EarlyBind = True Then
Dim oSC As MSScriptControl.ScriptControl
Set oSC = New ScriptControl
#Else
Static oSC As Object
Set oSC = CreateObject("ScriptControl")
#End If
Dim sOutput
If Not oSC Is Nothing Then
With oSC
.Language = "JScript"
' '? JSRegEx_Validate("/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/", "igm", "www.google.ca")
' .AddCode "function regEx_Pattern_Test(myInput) {" & _
' " var regEx_Pattern = " & sPattern & sPatternFlags & ";" & _
' " return regEx_Pattern.test(myInput);" & _
' "}"
' sOutput = .Run("regEx_Pattern_Test", vInput)
'? JSRegEx_Validate("/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/", "igm", "www.google.ca")
.AddCode "function regEx_Pattern_Test(myPattern, myPatternFlags, myInput) {" & _
" var regEx_Pattern = eval(myPattern + myPatternFlags);" & _
" return regEx_Pattern.test(myInput);" & _
"}"
sOutput = .Run("regEx_Pattern_Test", sPattern, sPatternFlags, vInput)
End With
End If
JSRegEx_Validate = sOutput
Error_Handler_Exit:
On Error Resume Next
Set oSC = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: JSRegEx_Validate" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
or
'---------------------------------------------------------------------------------------
' Procedure : JSRegEx_Validate
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Performs RegEx validation via the Script Control ocx instead of the
' VBScript RegulareExpression.
' It returns True if it is valid, otherwise it returns False
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding -> None required
' Early Binding -> Micrsoft Script Control X.X
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern : The RegEx pattern to test the object against
' sPatternFlags : The RegEx pattern flags to use
' vInput : The Object to validate against the specified pattern
'
' Usage:
' ~~~~~~
' Validate URL
' ? JSRegEx_Validate("/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/", "igm", "www.google.ca")
' Returns => True
'
' ? JSRegEx_Validate("/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/", "igm", "www.googleca")
' Returns => False
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2024-03-02 Initial Release
'---------------------------------------------------------------------------------------
Public Function JSRegEx_Validate(ByVal sPattern As String, _
ByVal sPatternFlags As String, _
ByVal vInput As Variant) As String
On Error GoTo Error_Handler
'#Const ScriptControl_EarlyBind = False 'Should normally be in the Module header
#If ScriptControl_EarlyBind = True Then
Dim oSC As MSScriptControl.ScriptControl
Set oSC = New ScriptControl
#Else
Static oSC As Object
Set oSC = CreateObject("ScriptControl")
#End If
Dim sOutput
If Not oSC Is Nothing Then
With oSC
.Language = "JScript"
'? JSRegEx_Validate("^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$", "igm", "www.google.ca")
.AddCode "function regEx_Pattern_Test(myPattern, myPatternFlags, myInput) {" & _
" var regEx_Pattern = new RegExp(myPattern, myPatternFlags);" & _
" return regEx_Pattern.test(myInput);" & _
"}"
sOutput = .Run("regEx_Pattern_Test", sPattern, sPatternFlags, vInput)
End With
End If
JSRegEx_Validate = sOutput
Error_Handler_Exit:
On Error Resume Next
Set oSC = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: JSRegEx_Validate" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
Validate a URL
You could use the above by doing:
? JSRegEx_Validate("^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$", "igm", "www.google.ca")
which will return True
? JSRegEx_Validate("^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$", "igm", "www.googleca")
which will return False
We could also create a wrapper helper function for various common tests: url, e-mail, phone, … to simply coding/usage. For instance, to validate URLs, we could do:
Public Function JSRegEx_Validate_URL(ByVal sURL As Variant) As Boolean
Const sPattern = "/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/"
Const sPatternFlags = "igm"
JSRegEx_Validate_URL = JSRegEx_Validate(sPattern, sPatternFlags, sURL)
End Function
and then validation become much simpler and only requires:
? JSRegEx_Validate_URL("www.google.ca") 'Returns True
? JSRegEx_Validate_URL("www.googleca") 'Returns False
Extracting Matches
Similarly to the above, we can easily develop a universal RegEx Match function that uses the Script Control ocx to find and extract segments of a larger string that match a specified pattern. So not just test if it conforms, but extract the matching segment.
We can do something like:
'---------------------------------------------------------------------------------------
' Procedure : JSRegEx_Matches
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Uses Script Control ocx RegEx to extract the matches of the defined
' pattern.
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding -> None required
' Early Binding -> Micrsoft Script Control X.X
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern : The RegEx pattern to test the object against and extract macthes of
' sPatternFlags : The RegEx pattern flags to use
' vInput : The Object to validate against the specified pattern
' sDelimiter : Delimiter to be used to separate the matches in the returned string
' If omitted , is used
'
' Usage:
' ~~~~~~
' Extract E-mail addresses from a string
' ? JSRegEx_Matches("([a-zA-ZF0-9\u00C0-\u017F._-]+@[a-zA-Z0-9\u00C0-\u017F._-]+\.[a-zA-Z0-9\u00C0-\u017F_-]+)", _
' "gmi", _
' "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")
' Returns => dani@ccc.com, marth@marth.ru, carl@hotmail.com
'
' ? JSRegEx_Matches("([a-zA-ZF0-9\u00C0-\u017F._-]+@[a-zA-Z0-9\u00C0-\u017F._-]+\.[a-zA-Z0-9\u00C0-\u017F_-]+)", _
' "gmi", _
' "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .", _
' "~")
' Returns => dani@ccc.com~marth@marth.ru~carl@hotmail.com
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2024-03-02 Initial Release
'---------------------------------------------------------------------------------------
Public Function JSRegEx_Matches(ByVal sPattern As String, _
ByVal sPatternFlags As String, _
ByVal vInput As Variant, _
Optional sDelimiter As String = ", ") As String
On Error GoTo Error_Handler
'#Const ScriptControl_EarlyBind = False 'Should normally be in the Module header
#If ScriptControl_EarlyBind = True Then
Dim oSC As MSScriptControl.ScriptControl
Set oSC = New ScriptControl
#Else
Static oSC As Object
Set oSC = CreateObject("ScriptControl")
#End If
Dim sScript As String
Dim sOutput As String
If Not oSC Is Nothing Then
With oSC
.Language = "JScript"
sScript = "function patternTest(myPattern, myPatternFlags, myInput, myDelimiter) {" & _
" var reg = new RegExp(myPattern, myPatternFlags);" & _
" var output = '';" & _
" var matches = myInput.match(reg);" & _
" if(matches && matches.length != 0){" & _
" for (var i = 0; i < matches.length; i++) {" & _
" output = output + myDelimiter + matches[i];" & _
" }" & _
" output = output.slice(myDelimiter.length);" & _
" }" & _
" return output;" & _
"}"
'Debug.Print sScript 'For Debugging
.AddCode sScript
sOutput = .Run("patternTest", sPattern, sPatternFlags, vInput, sDelimiter)
End With
End If
JSRegEx_Matches = sOutput
Error_Handler_Exit:
On Error Resume Next
Set oSC = Nothing
Exit Function
Error_Handler:
Debug.Print Err.Number, Err.Description
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: JSRegEx_Matches" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
Extracting E-mail Addresses
Below is a simple example of how this could be used to extract all the e-mail addresses from a string.
? JSRegEx_Matches("([a-zA-ZF0-9\u00C0-\u017F._-]+@[a-zA-Z0-9\u00C0-\u017F._-]+\.[a-zA-Z0-9\u00C0-\u017F_-]+)", _
"gmi", _
"some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")
which then would return
dani@ccc.com, marth@marth.ru, carl@hotmail.com
As with the previous section, we could simplify frequent usage by creating a simple wrapper helper functions like:
Public Function JSRegEx_Extract_Emails(ByVal sInput As String) As String
Const sPattern = "([a-zA-ZF0-9\u00C0-\u017F._-]+@[a-zA-Z0-9\u00C0-\u017F._-]+\.[a-zA-Z0-9\u00C0-\u017F_-]+)"
Const sPatternFlags = "igm"
Const sDelim = "||"
JSRegEx_Extract_Emails = JSRegEx_Matches(sPattern, sPatternFlags, sInput, sDelim)
End Function
and which we would call by doing:
?JSRegEx_Extract_Emails("some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")
which returns => dani@ccc.com||marth@marth.ru||carl@hotmail.com
Or we could instead create a wrapper like:
Public Function JSRegEx_Extract_Emails(ByVal sInput As String) As Variant
Const sPattern = "([a-zA-ZF0-9\u00C0-\u017F._-]+@[a-zA-Z0-9\u00C0-\u017F._-]+\.[a-zA-Z0-9\u00C0-\u017F_-]+)"
Const sPatternFlags = "igm"
Const sDelim = "||"
JSRegEx_Extract_Emails = Split(JSRegEx_Matches(sPattern, sPatternFlags, sInput, sDelim), sDelim)
End Function
which we could use like:
Sub JSRegEx_Extract_Emails2_Test()
Dim aEmails As Variant
Dim i As Long
Const myString = "some text with dani@ccc.com and marth@marth.ru. carl@hotmail.com Some more text ."
aEmails = JSRegEx_Extract_Emails(myString)
For i = 0 To UBound(aEmails)
Debug.Print aEmails(i)
Next i
End Sub
which would then output
dani@ccc.com marth@marth.ru carl@hotmail.com
Notes Regarding Using The Script Control
While developing my code and testing I discovered a couple things you should be aware of should you wish to do any development using the Script Control, most notably:
- JScript does not accept // for inline comments. We must use the /* */ syntax.
- JScript does not support ‘let’, ‘const’, … for declaring variables. So use ‘var’ for pretty much everything!
Basically the JS version is old and isn’t compatible with any of the newer syntax. So keep things as simple as possible even if it breaks what we would consider a best practice today. Otherwise you’ll get all sorts of very weird errors that truly do not indicate the true nature of the problem with your code, errors like:
Error 1004 – Expected ‘;’
Error 1009 – Expected ‘}’
If you are looking into Script Control coding with JScript, you may like to look over:
An Alternative Technique – Using Microsoft HTML Object Library (MSHTML)
I also wanted to quickly demonstrate that we could even use the MSHTML library. For instance, below is an example of a validation function using MSHTML.
'---------------------------------------------------------------------------------------
' Procedure : MSHTMLRegEx_Validate
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Performs RegEx validation via the Microsoft HTML Object Library instead of
' the VBScript RegulareExpression.
' It returns True if it is valid, otherwise it returns False
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding -> None required
' Early Binding -> Microsoft HTML Object Library (mshtml.tlb)
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern : The RegEx pattern to test the object against
' sPatternFlags : The RegEx pattern flags to use
' vInput : The Object to validate against the specified pattern
'
' Usage:
' ~~~~~~
' Validate URL
' ? MSHTMLRegEx_Validate("/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/", "igm", "www.google.ca")
' Returns => True
'
' ? MSHTMLRegEx_Validate("/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/", "igm", "www.googleca")
' Returns => False
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2024-03-02 Initial Release
'---------------------------------------------------------------------------------------
Public Function MSHTMLRegEx_Validate(ByVal sPattern As String, _
ByVal sPatternFlags As String, _
ByVal vInput As Variant) As Boolean
#Const HF_EarlyBind = True 'True => Early Binding -> Microsoft HTML Object Library (mshtml.tlb)
'False => Late Binding
'Typically a Global Module Level Variable
#If HF_EarlyBind = True Then
Dim oHTMLFile As MSHTML.HTMLDocument
Dim oElem As MSHTML.HTMLGenericElement
Dim oLink As MSHTML.HTMLLinkElement
Dim oTable As MSHTML.HTMLTable
Dim oTr As MSHTML.HTMLTableRow
Dim oTd As MSHTML.HTMLTableCell
Set oHTMLFile = New MSHTML.HTMLDocument
#Else
Dim oHTMLFile As Object
Dim oElem As Object
Dim oLink As Object
Dim oTable As Object
Dim oTr As Object
Dim oTd As Object
Set oHTMLFile = CreateObject("HTMLFile")
#End If
oHTMLFile.body.innerHTML = "" 'Clear the default paragraph line
Set oElem = oHTMLFile.createElement("p")
Call oElem.setAttribute("id", "result")
Call oHTMLFile.body.appendChild(oElem)
Set oElem = oHTMLFile.createElement("script")
Call oElem.setAttribute("type", "text/javascript") 'Not strictly necessary as this is the default value anyways
oElem.innerText = "function regEx_Pattern_Test() {" & _
" var regEx_Pattern = " & sPattern & sPatternFlags & ";" & _
" var myInput = '" & vInput & "';" & _
" document.getElementById('result').innerText = regEx_Pattern.test(myInput);" & _
"}"
Call oHTMLFile.body.appendChild(oElem)
Call oHTMLFile.parentWindow.execScript("regEx_Pattern_Test();")
'Debug.Print oHTMLFile.body.innerHTML ' For debugging
MSHTMLRegEx_Validate = CBool(oHTMLFile.getElementById("result").innerText)
Set oHTMLFile = Nothing
End Function
and it could be called by doing:
? MSHTMLRegEx_Validate("/^(https?:\/\/)?([\da-z\.-]+\.[a-z\.]{2,6}|[\d\.]+)([\/:?=]{1}[\da-z\.-]+)*[\/\?]?$/", "igm", "https://www.google.ca")
Returns => True
for matches, we could use a function like:
'---------------------------------------------------------------------------------------
' Procedure : MSHTMLRegEx_Matches
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Uses Microsoft HTML Object Library to run RegEx to extract the matches of
' the defined pattern.
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding -> None required
' Early Binding -> Microsoft HTML Object Library (mshtml.tlb)
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sPattern : The RegEx pattern to test the object against and extract macthes of
' sPatternFlags : The RegEx pattern flags to use
' vInput : The Object to validate against the specified pattern
' sDelimiter : Delimiter to be used to separate the matches in the returned string
' If omitted , is used
'
' Usage:
' ~~~~~~
' Extract E-mail addresses from a string
' ? MSHTMLRegEx_Matches("[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)", _
' "gmi", _
' "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")
' Returns => dani@ccc.com, marth@marth.ru, carl@hotmail.com
'
' ? MSHTMLRegEx_Matches("[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)", _
' "gmi", _
' "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .", _
' "~")
' Returns => dani@ccc.com~marth@marth.ru~carl@hotmail.com
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2024-03-21 Initial Release
'---------------------------------------------------------------------------------------
Public Function MSHTMLRegEx_Matches(ByVal sPattern As String, _
ByVal sPatternFlags As String, _
ByVal vInput As Variant, _
Optional sDelimiter As String = ", ") As String
'#Const HF_EarlyBind = True 'True => Early Binding / False => Late Binding
'Typically a Global Module Level Variable
#If HF_EarlyBind = True Then
Dim oHTMLFile As MSHTML.HTMLDocument
Dim oElem As MSHTML.HTMLGenericElement
Dim oLink As MSHTML.HTMLLinkElement
Dim oTable As MSHTML.HTMLTable
Dim oTr As MSHTML.HTMLTableRow
Dim oTd As MSHTML.HTMLTableCell
Set oHTMLFile = New MSHTML.HTMLDocument
#Else
Dim oHTMLFile As Object
Dim oElem As Object
Dim oLink As Object
Dim oTable As Object
Dim oTr As Object
Dim oTd As Object
Set oHTMLFile = CreateObject("HTMLFile")
#End If
oHTMLFile.body.innerHTML = "" 'Clear the default line
Set oElem = oHTMLFile.createElement("p")
Call oElem.setAttribute("id", "result")
Call oHTMLFile.body.appendChild(oElem)
Set oElem = oHTMLFile.createElement("script")
Call oElem.setAttribute("type", "text/javascript") 'Not strictly necessary as this is the default value anyways
'*** Don't use vbcrlf ... as it will erroneously place
tags in your code!
oElem.innerText = "function patternTest() {" & _
" var myPattern = '" & sPattern & "';" & _
" var myPatternFlags = '" & sPatternFlags & "';" & _
" var myInput = '" & vInput & "';" & _
" var myDelimiter = '" & sDelimiter & "';" & _
" var reg = new RegExp(myPattern, myPatternFlags);" & _
" var output = '';" & _
" var matches = myInput.match(reg);" & _
" if(matches && matches.length != 0){" & _
" for (var i = 0; i < matches.length; i++) {" & _
" output = output + myDelimiter + matches[i];" & _
" }" & _
" output = output.slice(myDelimiter.length);" & _
" }" & _
" document.getElementById('result').innerText = output;" & _
" return true;" & _
"}"
Call oHTMLFile.body.appendChild(oElem)
Call oHTMLFile.parentWindow.execScript("patternTest();")
MSHTMLRegEx_Matches = oHTMLFile.getElementById("result").innerText
Set oHTMLFile = Nothing
End Function
which would could use by doing:
MSHTMLRegEx_Matches("[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)", "gmi", "some text with dani@ccc.com and marth@marth.ru. " & Chr(10) & Chr(13) & "carl@hotmail.com Some more text .")
and which would return:
dani@ccc.com, marth@marth.ru, carl@hotmail.com
Conclusion
As you can see, we may have a relatively simple workaround to the loss of VBScript when it comes to RegEx in VBA.
I should emphasize the benefit of using Self-Healing Object Variable (SHOV) with any repetitive procedure. Thus, the above would likely all benefit from implementing SHOV. If you're not already familiar with SHOV, review the following article to learn more:

I just hope msscript.ocx is not part of vbscript!
Also, there's is always the possibility of running such code through the web browser controls or some other Web library. We could even turn to automating RegEx via PowerShell, but this wouldn't be my first choice though because there is a performance hit associated with PowerShell automation. So worst case scenario, there is still at least several other possible workarounds available to us to explore.
There is more that can be done with this approach, but that will be an article for another time.
Some Further Resources To Explore

Page History
| Date | Summary of Changes |
|---|---|
| 2024-03-18 | Initial Release |
| 2024-03-21 | Added MSHTMLRegEx_Matches sample function to the Alternative Approach section |
