trying to get some VBA code together to basically be able to run my rules from a button on my toolbar within outlook 2007. The following code runs the rules on my exchange server inbox, which is empty as everything moves to my "Personal Inbox". I just want to change the code below to read my personal inbox and not my exchange mailbox inbox. Have searched on the web and cant find my answer and hence my post -
Sub RunAllInboxRules()
Dim st As Outlook.Store
Dim myRules As Outlook.Rules
Dim rl As Outlook.Rule
Dim count As Integer
Dim ruleList As String
'On Error Resume Next
' get default store (where rules live)
Set st = Application.Session.DefaultStore
' get rules
Set myRules = st.GetRules
' iterate all the rules
For Each rl In myRules
' determine if it's an Inbox rule
If rl.RuleType = olRuleReceive Then
' if so, run it
rl.Execute ShowProgress:=True
count = count + 1
ruleList = ruleList & vbCrLf & rl.Name
End If
Next
' tell the user what you did
ruleList = "These rules were executed against the Inbox: " & vbCrLf & ruleList
MsgBox ruleList, vbInformation, "Macro: RunAllInboxRules"
Set rl = Nothing
Set st = Nothing
Set myRules = Nothing
End Sub
Try this. I have tested on my machine. This logs into the mailbox you are logged onto and runs the rules accordingly
Sub RunAllInboxRules()
Dim objOL As Outlook.Application
Dim st As Outlook.Store
Dim myRules As Outlook.Rules
Dim rl As Outlook.Rule
Dim count As Integer
Dim ruleList As String
Dim fldInbox As Object
Dim gnspNameSpace As Outlook.NameSpace
'On Error Resume Next
' get default store (where rules live)
'Logs into Outlook session
Set objOL = Outlook.Application
Set gnspNameSpace = objOL.GetNamespace("MAPI") 'Outlook Object
'Logs into the default Mailbox Inbox
'set the store to the mailbox
Set st = gnspNameSpace.GetDefaultFolder(olFolderInbox).Store
' get rules
Set myRules = st.GetRules
' iterate all the rules
For Each rl In myRules
' determine if it's an Inbox rule
If rl.RuleType = olRuleReceive Then
' if so, run it
rl.Execute ShowProgress:=True
count = count + 1
ruleList = ruleList & vbCrLf & rl.Name
End If
Next
' tell the user what you did
ruleList = "These rules were executed against the Inbox: " & vbCrLf & ruleList
MsgBox ruleList, vbInformation, "Macro: RunAllInboxRules"
Set rl = Nothing
Set st = Nothing
Set myRules = Nothing
End Sub
Related
My question is similar to but not the same to the one below,
Mark a mailitem as sent (VBA outlook)
Basically, something (AV, bug in Outlook or Exchange or both), has modified hundreds of incoming (external emails) to a particular user as drafts and now appear as unsent. This means the user cannot reply to these messages and the suggested alternative of copying and pasting looks very unprofessional and confusing to the user's clients. Thankfully whatever was causing it stopped but the damage is done.
I need some way to modify the PR_MESSAGE_FLAGS programmatically. I am comfortable with VB script, VBA, VB.Net and even C#/C++ but I am coming up empty for how to do it.
Should it matter, the server is Exchange 2013 and client is Outlook 2010 or 2016 (32 or 64bit). The entire mailbox has been exported to PST and can be worked on offline if that helps. :)
Based on Dmitry's answer, here is the code that clones the old messages and marks them as sent so they can be replied to.
Only concern with it is that it seems to be breaking Conversations.
Dim mysession
Sub doFixDrafts()
log " Starting scan!"
Set mysession = CreateObject("Redemption.RDOSession")
mysession.Logon
Const sRootFolder = "\\Mailbox\Inbox"
Set oRootFolder = mysession.getfolderfrompath(sRootFolder)
'Set oRootFolder = mysession.PickFolder
doCleanupFolder oRootFolder, sRootFolder
log "Scan complete!!"
End Sub
Sub doCleanupFolder(oFolder, sFolder)
Dim c: c = 0
Dim i: i = 0
Dim tc: tc = Format(oFolder.Items.Count, "0000")
'Get start timestamp so we can report in at regular intervals...
Dim st: st = Now()
log "Checking... " & sFolder
Dim aMsgIDs()
'Make a list of 'unsent' messages
For Each Item In oFolder.Items
i = i + 1
If Not Item.Sent Then
c = c + 1
msgID = Item.EntryID
ReDim Preserve aMsgIDs(1 To c)
aMsgIDs(c) = msgID
c = Format(c, "0000")
End If
'Give update for large folders...
ct = Now()
td = DateDiff("s", st, ct)
If td > 15 Then
log c & "/" & i & "/" & tc & " so far..."
st = ct
End If
DoEvents
Next
c = Format(c, "0000")
log c & "," & tc & "," & sFolder
'Fix the corrupt messages
For m = 1 To CInt(c)
Set badMsg = mysession.GetMessageFromID(aMsgIDs(m))
sSender = badMsg.Sender
sSubject = badMsg.Subject
dSentDate = badMsg.SentOn
Set newMsg = oFolder.Items.Add("IPM.Note")
newMsg.Sent = True
badMsg.CopyTo (newMsg)
newMsg.Save
badMsg.Delete
Dim a As String
a = Format(m, "0000") & "," & sSender & ","
a = a & Chr(34) & sSubject & Chr(34) & ","
a = a & Chr(34) & dSentDate & Chr(34)
log a
DoEvents
Next m
For Each Item In oFolder.Folders
doCleanupFolder Item, sFolder & "\" & Item.Name
Next
End Sub
Sub log(s As String)
d = Format(Now(), "yyyy-mm-dd hh:mm:ss")
t = d & " " & s
Debug.Print t
Const logfile = "c:\temp\fixdrafts.txt"
Open logfile For Append As #1
Print #1, t
Close #1
End Sub
The answer is still the same - on the low (Extended MAPI) level, sent/unsent status (MSGFLAG_UNSENT bit in the PR_MESSAGE_FLAGS property) can only be changed before the item is saved for the very first time.
Outlook Object Model is subject to the same limitation of course, and the only way to create an item in the sent state is to create a PostItem object - it is created in the sent state. You will then need to change the message class back to IPM.Note and remove the icon related properties to make sure the item looks right.
Redemption (I am its author) lets you change the item's state (RDOMail.Sent is read/write before the first call to Save).
It should be pretty easy to create copies of existing unsent messages in the sent state - loop through the problematic messages (it is better to avoid using "for each" if you will be creating new items in the same folder - your "for each" loop will start picking up new messages. Loop through the messages first and store their entry ids in a list or array), create new item using Redemption (RDOFolder.Items.Add), set the Sent property to true (RDOMail.Sent = true), open the problematic message by its entry ids (RDOSession.GetMessageFromID), copy the problematic message into the new message using RDOMail.CopyTo(AnotherRDOMailObject), call RDOMail.Save on the new message and RDOMail.Delete on the old message.
How to add multiple email addresses in CC list for VB Script send mail.
option explicit
' --------------------------------------------------------------------------
' -- Create Lotus Notes email (and add attachment) using VB Script
' --
' -- Version 1.01
' --
' -- Created by : Michael Green
' -- migreen#westpac.com.au
' --
' -- Based on in-complete/partially working script from :
' -- http://en.allexperts.com/q/Using-Lotus-Notes-1427/Creating-LotusNotes-email-using-1.htm
' --
' -- Created : 06/10/2009
' -- Last Updated: 07/10/2009
' --------------------------------------------------------------------------
Dim oSession ' AS NotesSession
Dim strServer
Dim strUserName
Dim strMailDbName
Dim oCurrentMailDb ' as NOTESDATABASE
Dim oMailDoc ' as NOTESDOCUMENT
Dim ortItem ' as NOTESRICHTEXTITEM
Dim ortAttacment ' as NOTESRICHTEXTITEM
Dim oEmbedObject ' as ????
dim cstrAttachment
Dim blAttachment
cstrAttachment = "c:\Temp\Telstra.xls"
blAttachment = True
' Start a session to notes
wscript.echo "## Connecting to Lotus Notes session..."
Set oSession = CreateObject("Notes.NotesSession")
wscript.echo("NotesVersion : " & oSession.NotesVersion)
wscript.echo("NotesBuildVersion: " & oSession.NotesBuildVersion)
wscript.echo("UserName : " & oSession.UserName)
wscript.echo("EffectiveUserName: " & oSession.EffectiveUserName)
wscript.echo "## GetEnvironmentString..."
strServer = oSession.GetEnvironmentString("MailServer",True)
wscript.echo("Server :" & strServer)
' eg. CN=Michael V Green/OU=CORPAU/OU=WBCAU/O=WBG
strUserName = oSession.UserName
strMailDbName = Left(strUserName, 1) & Right(strUserName, (Len(strUserName) - InStr(1, strUserName, "")))&".nsf"
wscript.echo("MailDbName :" & strMailDbName)
wscript.echo "## Getting current Notes database..."
' open the mail database in Notes
set oCurrentMailDb = oSession.CurrentDatabase
wscript.echo("fileName:" & oCurrentMailDb.fileName)
wscript.echo("filePath:" & oCurrentMailDb.filePath)
wscript.echo("server:" & oCurrentMailDb.server)
wscript.echo("Title:" & oCurrentMailDb.Title)
If oCurrentMailDb.IsOpen = True Then
' Already open for mail
wscript.echo "## Lotus Notes mail database is already open !"
Else
wscript.echo "## Opening Lotus Notes mail database..."
oCurrentMailDb.OPENMAIL
End If
' Create a document in the back end
Set oMailDoc = oCurrentMailDb.CREATEDOCUMENT
' Set the form name to memo
OMailDoc.form = "Memo"
with oMailDoc
.SendTo = "migreen#westpac.com.au"
.BlindCopyTo = "mgreen#ozemail.com.au"
.CopyTo = "migreen#westpac.com.au"
.Subject = "This is a test of VB scripting driving Lotus Notes 7 "
end with
set ortItem = oMaildoc.CREATERICHTEXTITEM("Body")
with ortItem
.AppendText("Test of RTF Item append")
.AddNewLine(2)
.AppendText("Signature")
End With
' Create additional Rich Text item and attach it
If blAttachment Then
Set ortAttacment = oMailDoc.CREATERICHTEXTITEM("Attachment")
' Function EMBEDOBJECT(ByVal TYPE As Short, ByVal CLASS As String, ByVal SOURCE As String, Optional ByVal OBJECTNAME As Object = Nothing) As Object
' Member of lotus.NOTESRICHTEXTITEM
Set oEmbedObject = ortAttacment.EMBEDOBJECT(1454, "", cstrAttachment, "Attachment")
End If
wscript.echo "## Sending email..."
with oMailDoc
.PostedDate = Now()
.SAVEMESSAGEONSEND = "True"
.send(false)
end with
wscript.echo "## Sent !"
' close objects
set oMailDoc = nothing
set oCurrentMailDb = nothing
set oSession = nothing
Just use an array to set the values
with oMailDoc
.SendTo = Array( "migreen#westpac.com.au", "mgreen#westpac.com.au", "green#westpac.com.au" )
.BlindCopyTo = "mgreen#ozemail.com.au"
.CopyTo = "migreen#westpac.com.au"
.Subject = "This is a test of VB scripting driving Lotus Notes 7 "
end with
Originally I did not want to comment on the quality of the copied code at all. But the discussion with Lankymart made me think, it would be good to comment on it.
Set oSession = CreateObject("Notes.NotesSession")
This line creates an OLE interface to a running Notes- Client. If client does not run, then it will be started. If you used Set oSession = CreateObject("Lotus.NotesSession") then it would have been a COM- Object you get. Be aware, that Some OLE- Methods do not work in COM and vice versa. e.g. oCurrentMailDb.OPENMAIL is OLE, while the same thing in COM would be oCurrentMailDb.OpenMailDatabase()
' eg. CN=Michael V Green/OU=CORPAU/OU=WBCAU/O=WBG
strUserName = oSession.UserName
strMailDbName = Left(strUserName, 1) & Right(strUserName, (Len(strUserName) - InStr(1, strUserName, "")))&".nsf"
Getting the users' mailfile is completely nonsense, the code will get everything but a correct filename. As the variable is not used at all - can be forgotten
set oCurrentMailDb = oSession.CurrentDatabase
Just gets the database that is currently open in the client. If no database is open, an error will be thrown in the next wscript.echo- line, and we will never get to the next lines where it checks, if a database is open...
The problem with this line: Sending mails is possible from ANY database in Lotus Notes. If the database that is open e.g. is the personal addressbook, then the mail will be saved and sent from there (and you will never find it in the Sent- View of your mailfile.
I would suggest to use OPENMAIL first and only do a fallback to the currently open database if that fails.
The rest of the code seems to be OK.
Create an array of email address strings and set CopyTo to that array:
Dim addresses (2)
addresses(0) = "EMAIL"
addresses(1) = "EMAIL"
addresses(2) = "EMAIL"
with oMailDoc
.SendTo = "migreen#westpac.com.au"
.BlindCopyTo = "mgreen#ozemail.com.au"
.CopyTo = addresses
.Subject = "This is a test of VB scripting driving Lotus Notes 7 "
end with
Ok, so it's not, but...
so this is a quick script I found on the internet which runs on my Exchange server and dumps a list of email addresses that I can use for recipient validation on a spam filter:
' Export all valid recipients (= proxyAddresses) into a
' file virtual.txt
'
' Ferdinand Hoffmann & Patrick Koetter
' 20021100901
' Shamelessly stolen from
' http://www.microsoft.com/windows2000/techinfo/ \
' planning/activedirectory/bulksteps.asp
'Global variables
Dim Container
Dim OutPutFile
Dim FileSystem
'Initialize global variables
Set FileSystem = WScript.CreateObject("Scripting.FileSystemObject")
Set OutPutFile = FileSystem.CreateTextFile("virtual.txt", True)
Set Container=GetObject("LDAP://CN=Users,DC=office,DC=example,DC=com")
'Enumerate Container
EnumerateUsers Container
'Clean up
OutPutFile.Close
Set FileSystem = Nothing
Set Container = Nothing
'Say Finished when your done
WScript.Echo "Finished"
WScript.Quit(0)
'List all Users
Sub EnumerateUsers(Cont)
Dim User
'Go through all Users and select them
For Each User In Cont
Select Case LCase(User.Class)
'If you find Users
Case "user"
'Select all proxyAddresses
Dim Alias
If Not IsEmpty(User.proxyAddresses) Then
For Each Alias in User.proxyAddresses
OutPutFile.WriteLine "alias: " & Alias
'WScript.Echo Alias
Next
End If
Case "organizationalunit" , "container"
EnumerateUsers User
End Select
Next
End Sub
The catch is that the list of recipients comes back like this:
smtp:user#local.lan
SMTP:user#publicdomain.com
x400:c=US;a= ;p=local;o=Exchange;s=lastname;g=firstname;
smtp:postmaster#publicdomain.com
smtp:webmaster#publicdomain.com
The spam filter has an import scrip that only imports lines with "smtp" or "SMTP" prefixed so the x400 isn't an issue. What is an issue is that I don't want the VBscript exporting the "user#local.lan" address. I've tried this:
'List all Users
Sub EnumerateUsers(Cont)
Dim User
'Go through all Users and select them
For Each User In Cont
Select Case LCase(User.Class)
'If you find Users
Case "user"
'Select all proxyAddresses
Dim Alias
If Not IsEmpty(User.proxyAddresses) Then
For Each Alias in User.proxyAddresses
If Not Alias = "*.lan" Then
OutPutFile.WriteLine "alias: " & Alias
WScript.Echo Alias
End If
Next
End If
Case "organizationalunit" , "container"
EnumerateUsers User
End Select
Next
End Sub
But, that doesn't do anything. I've tried matching for the public domain (If Alias = "publicdomain" Then) but that didn't produce any results.
So, how do I filter the output so I only get addresses ont he public domain?
Replace
If Not Alias = "*.lan"
with
If Right(Alias, 4) <> ".lan"
(It can be done with regular expressions but it's Friday and I'm tired!)
You could use a regular expression to filter out lines that don't match your criteria. Something like the following.
smtp:.*#publicdomain\.com
Alternatively you could also tweak your LDAP query to only return users of a certain OU. Is there an AD group that only users with exchange accounts belong in?
Here's the VBS for RegEx matching...
Dim s : s = "smtp:user#local.lan" & VBCRLF & _
"SMTP:user#publicdomain.com" & VBCRLF & _
"x400:c=US;a= ;p=local;o=Exchange;s=lastname;g=firstname;" & VBCRLF & _
"smtp:postmaster#publicdomain.com" & VBCRLF & _
"smtp:webmaster#publicdomain.com"
Dim ex : ex = "smtp:.*#publicdomain\.com"
Dim oRE: Set oRE = New RegExp
oRE.IgnoreCase = True
oRE.Global = True
oRE.Pattern = ex
Dim matches : Set matches = oRE.Execute(s)
For Each match In matches
WScript.Echo match.Value
Next
Following the howto Extending the Active Directory Schema To Track Custom Info I'm able to setup a single-value schema attribute that is easily changeable via a context menu in ADUC. Multi-value schema attributes get considerably more complicated. Say (for the sake of argument) my value is "Projects" and each user may be a list as many projects as necessary.
Following is a sad little script that will set Project to a single value:
Dim oproject
Dim oUser1
Dim temp1
Set oproject = Wscript.Arguments
Set oUser1 = GetObject(oproject(0))
temp1 = InputBox("Project: " & oUser1.project & vbCRLF & vbCRLF & "Project")
if temp1 <> "" then oUser1.Put "project",temp1
oUser1.SetInfo
Set oUser1 = Nothing
Set oproject = Nothing
Set temp1 = Nothing
WScript.Quit
How can I modify this to allow, assign, and modify multiple values?
I gave up on an elegant UI and just went with the semicolon delimited list. Here's the code if anyone cares:
Dim objProject
Dim objUser
Dim temp1, title, message, default
Dim projects
title = "Projects"
Set objProject = Wscript.Arguments
Set objUser = GetObject(objProject(0))
'Find our current projects
projects = objUser.projects
If Not isArray(projects) Then
projects = Array(projects)
End If
'Setup our message box
message = "Semicolon-delimited list of Projects"
default = arrayToStr(projects)
temp1 = InputBox(message, title, default)
'catch cancels
if IsEmpty(temp1) Then
WScript.Quit
End If
' update our data
projects = strToArray(temp1)
objUser.Put "projects",projects
objUser.SetInfo
'Clean up and quit
Set projects = Nothing
Set objUser = Nothing
Set objProject = Nothing
Set temp1 = Nothing
Set title = Nothing
Set message = Nothing
Set default = Nothing
WScript.Quit
'Functions
Function strToArray(s)
Dim a
Dim token
' discard blank entries
For Each token in split(s, ";")
token = trim(token)
If token <> "" Then
If isEmpty(a) Then
a = token
Else
a = a & ";" & token
End If
End If
Next
' return array
strToArray = split(a, ";")
End Function
Function arrayToStr(a)
Dim s
Dim token
For Each token in a
If isEmpty(s) Then
s = token
Else
s = s & ";" & token
End If
Next
' return string
arrayToStr = s
End Function
My marketing department, bless them, has decided to make a sweepstakes where people enter over a webpage. That is great but the information isn't stored to a DB of any sort but is sent to an exchange mail box as an email. Great.
My challenge is to extract the entry (and marketing info) from these emails and store them someplace more useful, say a flat file or CSV. The only saving grace is that the emails have a highly consistant format.
I am sure I could spend the time saving all the emails to files and then write an app to munge through them all but was hoping for a much more elegant solution. Can I programmatically access an exchange mailbox, read all the emails and then save that data?
Here is the code I used....
Private Sub btnGo_Click()
If ComboBox1.SelText <> "" Then
Dim objOutlook As New Outlook.Application
Dim objNameSpace As Outlook.NameSpace
Dim objInbox As MAPIFolder
Dim objMail As mailItem
//Get the MAPI reference
Set objNameSpace = objOutlook.GetNamespace("MAPI")
//Pick up the Inbox
Set objInbox = objNameSpace.GetDefaultFolder(olFolderInbox)
For Each objFolder In objInbox.Folders
If (objFolder.Name = ComboBox1.SelText) Then
Set objInbox = objFolder
End If
Next objFolder
//Loop through the items in the Inbox
Dim count As Integer
count = 1
For Each objMail In objInbox.Items
lblStatus.Caption = "Count: " + CStr(count)
If (CheckBox1.Value = False Or objMail.UnRead = True) Then
ProcessMailItem (objMail.Body)
count = count + 1
objMail.UnRead = False
End If
Next objMail
End If
End Sub
Private Sub ProcessMailItem(strBody As String)
Open "C:\file.txt" For Append As 1
Dim strTmp As String
strTmp = Replace(strBody, vbNewLine, " ")
strTmp = Replace(strTmp, vbCrLf, " ")
strTmp = Replace(strTmp, Chr(13) & Chr(10), " ")
strTmp = Replace(strTmp, ",", "_")
//Extra Processing went here (Deleted for brevity)
Print #1, strTmp
Close #1
End Sub
Private Function Strip(strStart As String, strEnd As String, strBody As String) As String
Dim iStart As Integer
Dim iEnd As Integer
iStart = InStr(strBody, strStart) + Len(strStart)
If (strEnd = "xxx") Then
iEnd = Len(strBody)
Else
iEnd = InStr(strBody, strEnd) - 1
End If
Strip = LTrim(RTrim(Mid(strBody, iStart, iEnd - iStart)))
End Function
Private Sub UserForm_Initialize()
Dim objOutlook As New Outlook.Application
Dim objNameSpace As Outlook.NameSpace
Dim objInbox As MAPIFolder
Dim objFolder As MAPIFolder
//Get the MAPI reference
Set objNameSpace = objOutlook.GetNamespace("MAPI")
//Pick up the Inbox
Set objInbox = objNameSpace.GetDefaultFolder(olFolderInbox)
//Loop through the folders under the Inbox
For Each objFolder In objInbox.Folders
ComboBox1.AddItem objFolder.Name
Next objFolder
End Sub
There's lots of different ways to get at the messages in an exchange mailbox, but since it seems this is something you're only going to want to run once to extract the data I'd suggest writing a VBA macro to run inside Outlook itself (having opened the exchange mailbox in question within Outlook). It's pretty easy to iterate through the mail items in a specific mailbox and read the body text from them. You can then write a text file with just the stuff you want.