0

First, thanks to all who have already assisted in the first iteration of this message. After looking around at other sites I found a better method for searching messages for URLs using regular expressions on this page, Open All Hyperlinks in an Outlook Email Message, on slipstick.com.

The my personal tweaking of the code is:

Option Explicit

Public Sub OpenLinksMessage()

 Dim olMail As Outlook.MailItem
 Dim Reg1 As RegExp
 Dim AllMatches As MatchCollection
 Dim M As Match
 Dim strURL As String
 Dim oApp As Object

 Set oApp = CreateObject("InternetExplorer.Application")

 Set olMail = ActiveExplorer.Selection(1)

 Set Reg1 = New RegExp

'Set the Regular Expression to search for any http:// or https:// format URL
'The Global feature says to look through the entire message text being tested
With Reg1
 .Pattern = "(https?[:]//([0-9a-z=\?:/\.&-^!#$;_])*)"
 .Global = True
 .IgnoreCase = True
End With

' If the regular expression test for URLs comes back true
If Reg1.test(olMail.Body) Then

'      Use the RegEx to return all instances that match it to the AllMatches group
       Set AllMatches = Reg1.Execute(olMail.Body)
       For Each M In AllMatches
               strURL = M.SubMatches(0)
'              Don't activate any URLs that are for unsubscribing; skip them
               If InStr(strURL, "unsubscribe") Then GoTo NextURL
'              If the URL ends with a > from being enclosed in darts, strip that > off
               If Right(strURL, 1) = ">" Then strURL = Left(strURL, Len(strURL) - 1)
'              We now have a URL that we want to open in a new tab in IE
               oApp.navigate strURL, CLng(2048)
               oApp.Visible = True

' wait for page to load before passing the web URL
  Do While oApp.Busy
    DoEvents
  Loop

  NextURL:
     Next
End If

Set Reg1 = Nothing

End Sub

Private Sub TestLaunchURL()
    OpenLinksMessage
End Sub

This will be running under Windows 10, and I am wondering if there is some way to substitute the statement:

 Set oApp = CreateObject("InternetExplorer.Application")

with its equivalent but snagging the application that the user has chosen as their default web browser? I'm also not certain of what, if any change, would be necessary to the Clng(2048) to communicate that I'd like the URL to be opened in a new tab of that browser rather than the one that currently has focus.

Would:

Set olMail = ActiveExplorer.Selection(1)

be the correct syntax for the "message I've just received" when this is being triggered by a rule for an incoming message? If not, what is?

Is that DO loop really necessary? I am trying to get a handle on what it does but really don't have it yet.

There should only be a single link in messages received from a specific e-mail address that should be parsed for the link and opened, but the test for that should be in the rule that invokes this subroutine rather than in the VBA code itself. I may have some additional filtering logic, but I think it will be a matter of adding an IF statement or two.

If anyone sees any glaring error in this adapted code please do let me know. It seems to be working on tests on individual messages, as IE opens and every link that's in the message is being opened in its own tab. I'd really like to make it open in the user's default web browser if at all possible.

Thanks in advance for your assistance. It has been invaluable already.


Phase 2: The script is thoroughly tested and works. It has been installed under Outlook 2016, we signed it with a certificate created with selfcert, the Trust Center Macro permissions are still at "Notifications for digitally signed macros, all other macros disabled."

When the the rule that invokes the script is run, and the script is triggered, the following error message box appears:

VBA error message box

Any theories on what went wrong and how to fix it? I have not yet created a second certificate with selfcert and signed this again because I wanted to know if this error message might pop up for other reasons, as sometimes happens.

Also, am I correct in believing that signed, user created macros will run, if signed, regardless of what the macro security is set to unless the setting is "all macros disabled"?


Phase 3.5 (sort of)

It appears the root of the problem is that the script itself is not being run at all when a new message arrives. I set up a testing rule that initially was to play a sound and invoke the script when a message arrives with either of two addresses in the sender's address. The sound would consistently play, but nothing else happened. So, I thought, let's take the sound playing out of the Outlook rule and put it in the script itself, with one sound playing unconditionally at the start of the script. Well, nada. Here is the latest code (some of which is taken directly from prior threads here on stackoverflow):

Option Explicit

Private Declare Function ShellExecute _
  Lib "shell32.dll" Alias "ShellExecuteA" ( _
  ByVal hWnd As Long, _
  ByVal Operation As String, _
  ByVal Filename As String, _
  Optional ByVal Parameters As String, _
  Optional ByVal Directory As String, _
  Optional ByVal WindowStyle As Long = vbMinimizedFocus _
  ) As Long

Private Declare Function sndPlaySound32 _
    Lib "winmm.dll" _
    Alias "sndPlaySoundA" ( _
        ByVal lpszSoundName As String, _
        ByVal uFlags As Long) As Long

Sub PlayTheSound(ByVal WhatSound As String)
    If Dir(WhatSound, vbNormal) = "" Then
        ' WhatSound is not a file. Get the file named by
        ' WhatSound from the Windows\Media directory.
        WhatSound = Environ("SystemRoot") & "\Media\" & WhatSound
        If InStr(1, WhatSound, ".") = 0 Then
            ' if WhatSound does not have a .wav extension,
            ' add one.
            WhatSound = WhatSound & ".wav"
        End If
        If Dir(WhatSound, vbNormal) = vbNullString Then
            Beep            ' Can't find the file. Do a simple Beep.
            Exit Sub
        End If
    Else
        ' WhatSound is a file. Use it.
    End If

    sndPlaySound32 WhatSound, 0&    ' Finally, play the sound.
End Sub

Public Sub OpenLinksMessage(olMail As Outlook.MailItem)

Dim Reg1 As RegExp
Dim AllMatches As MatchCollection
Dim M As Match
Dim strURL As String
Dim RetCode As Long

Set Reg1 = New RegExp

With Reg1
.Pattern = "(https?[:]//([0-9a-z=\?:/\.&-^!#$;_])*)"
.Global = True
.IgnoreCase = True
End With

PlayTheSound "chimes.wav"

' If the regular expression test for URLs in the message body finds one or more
If Reg1.test(olMail.Body) Then

'      Use the RegEx to return all instances that match it to the AllMatches group
       Set AllMatches = Reg1.Execute(olMail.Body)
       For Each M In AllMatches
               strURL = M.SubMatches(0)
'              Don't activate any URLs that are for unsubscribing; skip them
               If InStr(1, strURL, "unsubscribe") Then GoTo NextURL
'              If the URL ends with a > from being enclosed in darts, strip that > off
               If Right(strURL, 1) = ">" Then strURL = Left(strURL, Len(strURL) - 1)
'              The URL to activate to accept must contain both of the substrings in the IF statement
               If InStr(1, strURL, ".com") Then
                     PlayTheSound "TrainWhistle.wav"
'                    Activate that link to accept the job
                     RetCode = ShellExecute(0, "Open", "http://nytimes.com")
                     Set Reg1 = Nothing
                     Exit Sub
               End If

NextURL:
   Next

End If

Set Reg1 = Nothing

End Sub

Private Sub TestLaunchURL()
    Dim currItem As MailItem
    Set currItem = ActiveExplorer.Selection(1)
    OpenLinksMessage currItem
End Sub

If I trigger the above code either by using the TestLaunch subroutine via the debugger, or create a rule and at the end say "Run on messages already in the inbox," it functions perfectly.

The only thing I can't do right now is get this to be triggered using the "run script" feature of an Outlook Rule when a new message arrives.

Any theories or assistance regarding how to get over this last hurdle would be very much appreciated.

britechguy
  • 21
  • 7
  • On Windows you should be able to Shell out (e.g.) `start http://google.com` and that should use the user's default browser. – Tim Williams Aug 06 '17 at 19:27
  • Previously: https://stackoverflow.com/questions/3166265/open-an-html-page-in-default-browser-with-vba – Tim Williams Aug 06 '17 at 19:28
  • Thanks, Tim. Just tried that by hand in PowerShell and not only does it work, but the URL is opened in a new tab in my default browser since this is how I have my preferences set. I presume if someone elected to open links in a new window a second instance of their default browser would appear. – britechguy Aug 06 '17 at 19:32
  • I don't know - easy enough to test though... – Tim Williams Aug 06 '17 at 19:33
  • 2048 is BrowserNavConstants.navOpenInNewTab in this question's context. – S Meaden Aug 06 '17 at 20:20
  • @TimWilliams, I am pulling my hair out over trying to shell out. In either command prompt or PowerShell typing the command _start URL_ fires up the default browser. If I try, `Shell ("C:\Program Files (x86)\Mozilla Firefox\firefox.exe" & " " & strURL)`, this fires up Firefox specifically with the URL. But how do I get the command, _start URL_ be the thing that gets executed? I've tried, `Shell ("start " & strURL)` and `Shell ("start" & " " & strURL)` to no avail. I get a file not found error at runtime. I thought this would pass "start URL" to be processed, but apparently not. – britechguy Aug 06 '17 at 21:25
  • I copied Dirk's answer from the question I linked: it worked fine. – Tim Williams Aug 06 '17 at 23:30
  • @TimWilliams Thanks. I misinterpreted your original message as meaning I could use the built in Shell function call, not that I would need to pull in the ShellExecute function and use it instead. That does work like a charm. I'd still love to know why I can't achieve this using Shell and somehow passing it "start URL", but that's strictly my personal curiosity. I greatly appreciate your help. – britechguy Aug 07 '17 at 00:17
  • http://www.vbforums.com/showthread.php?726365-RESOLVED-ShellExecute-or-VBA-Shell Shell is for when you know the executable path; ShellExecute works with just a file or URL and automatically uses the default application. – Tim Williams Aug 07 '17 at 00:33
  • @TimWilliams Thanks so much and for your time and including the references. This has been a great learning experience. – britechguy Aug 07 '17 at 01:12
  • To properly use this Q & A site you ask about a specific problem. As development continues you can post a new question. In this way there may be answers, in answer posts you can accept if it solves the problem, rather than comments. If you found your own solution this should be in an answer post. The question about run a script may be programming related but should be asked separately, perhaps with a link to this question. – niton Aug 13 '17 at 12:36
  • Thank you, niton. I am new to stackoverflow and it was unclear to me as to whether each successive problem should have its own dedicated question or be added to the end of an existing one when they are closely related. I have created a new question giving the link back to this one for those who want a more in-depth history of what's come before. – britechguy Aug 13 '17 at 15:29

2 Answers2

0

This activates the specific URL. The part that plays the sound is not necessary.

Option Explicit

Private Declare Function ShellExecute _
  Lib "shell32.dll" Alias "ShellExecuteA" ( _
  ByVal hWnd As Long, _
  ByVal Operation As String, _
  ByVal Filename As String, _
  Optional ByVal Parameters As String, _
  Optional ByVal Directory As String, _
  Optional ByVal WindowStyle As Long = vbMinimizedFocus _
  ) As Long

Private Declare Function sndPlaySound32 _
    Lib "winmm.dll" _
    Alias "sndPlaySoundA" ( _
        ByVal lpszSoundName As String, _
        ByVal uFlags As Long) As Long

Sub PlayTheSound(ByVal WhatSound As String)
    If Dir(WhatSound, vbNormal) = "" Then
        ' WhatSound is not a file. Get the file named by
        ' WhatSound from the Windows\Media directory.
        WhatSound = Environ("SystemRoot") & "\Media\" & WhatSound
        If InStr(1, WhatSound, ".") = 0 Then
            ' if WhatSound does not have a .wav extension,
            ' add one.
            WhatSound = WhatSound & ".wav"
        End If
        If Dir(WhatSound, vbNormal) = vbNullString Then
            Beep            ' Can't find the file. Do a simple Beep.
            Exit Sub
        End If
    Else
        ' WhatSound is a file. Use it.
    End If

    sndPlaySound32 WhatSound, 0&    ' Finally, play the sound.
End Sub

Public Sub OpenLinksMessage(olMail As Outlook.MailItem)

Dim Reg1 As RegExp
Dim AllMatches As MatchCollection
Dim M As Match
Dim strURL As String
Dim RetCode As Long

Set Reg1 = New RegExp

With Reg1
.Pattern = "(https?[:]//([0-9a-z=\?:/\.&-^!#$;_])*)"
.Global = True
.IgnoreCase = True
End With

PlayTheSound "chimes.wav"

' If the regular expression test for URLs in the message body finds one or more
If Reg1.test(olMail.Body) Then

'      Use the RegEx to return all instances that match it to the AllMatches group
       Set AllMatches = Reg1.Execute(olMail.Body)
       For Each M In AllMatches
               strURL = M.SubMatches(0)
'              Don't activate any URLs that are for unsubscribing; skip them
               If InStr(1, strURL, "unsubscribe") Then GoTo NextURL
'              If the URL ends with a > from being enclosed in darts, strip that > off
               If Right(strURL, 1) = ">" Then strURL = Left(strURL, Len(strURL) - 1)
'              The URL to activate to accept must contain both of the substrings in the IF statement
               If InStr(1, strURL, ".com") Then
                     PlayTheSound "TrainWhistle.wav"
'                    Activate that link to accept the job
                     RetCode = ShellExecute(0, "Open", "http://nytimes.com")
                     Set Reg1 = Nothing
                     Exit Sub
               End If

NextURL:
   Next

End If

Set Reg1 = Nothing

End Sub

Private Sub TestLaunchURL()
    Dim currItem As MailItem
    Set currItem = ActiveExplorer.Selection(1)
    OpenLinksMessage currItem
End Sub

"If I trigger the above code either by using the TestLaunch subroutine via the debugger, or create a rule and at the end say "Run on messages already in the inbox," it functions perfectly." britechguy

niton
  • 8,771
  • 21
  • 32
  • 52
0

The answer to this question, including the code for same, has been answered by me on a follow up question I'd asked: Why does this regular expression test give different results for what should be the same body text?

Please refer to the answer on that thread for the solution.

britechguy
  • 21
  • 7