Treating Item As MailItem - vba

I am creating an VBA application in Outlook 2016. It analyzes an incoming email and takes its subject line to search for duplicate (or close to duplicate) subject lines. I use a for-each loop to go through a list of Items (which are emails within the inbox) and analyze each one for the criteria.
Once a response is required, both the incoming email and the duplicate email are flagged so show that I have already responded to them.
I know both Item and olItem should both be Item objects. The problem I am having is in the line:
If InStr(1, GetPreceedingSubject(olItem.Subject), GetPreceedingSubject(SubjectString)) <> 0 _
And olItem.FlagRequest <> "Follow up" Then
It gives me the error
"Run-time error '438': Object doesn't support this property or method"
I know it is the olItem because it is the only part of the function that I had changed before I got the error. This strikes me as odd because the following snippet still works:
' flag both the emails that prompted the response
With Item
' due this week flag
.MarkAsTask olMarkThisWeek
' sets a specific due date
.TaskDueDate = Now + 3
.FlagRequest = "Follow up"
.FlagStatus = 2
.ReminderSet = False
.Save
End With
With olItem
' due this week flag
.MarkAsTask olMarkThisWeek
' sets a specific due date
.TaskDueDate = Now + 3
.FlagRequest = "Follow up"
.FlagStatus = 2
.ReminderSet = False
.Save
End With
So in the first code snippet, it appears that it is treating the olItem as an object, but in the next one it allows me to treat it like a MailItem object. Any suggestions? I have looked up ways to cast from Item to MailItem, even just temporarily for that line of code, but obviously to no avail. Full subroutine below:
Private Sub myOlItems_ItemAdd(ByVal Item As Object)
If ParsingEnabled = False Then
Exit Sub
End If
Dim SubjectString As String ' tracks the control word to search the subject line for
Dim pingCount As Integer ' tracks the number of copies found.
Dim TimeDiff As Double
Dim Protocol As Variant
Dim FlagStatus As Integer
pingCount = 0
SubjectString = Item.Subject ' searches subject line for this word
' If the email is a read receipt, then move it to a different folder
If TypeName(Item) = "ReportItem" Then
NullPrompt = MoveFolders(Item, "Read")
If NullPrompt >= 0 Then
setLblDebug ("Read receipt: " & Mid(SubjectString, 7, Len(SubjectString)))
Item.UnRead = False
Else
NullPrompt = setLblDebug("Error when moving read receipt. Please check inbox and correct", lngRed)
End If
End If
' Check to make sure it is an Outlook mail message, otherwise
' subsequent code will probably fail depending on what type
' of item it is.
If TypeName(Item) = "MailItem" Then
' display the message
setLblDebug ("Incoming Message: " & Item.Subject)
Item.UnRead = False ' mark message as read
' Iterate through each item of the list
For Each olItem In myOlItems
If InStr(1, GetPreceedingSubject(olItem.Subject), GetPreceedingSubject(SubjectString)) <> 0 _
And olItem.FlagRequest <> "Follow up" Then
Protocol = ProtocolCode(Item.Subject)
If Protocol(0) <> 0 Then
' Time difference between the 2 emails
TimeDiff = (Item.ReceivedTime - olItem.ReceivedTime) * 24 ' Gives the hour difference
' If time difference is 0, then it is the same email
If Protocol(0) >= TimeDiff And TimeDiff <> 0 Then
' flag both the emails that prompted the response
With Item
' due this week flag
.MarkAsTask olMarkThisWeek
' sets a specific due date
.TaskDueDate = Now + 3
.FlagRequest = "Follow up"
.FlagStatus = 2
.ReminderSet = False
.Save
End With
With olItem
' due this week flag
.MarkAsTask olMarkThisWeek
' sets a specific due date
.TaskDueDate = Now + 3
.FlagRequest = "Follow up"
.FlagStatus = 2
.ReminderSet = False
.Save
End With
' email and call if required
RenderMail (olItem)
If Protocol(1) = 1 Then
NullPrompt = RenderCallPrompt(olItem.Subject, Item.ReceivedTime)
End If
' set the debug prompt message
NullPrompt = setLblDebug("Response Made: " & Item.Subject & " [" & Item.ReceivedTime & "]", lngBlue)
If True Then Exit For ' Reponse made, stop looking for additional emails
End If
End If
End If
Next olItem
End If
End Sub

You cannot treat an Object which is not a MailItem as a MailItem.
MailItem is a subset of Object. Object encompasses TaskItem, AppointmentItem and others.
Other types will not necessarily have the properties of a MailItem.
In your code:
' Check to make sure it is an Outlook mail message, otherwise
' subsequent code will probably fail depending on what type
' of item it is.
If TypeName(Item) = "MailItem" Then
Add the same test to ensure olItem is a MailItem.
For Each olItem In myOlItems
' Check to make sure it is an Outlook mail message, otherwise
' subsequent code will probably fail depending on what type
' of item it is.
If TypeName(olItem) = "MailItem" Then
'
End If
Next olItem

Related

VBA Outlook Email, update current/selected field before sending

I wrote a macro, where the starting time of a meeting should be entered into the "Subject" Field of a meeting and the mail will be automatically send right after.
When I start the macro through a button and the last selected field like Subject or Start Time is selected and changed, the email will be send, but with the old data.
For example:
My last input to the email was the text "Test" in the empty Subject field. After that I send the email, through the button.
The email is sent, but the subject field remains empty.
Is there a way to update fields like subject and starttime?
I tried to use commands like update, SendKeys "{TAB}", myItem.Close olDiscard to close and update the field and send it right after.
Sub startTimeSend()
On Error GoTo HandleErr
Dim myItem As Object
Set myItem = Application.ActiveInspector.CurrentItem
Dim oldTitle As String
Dim startTime As String
Dim scanForOldNr As String
Dim newStartTimeFormat As String
' olPromptForSave
' SendKeys "{ENTER}"
' SendKeys "{ENTER}", True
' Application.SendKeys ("{ENTER}")
oldTitle = myItem.Subject
startTime = myItem.Start
' MsgBox (oldTitle)
' scanForOldNr contains third char (usually ":")
scanForOldNr = Mid(oldTitle, 3, 1)
If scanForOldNr Like "*:*" Then
' 7 da es von 1 hochzählt nicht null
' MsgBox (scanForOldNr)
oldTitle = Mid(oldTitle, 7)
End If
' Cancel = True
newStartTimeFormat = Format(startTime, "hh:mm")
myItem.Subject = newStartTimeFormat & " " & oldTitle
myItem.Send
ExitHere:
Exit Sub
HandleErr:
' Cancel = False
Resume ExitHere
End Sub
Outlook doesn't propagate changes made via OOM to the UI until you close and re-open the item. Or your changes made manually may not be visible in the OOM until the item is saved. I'd suggest dealing with proper OOM properties instead of inventing new mechanisms for sending and receiving data.
Use the AppointmentItem.Start property which returns or sets a Date indicating the starting date and time for the Outlook item. To get the associated appointment you can use the MeetingItem.GetAssociatedAppointment method.

Getting "object variable or with block variable not set" within Outlook VBA sometimes

I have a fairly straightforward VBA script that accepts meeting requests in Outlook (2013). It works fine most of the times, but for some meeting requests it gives me "object variable or with block variable not set" on this line
Set oResponse = oAppt.Respond(olMeetingAccepted, True)
I've tried to look at the different meeting requests to figure out any differences which might be triggering it but for all purposes they look like identical requests (of course they're from different senders with different subject and time, but nothing that I can see as triggering a fail).
Any thoughts what might be going on? Here's the complete Sub (it gets triggered by an outlook rule)
Sub AutoAcceptMeetings(oRequest As MeetingItem)
Dim senderName As String
Dim subjectName As String
Dim meetingTime As String
Dim senderContains As Integer
Dim subjectContains As Integer
Dim oResponse As MeetingItem
Dim oAppt As AppointmentItem
On Error GoTo debugs
If oRequest.MessageClass <> "IPM.Schedule.Meeting.Request" Then
Exit Sub
End If
Set oAppt = oRequest.GetAssociatedAppointment(True)
senderName = oRequest.senderName
subjectName = oRequest.Subject
senderContains = InStr(1, senderName, "Gina")
'Her meeting invitations don't have a reminder set.
If (senderContains > 0) Then
oAppt.ReminderSet = True
oAppt.ReminderMinutesBeforeStart = 15
End If
senderContains = InStr(1, senderName, "Jim")
If (senderContains > 0) Then 'I don't want a reminder
oAppt.ReminderSet = False
oAppt.BusyStatus = olTentative
oAppt.Save
Else 'useful meetings. accept and send response.
meetingTime = oAppt.Start
Set oResponse = oAppt.Respond(olMeetingAccepted, True)
oResponse.Send
MsgBox ("Meeting accepted " + subjectName + " from " + senderName + " for " + meetingTime)
End If
debugs:
If Err.Description <> "" Then MsgBox (Err.Description + " - Source: AutoAcceptMeetings")
End Sub
So, the issue was that I was trying to send a "response" to a meeting request that didn't require any responses. On the front end (outlook) if you accept the meeting, it goes into your calendar and the request gets deleted but no response goes to the organizer (people do that when they invite a large group and don't necessarily care to know who's attending).
Anyway, the solution for me was to put in a simple check before actually responding to the request.
If (oAppt.ResponseRequested) Then
Set oResponse = oAppt.Respond(olMeetingAccepted, True)
oResponse.Send
End If

Detect if the last selected email has been marked as read and prompt to save

My outlook VBA code aims at doing the following:
In the event of selection change (i.e. say the user clicks on a different email in the inbox)
If [the previously selected email was originally 'Unread' and just became 'Read'] Then
Prompt the user to save the previous email
Else
Do Nothing
End If
To do that I used the Explorer_SelectionChange event. The problem I'm facing is that outlook takes about 1 to 2 seconds before it marks the previous email as read! My code gets executed before these 2 seconds pass. Hence it always sees the previous email as unread! :(
I tried to introduce a pause to my Sub but it didn't work. Outlook waits until my code finishes including the pause before it in turn waits 1 to 2 seconds and then mark the previous email as read.
So in summary my question is: Is there an Event that identifies when the previously selected email is marked as Read?? (PS: I tried MailItem.Read Event but it is also instantaneous and applies to all 'read and unread' emails]
Here is the part of my code that specifically tries to achieve the above described functionality:
Public WithEvents myOlExp As Outlook.Explorer
Dim Flag As Integer
Dim oMail As Outlook.MailItem
Private Sub Application_Startup()
Dim objItem As Object
Set myOlExp = Application.ActiveExplorer
enviro = CStr(Environ("USERPROFILE"))
'Identify the status of the selected email at startup
For Each objItem In myOlExp.Selection
If objItem.MessageClass = "IPM.Note" Then
Set oMail = objItem
End If
Next
If oMail.UnRead Then
Flag = 1 'Means Current selection is an unread email
Else
Flag = 0 'Means Current selection has been read before
End If
End Sub
Private Sub myOlExp_SelectionChange()
'If previous selected email was Unread
If Flag = 1 Then
If oMail.UnRead = False Then
MsgBox "previous email has just been read do you want to save?"
'^^This is where the problem happens: the previously selected email is always seen as read by the code
'because Outlook takes 1-2 seconds after the selection change event before it marks the email as read!!
Else
MsgBox "Previous email still marked as unread, do nothing"
'^^I am always getting this outcom when I change selection from an unread email to another email!
End If
'Now identify the status of the newly selected email
For Each objItem In myOlExp.Selection
If objItem.MessageClass = "IPM.Note" Then
Set oMail = objItem
End If
Next
If oMail.UnRead Then
Flag = 1 'Means Current selection is an unread email
Else
Flag = 0 'Means Current selection has been read before
End If
Else
' Flag = 0 i.e previous email was already read
' Identify the status of the newly selected item.
For Each objItem In myOlExp.Selection
If objItem.MessageClass = "IPM.Note" Then
Set oMail = objItem
End If
Next
If oMail.UnRead Then
Flag = 1
Else
Flag = 0
End If
End If
End Sub
I hope I managed to formulate my question clearly! Any help is most appreciated.
Many Thanks
Once you set Flag = 1, oMail.UnRead status does not matter.
If Flag = 1 Then
' Remove this test
'If oMail.UnRead = False Then
MsgBox "...

Auto-append an autonumbered TaskID to Outlook Exchange (shared) mailbox

Very similar requirement at first glance to Luke123's 'Append a Tag to Outlook' request.
Here, I need to append an autonumber Task ID (which is specific only to this requirement) into the Subject line of an Outlook Exchange (shared) mailbox.
This therefore needs to a) autonumber sequentially and b) run automatically as the e-mails land.
Pretty certain rules for shared mailboxes run server-side and are locked down by the business.
All ideas/help gratefully received.
This code will most likely need to be tweaked but should do what you want. You may need to take the existing Inbox items and give them a task ID in order to get the ball rolling. See my comments for explanation of the code as well as places you need to edit.
Private WithEvents Items As Outlook.Items
Private Sub Application_Startup()
Dim olApp As Outlook.Application
Set olApp = Outlook.Application
' edit this line to reflect the actual mailbox name as displayed in Outlook
Set Items = Session.Folders("Mailbox - My Shared Mailbox").Folders("Inbox")
End Sub
Private Sub Items_ItemAdd(ByVal item As Object)
On Error GoTo ErrorHandler
Dim msg As Outlook.mailItem
Dim firstObj As Object
Dim i As Long
Dim firstMsg As Outlook.mailItem
Dim currentTaskID As String
Dim nextTaskID As Long
If TypeName(item) = "MailItem" Then ' it's an email
Set msg = item
' get first email from Inbox to determine next task ID
Do Until TypeName(firstObj) = "MailItem" Or i = Session.Folders("Mailbox - My Shared Mailbox").Folders("Inbox").Items.Count
i = i + 1
' might have to start at item #2?
Set firstObj = Session.GetDefaultFolder(olFolderInbox).Items(i)
Loop
' typecast the object to mailitem for Intellisense
If TypeName(firstObj) = "MailItem" Then
Set firstMsg = firstObj
Else
' display messagebox?
Goto ProgramExit
End If
' get task id and calculate next value, let's assume it's the last three chars of subject
' Ex: Subject: Incoming Email - TaskId: 001
currentTaskID = Right$(firstMsg.Subject, 3)
nextTaskID = CLng(currentTaskID) + 1
' put next task ID number into new email's subject
With msg
.Subject = msg.Subject & " - TaskId: " & nextTaskID
.Save
End With
End If
ProgramExit:
Exit Sub
ErrorHandler:
MsgBox Err.Number & " - " & Err.Description
Resume ProgramExit
End Sub

Forward a highlighted email to a specific recipient

How do I forward a highlighted email to a specific recipient?
Is it possible to link that to a CTRL-ALT- shortcut?
I'm on Windows 7, Outlook 2010
Ctrl+Alt does not appear to be a feature of Outlook 2003 so I cannot comment on it. I can run the following macro by adding the macro to a toolbar using Tools, Customise.
This macro will forward all selected items. If you want to ensure only one item is selected, test SelectedItems.Count. If the current folder is empty, nothing happens because SelectedItems.Count = 0
Sub ForwardSelectedItems()
Dim Inx As Integer
Dim NewItem As MailItem
Dim SelectedItems As Outlook.Selection
Set SelectedItems = ActiveExplorer.Selection
For Inx = 1 To SelectedItems.Count
Set NewItem = SelectedItems.Item(Inx).Forward
With NewItem
' ########## Remove following block if attachments are to be sent.
' Delete any attachments.
With .Attachments
While .Count > 0
.Remove 1
Wend
End With
' ########## Remove above block if attachments are to be sent.
With .Recipients
' Add new recipient. Note Forward deletes existing recipients.
.Add "my.friend#isp.com"
End With
' Choices ######
'.Display ' Show to user and let them send if they wish
.Send ' Send automatically without showing to user
' End of choices ######
End With
Next
End Sub
Forward a highlighted email to a specific recipient
Return the currently highlighted email:
GetCurrentItem will return the currently selected or opened email to the calling procedure.
Function GetCurrentItem() As Object
' returns reference to current item, either the one
' selected (Explorer), or the one currently open (Inspector)
Select Case True
Case IsExplorer(Application.ActiveWindow)
Set GetCurrentItem = ActiveExplorer.Selection.item(1)
Case IsInspector(Application.ActiveWindow)
Set GetCurrentItem = ActiveInspector.CurrentItem
End Select
End Function
Function IsExplorer(itm As Object) As Boolean
IsExplorer = (TypeName(itm) = "Explorer")
End Function
Function IsInspector(itm As Object) As Boolean
IsInspector = (TypeName(itm) = "Inspector")
End Function
Forward the selected email:
This uses the above functions to forward and display an email. The rest is up to you.
Sub FwdMail()
Dim obj As Object
Dim msg As Outlook.MailItem
Set obj = GetCurrentItem
If TypeName(obj) = "MailItem" Then
Set msg = obj
With msg
.Forward
.Display
End With
End If
End Sub