问题
Here's the pertinent code, which is giving different results on the regular expression test for the message body depending on whether I launch it using TestLaunchURL or the message is passed to it by Outlook when an incoming message arrives:
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 "Speech On.wav"
RetCode = Reg1.Test(olMail.Body)
MsgBox "The RetCode from Reg1.Test(olMail.Body) equals" + Str(RetCode)
' If the regular expression test for URLs in the message body finds one or more
If RetCode Then
PlayTheSound "chimes.wav"
' 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
PlayTheSound "tada.wav"
If InStr(1, strURL, ".com") Then
PlayTheSound "TrainWhistle.wav"
' Activate that link to accept the job
RetCode = ShellExecute(0, "Open", strURL)
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
The test IF Reg1.Test(olMail.Body) always returns a 0 when invoked from an Outlook rule on an incoming message and always returns a -1 when I use the debugger to trigger it for that same message from my inbox.
The code is acting almost as though it has a null message body when it is triggered by an Outlook rule versus having the message body when kicked off by me from exactly the same message once it's in my inbox.
I am completely flummoxed, as I can't understand how one and the same message, with one and the same body, can give 2 different results depending on who hands the message to the subroutine.
Additional Debugging Information: Since the issue appears to surround the value of the Body of the message, I added the following code, that also examines the HTMLBody as well:
If IsNull(olMail.Body) Then
MsgBox "The message body is null!!"
Else
MsgBox "BODY: " + "|" + olMail.Body + "|"
End If
If IsNull(olMail.HTMLBody) Then
MsgBox "The message HTMLbody is null!!"
Else
MsgBox "BODY: " + "|" + olMail.HTMLBody + "|"
End If
When the script is triggered by the Outlook rule on a message with the content, and only the content, "http://britishtoolworks.com", when it arrives these are the two message boxes:
[I am being forbidden to post images for some reason. These show absolutely nothing between the two pipe characters for BODY and some text, but nothing with the URL in it, for the HTMLBody]
while these are the message boxes if I trigger the script via TestLaunchURL after that very same message is sitting in my inbox:
[Shows the actual expected content. I am forbidden from posting more images.]
If anyone can explain this discrepancy, please do.
回答1:
Here is the code that finally works. It's clear that the .Body member of olMail is not available until some sort of behind the scenes processing has had time to occur and if you don't wait long enough it won't be there when you go to test using it. Focus on the Public Sub OpenLinksMessage which is where the problem had been occurring.
The major (and only) change that allowed the expected processing of olMail.Body to take place, apparently, was the addition of the line of code: Set InspectMail = olMail.GetInspector.CurrentItem. The time it takes for this set statement to run allows the .Body to become available on the olMail parameter that's passed in by the Outlook rule. What's interesting is that if you immediately display InspectMail.Body after the set statement it shows as empty, just like olMail.Body used to.
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
Public Sub OpenLinksMessage(olMail As Outlook.MailItem)
Dim InspectMail As Outlook.MailItem
Dim Reg1 As RegExp
Dim AllMatches As MatchCollection
Dim M As Match
Dim strURL As String
Dim SnaggedBody As String
Dim RetCode As Long
' The purpose of the following Set statement is strictly to "burn time" so that the .Body member of
' olMail is available by the time it is needed below. Without this statement the .Body is consistently
' showing up as empty. What's interesting is if you use MsgBox to display InspectMail.Body immediately after
' this Set statement it shows as empty.
Set InspectMail = olMail.GetInspector.CurrentItem
Set Reg1 = New RegExp
With Reg1
.Pattern = "(https?[:]//([0-9a-z=\?:/\.&-^!#$;_])*)"
.Global = True
.IgnoreCase = True
End With
RetCode = Reg1.Test(olMail.Body)
' If the regular expression test for URLs in the message body finds one or more
If RetCode 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
' Activate that link to accept the job
RetCode = ShellExecute(0, "Open", strURL)
Set InspectMail = Nothing
Set Reg1 = Nothing
Set AllMatches = Nothing
Set M = Nothing
Exit Sub
End If
NextURL:
Next
End If
Set InspectMail = Nothing
Set Reg1 = Nothing
Set AllMatches = Nothing
Set M = Nothing
End Sub
Special thanks to niton for his patience and assistance on other questions that formed the basis for this one. He led me to the solution.
Addendum: Another individual assisting me elsewhere brought up something that deserves noting here, as I think she's got it right. I am using Gmail via IMAP access to download my messages. What appears to be happening is that once the header information is populated into the MailItem object, the Outlook Rule is immediately being triggered. The rest of the members of that object, including .Body, appear to be being populated asynchronously behind the scenes. The speed of processing in your script versus the speed of population processing can lead to situations where the script is triggered with the header information and gets to the point where it accesses the .Body before it's been populated by Outlook itself. What's interesting is when this occurred, and that was most of the time until this solution was found, .Body was not considered to be NULL. The IsNull test never passed, but the content when printed was nothing, as in absolutely nothing between the two pipe characters I used as delimiters. What is "nothing that takes up any characters" but that also is not NULL?
Clearly the whole MailItem passed would not pass the "Is Nothing" test, and I would not think to test an individual member of an object with "Is Nothing."
For myself, I consider this to be buggy. Before a MailItem object is ever handed off for script processing it would be the logical presumption that all Members of that object that can be prepopulated will be prepopulated by Outlook before the handoff. It just doesn't appear to be happening that way, and this is under Outlook 2010 on my machine and Outlook 2016 on another. If you get a member that has not yet been populated it should always have the NULL value, as that should be what everything is initialized to prior to the population process taking place.
来源:https://stackoverflow.com/questions/45682765/why-does-this-regular-expression-test-give-different-results-for-what-should-be