retrieving email address from outlook through userform - excel

I would like to ask if there is a way to retrieve email address from outlook address book from the values enter in user form textbox and retrieve it.
For example, my textbox1 have user entering the full names of people they want to search and with a search button, textbox2 will have all the email address retrieve from outlook address book based on textbox1.
Currently what I have is, a module call retrieve email
Option Explicit
Sub GetAddresses()
Dim o, AddressList, AddressEntry
Dim c As Range, r As Range, AddressName As String
Set o = CreateObject("Outlook.Application")
Set AddressList = o.Session.AddressLists("Contacts")
'Chage this range to include the first names only. AddressName assignment line handles concatenating the last name.
Set r = Add.Emailname
For Each c In r
AddressName = c.Value & " " & c.Offset(0, 1).Value
For Each AddressEntry In AddressList.AddressEntries
If AddressEntry.name = AddressName Then
c.Offset(0, 2).Value = AddressEntry.Address
Exit For
End If
Next AddressEntry
Next c
End Sub
And in my user form, the search button
Private Sub Searchbutton_Click()
Call GetAddresses
End Sub
The code is what I have seen from online. Can anyone help me edit and guide me?

I see you have copied your code. This code is meant to loop around a range. You could simply remove the loop and inmplement your textbox value and assign it to your searchbutton. However you'll need a .ListBox1 (instead of .TextBox2) since you wanted to have all hits in contact to appear in a list. I've also implemented the .Instr function to see if the searchvalue appears in the contacts name (using LCase to be sure). This would making a search on just a lastname way easier. The code then would look like:
Option Explicit
Private Sub GetAddresses()
Dim o, AddressList, AddressEntry
Dim AddressName As String
Set o = CreateObject("Outlook.Application")
Set AddressList = o.Session.AddressLists("Contacts")
'Change this range accordingly
AddressName = UserForm1.TextBox1.Value
For Each AddressEntry In AddressList.AddressEntries
If InStr(1, LCase(AddressEntry.Name), LCase(AddressName)) <> 0 Then
UserForm1.ListBox1.AddItem AddressEntry.Address
End If
Next AddressEntry
End Sub
Private Sub Searchbutton_Click()
UserForm1.ListBox1.Clear
Call GetAddresses
End Sub

Related

How to call MailItem.Display method when clicking on a table cell?

I need to call the Outlook MailItem.Display method when clicking on a specific cell in a table column in Excel.
Below is my module for filling out table.
' This module performs email retrieval and viewing. Dynamically adds email information to a table and creates
' links that open Outlook mailitems in a modal window.
Option Explicit
'Initialize Outlook objects
Dim appOL, appNS, appFolder, email As Object
'initialize ListObject
Dim tbl As ListObject
'Add email information to tbl_email_data
Public Sub addDataToEmailTable()
'GetDefaultFolder(6) is "Inbox" of whoever is signed into Outlook desktop version.
'Does not account for subfolders in Inbox and does not work with Web Outlook version.
Set appOL = CreateObject("Outlook.Application")
Set appNS = appOL.GetNamespace("MAPI")
Set appFolder = appNS.GetDefaultFolder(6)
'initialize table
Set tbl = ThisWorkbook.Worksheets("Email").ListObjects("tbl_email_data")
Dim rowCount As Long
rowCount = 1
If tbl.DataBodyRange Is Nothing Then
tbl.ListRows.Add
End If
'loop through emails and put information into tbl_email_data
For Each email In appFolder.Items
If email.Unread = True Then
tbl.DataBodyRange.Cells(rowCount, 1).Value2 = "Unread"
Else
tbl.DataBodyRange.Cells(rowCount, 1).Value2 = "Read"
End If
tbl.DataBodyRange.Cells(rowCount, 2).Value2 = email.SenderName
tbl.DataBodyRange.Cells(rowCount, 3).Value2 = email.SentOn
tbl.DataBodyRange.Cells(rowCount, 4).Value2 = email.Subject
rowCount = rowCount + 1
Next email
End Sub
I was going to create a userform with a comboBox so when selected a textbox fills with item.body.
This does not account for embedded images, and HTML formatted messages.
I saw Outlook has a method for mailitem that opens the email directly without exiting Excel.
So I figured out how to call the MailItem.Display method based on a table that represents email information in the Inbox folder of Outlook. Lots of trial and error but I got it to work. Below is the full code for the module that will handle all of this.
Option Explicit
Public excelInbox As Collection
Dim appOL, appNS, appInbox, appItem As Object
Public isOnline As Boolean
Public Function checkConnection(status As Boolean)
Set appOL = CreateObject("Outlook.Application")
Set appNS = appOL.GetNameSpace("MAPI")
If appNS.Offline = True Then
MsgBox "Outlook account is not connected to Exchange server. Please verify network connection to get updated Inbox preview"
status = False
Set appNS = Nothing
Set appOL = Nothing
Else
MsgBox "Outlook account is online"
status = True
End If
Set appInbox = appNS.GetDefaultFolder(6)
Set excelInbox = New Collection
End Function
Public Sub makeExcelInbox()
Call checkConnection(isOnline)
If isOnline <> True Then Exit Sub
Set appInbox = appNS.GetDefaultFolder(6) '6 is the enumeration for Inbox root folder in Outlook.
'loop and place only emails into excel Inbox.
For Each appItem In appInbox.Items
If appItem.Class = 43 Then excelInbox.Add appItem '43 represents a mail item in Outlook.
Next appItem
End Sub
Public Sub makeEmailPreviewTable()
Call makeExcelInbox
If excelInbox.Count <> 0 Then
Dim tbl As ListObject
Dim rowCount As Integer
Set tbl = ws_email.ListObjects("tbl_emailData")
rowCount = 1
For Each appItem In excelInbox
If appItem.Unread = True Then
tbl.DataBodyRange.Cells(rowCount, 1).Value2 = "Unread"
Else
tbl.DataBodyRange.Cells(rowCount, 1).Value2 = "Read"
End If
tbl.DataBodyRange.Cells(rowCount, 2).Value2 = appItem.SenderName
tbl.DataBodyRange.Cells(rowCount, 3).Value2 = appItem.SentOn
tbl.DataBodyRange.Cells(rowCount, 4).Value2 = appItem.Subject
rowCount = rowCount + 1
Next appItem
Set tbl = Nothing
ElseIf excelInbox.Count = 0 Then MsgBox "No messages to show in Inbox Preview."
End If
End Sub
Public Function getEmailForDisplay(Target As Range)
'Call makeExcelInbox
For Each appItem In excelInbox
If Target.Value = appItem.Subject Then appItem.Display
Next appItem
End Function
I used the selection change event in the worksheet that has the table to pass the target range value to a function that checks if that value is the same as the subject property of an email in the inbox. It is not the prettiest code, but for any others that come across this with a similar problem this should at least get you on the right path. Here is the worksheet code for event below.
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'Check for selection of a cell in tbl_emailData column Subject, then pass the value to a function.
Dim rng As Range
Dim tableRange As Range
Set tableRange = ListObjects("tbl_emailData").DataBodyRange
Dim rowCount As Long
rowCount = 1
If Intersect(Target, tableRange) Is Nothing Then Exit Sub
'check for valid target location
For Each rng In tableRange
On Error GoTo ErrorHandler
If Target = tableRange(rowCount, 4) Then
Call getEmailForDisplay(Target)
Else
rowCount = rowCount + 1
End If
Next rng
ErrorHandler:
Exit Sub
End Sub
Just an important note, I am still designing this program so if you sample the code you have to make sure you have a table called "tbl_emailData" and a worksheet called "ws_email". Then when you want to run the code, make sure to run the sub "makeEmailPreviewTable" first. In my design the worksheets and cells will all be locked so only the subject column cells will be selectable, this prevents run-time errors in case the user selects more than one cell.
Update: Added errorhandling to the selection event to ignore multi-selection errors. This will ignore, and then when a proper cell is selected then display the email in a Outlook modal.

Send one report to each manager using VBA and Outlook

I have a list which contains:
- Clients
- Manager e-mail
- Head manager e-mail
I'm trying to send e-mails using VBA and Outlook, in a way that each time the loop finds one manager (I'm checking for an e-mail address), it sends every client listed for that manager.
If a branch has no manager e-mail address listed, the e-mail should go to the Head Manager (branch 1236, for example, would receive one e-mail, to the Head Manager, with several clients).
The e-mail body will contain pre formatted text, and after that the sheet list with the client list.
I'm having some troubles:
a) to list the branch's clients from the sheet to the mail body
b) to jump from the next manager after the first e-mail, instead of repeating the e-mail for the same manager every time the loop finds the same manager
c) logging the mail sent on the J column
This is a sheet with some of the report:
https://drive.google.com/file/d/1Qo-DceY8exXLVR7uts3YU6cKT_OOGJ21/view?usp=sharing
My loop works somewhat, but I believe I need some other approach to achieve this.
Private Sub CommandButton2_Click() 'envia o email com registro de log
Dim OutlookApp As Object
Dim emailformatado As Object
Dim cell As Range
Dim destinatario As String
Dim comcopia As String
Dim assunto As String
'Dim body_ As String
Dim anexo As String
Dim corpodoemail As String
'Dim publicoalvo As String
Set OutlookApp = CreateObject("Outlook.Application")
'Loop para verificar se o e-mail irá para o gerente da carteira ou para o gerente geral
For Each cell In Sheets("publico").Range("H2:H2000").Cells
If cell.Row <> 0 Then
If cell.Value <> "" Then 'Verifica se carteira possui gerente.
destinatario = cell.Value 'Email do gerente da carteira.
Else
destinatario = cell.Offset(0, 1).Value 'Email do Gerente Geral.
End If
assunto = Sheets("CAPA").Range("F8").Value 'Assunto do e-mail, conforme CAPA.
'publicoalvo = cell.Offset(0, 2).Value
'body_ = Sheets("CAPA").Range("D2").Value
corpodoemail = Sheets("CAPA").Range("F11").Value & "<br><br>" & _
Sheets("CAPA").Range("F13").Value & "<br><br>" ' & _
Sheets("CAPA").Range("F7").Value & "<br><br><br>"
'comcopia = cell.Offset(0, 3).Value 'Caso necessário, adaptar para enviar email com cópia.
'anexo = cell.Offset(0, 4).Value 'Caso necessário, adaptar para incluir anexo ao email.
'Montagem e envio dos emails.
Set emailformatado = OutlookApp.CreateItem(0)
With emailformatado
.To = destinatario
'.CC = comcopia
.Subject = assunto
.HTMLBody = corpodoemail '& publicoalvo
'.Attachments.Add anexo
'.Display
End With
emailformatado.Send
Sheets("publico").Range("J2").Value = "enviado"
End If
Next
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Have a manager class that has a collection of clients. Have a collection of manager instances.
Manager Class
'#Folder("VBAProject")
Option Explicit
Private Type TManager
ManagerEmail As String
Clients As Collection
End Type
Private this As TManager
Private Sub Class_Initialize()
Set this.Clients = New Collection
End Sub
Private Sub Class_Terminate()
Set this.Clients = Nothing
End Sub
Public Property Get ManagerEmail() As String
ManagerEmail = this.ManagerEmail
End Property
Public Property Let ManagerEmail(ByVal value As String)
this.ManagerEmail = value
End Property
Public Property Get Clients() As Collection
Set Clients = this.Clients
End Property
Client Class
'#Folder("VBAProject")
Option Explicit
Private Type TClient
ClientID As String
End Type
Private this As TClient
Public Property Get ClientID() As String
ClientID = this.ClientID
End Property
Public Property Let ClientID(ByVal value As String)
this.ClientID = value
End Property
Standard Module
Option Explicit
Dim Managers As Collection
Sub PopulateManagers()
Set Managers = New Collection
Dim currWS As Worksheet
Set currWS = ThisWorkbook.Worksheets("publico")
With currWS
Dim loopRange As Range
Set loopRange = .Range(.Cells(2, 8), .Cells(.UsedRange.Rows.Count, 8)) 'H2 to the last used row; assuming it's the column for manager emails
End With
Dim currCell As Range
For Each currCell In loopRange
If currCell.value = vbNullString Then 'no manager; try for a head manager
If currCell.Offset(0, 1).value = vbNullString Then 'no managers at all
Dim currManagerEmail As String
currManagerEmail = "NoManagerFound"
Else
currManagerEmail = currCell.Offset(0, 1).Text
End If
Else
currManagerEmail = currCell.Text
End If
Dim currManager As Manager
Set currManager = Nothing
On Error Resume Next
Set currManager = Managers(currManagerEmail)
On Error GoTo 0
If currManager Is Nothing Then
Set currManager = New Manager
currManager.ManagerEmail = currManagerEmail
Managers.Add currManager, Key:=currManager.ManagerEmail
End If
Dim currClient As Client
Set currClient = New Client
currClient.ClientID = currWS.Cells(currCell.Row, 1).Text 'assumes client ID is in column 1
currManager.Clients.Add currClient, Key:=currClient.ClientID
Next
End Sub
once you have the collection of managers just loop that to create your manager-specific email.
Since I used Usedrange.Rows.Count to set up the range to loop it should have worked ok without an additional check. However, since I don't have your actual data to be sure, you may need it. I don't have line numbers, so I don't know what line 51 refers to. To loop the Managers:
Sub LoopManagers()
Dim currManager As Manager
For Each currManager In Managers
Debug.Print currManager.ManagerEmail
Dim currClient As Client
For Each currClient In currManager.Clients
Debug.Print currClient.ClientID
Next
Next
End Sub
You will need to adapt what I've provided to create your emails. Work on it. If you need more help post what you tried and describe what problems you have.

How to assign multiple listbox values to one variable and put those in Email "TO" field

I am trying to be able to select multiple values in two listboxes and assign to two separate variables. I want to then take those variables containing the selections and generate an email with the variable contents populating the "TO" field in an outlook email. Right now I am getting a Run-Time Error 94 - Invalid Use of Null.
Thanks for all your help!
Dim EAddress, MAddress As String
Public Sub UserForm_Initialize()
Emailfrm.EmpEmaillb.RowSource = "Searched_Employee_Email"
Emailfrm.ManagerEmaillb.RowSource = "Searched_Manager_Email"
End Sub
Public Sub Email_Click()
Dim OLobjMsg, NewMsg As Object
EAddress = Emailfrm.EmpEmaillb.Value
MAddress = Emailfrm.ManagerEmaillb.Value
Set objMsg = CreateObject("Outlook.Application")
objMsg.Session.Logon
Set NewMsg = objMsg.CreateItem(0)
With NewMsg
.To = EAddress & MAddress
.Subject = "BT Employee Database Inquiry Email"
'.Body = "Have a great weekend!"
End With
Unload Me
NewMsg.Display
End Sub
Asuming that your listboxes contain valid email addresses, I propose to
define ListBox1.MultiSelect = fmMultiSelectMulti
select multiple mail addresses by using Ctrl-Click
concatenate all selected addresses from the Listbox into one single string, e.g.
_
Private Sub CommandButton1_Click()
Dim LBCnt As Integer, AllAddr As String
AllAddr = ""
For LBCnt = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(LBCnt) Then
If AllAddr = "" Then
AllAddr = ListBox1.List(LBCnt)
Else
AllAddr = AllAddr & ";" & ListBox1.List(LBCnt)
End If
End If
Next LBCnt
Debug.Print AllAddr
End Sub
repeat for the 2nd listbox ...

Excel VBA code modification so only alert me once for same item

I manage a contract log that list out all of my company's contracts with the effective and expiring date.
I've written VBA code that alerts me whenever any one of the contracts is about to expire; a message box will show up that tells me the "carrier's contract# is about to expire". (Please see the code below).
However, because there are different Amendments for each contract, the same contract number may be listed out multiple times in the spreadsheet. If one contract is about to expire, the code notifies me multiple times.
How can I modify my code so it only alerts me once for the same contract number?
Column A is the carrier name, column B is the contract #, Column C is the Amendment# and Column G is the expiration date for each contract.
Let me know if I didn't make myself clear enough or more information is needed.
Private Sub Workbook_Open()
Dim rngC As Range
With Worksheets("NON-TPEB SC LOGS(OPEN)")
For Each rngC In .Range(.Range("G5"), .Cells(.Rows.Count, "G").End(xlUp))
If rngC.Value > Now And (rngC.Value - Now) < 7 Then
MsgBox .Cells(rngC.Row, 1).Value & "'s " & _
.Cells(rngC.Row, 2).Value & " is expiring!!"
End If
Next rngC
End With
End Sub
I would use a Scripting.Dictionary to keep track of contract numbers that have already been checked. This is how you might implement it.
After you do your logic test (If rngC.Value > Now And...) check to see if the contractNum exists in the dictionary. That's what this line does:
If Not checkedDict.Exists(contractNum) Then
If that evaluates True, then the contract has not already been checked, so we add it to the dictionary, and display the message box.
If that evaluates to False, then the contract does exist in the
dictionary, so can do nothing, since the user has already been
informed of the expiring contract.
Here is the full code (untested):
Private Sub Workbook_Open()
'Requires reference to Microsoft SCripting Runtime
' or, simply declare the scripting obects as generic "Object" variables.
Dim checkedDict As Scripting.Dictionary
'Dim checkedDict as Object '## Use this line (andcomment out the preceding line if you cannot enable the library reference to Scripting Runtime
Dim contractNum As String
Dim carrierName As String
Dim rngC As Range
Set checkedDict = CreateObject("Scripting.Dictionary")
With Worksheets("NON-TPEB SC LOGS(OPEN)")
For Each rngC In .Range(.Range("G5"), .Cells(.Rows.Count, "G").End(xlUp))
carrierName = .Cells(rngC.Row, 1).Value
contractNum = .Cells(rngC.Row, 2).Value
If rngC.Value > Now And (rngC.Value - Now) < 7 Then
If Not checkedDict.Exists(contractNum) Then
checkedDict.Add contractNum, carrierName
MsgBox carrierName & "'s " & _
contractNum & " is expiring!!"
Else:
' this contract# already exists, so, do nothing
' because the user was already informed.
End If
End If
Next rngC
End With
set checkedDict = Nothing
End Sub
The above code requires a reference to Microsoft Scripting Runtime Library, or, simply Dim checkedDict as Object instead.
I always use an AlreadyChecked string variable to keep track of what has already been processed.
In the loop add a check like this:
Dim AlreadyChecked As String
AlreadyChecked = "#"
If Instr(AlreadyChecked, "#" & ValueToCheck & "#") = 0 Then
AlreadyChecked = AlreadyChecked & ValueToCheck & "#"
... do your stuff ...
End If

Format of email address from Outlook (EX instead of SMTP)

I have a list of first and last names in Excel and I want to utilize that list to look up email address in Outlook using visual basic.
I'm using the following VB code:
Private Sub GetAddresses()
Dim o, AddressList, AddressEntry
Dim c As Range, r As Range, AddressName As String
Set o = CreateObject("Outlook.Application")
Set AddressList = o.Session.AddressLists("Global Address List")
Set r = Range("a1:a3")
For Each c In r
AddressName = Trim(c.Value) & ", " & Trim(c.Offset(0, 1).Value)
For Each AddressEntry In AddressList.AddressEntries
If AddressEntry.Name = AddressName Then
c.Offset(0, 2).Value = AddressEntry.Address
Exit For
End If
Next AddressEntry
Next c
End Sub
The code seems to be working fine up until the point of actually retrieving the email address. After it matches a name its returning the following instead of the address. Does anyone have an idea of what I'm doing wrong.
/O=Compnay/OU=Company/cn=Recipients/cn=shs
Thanks in advance for you help.
I am assuming that these are domain users. You want to get the SMTP address from the exchangeUser object. I have updated your code to show this.
Private Sub GetAddresses()
Dim o, AddressList, AddressEntry
Dim c As Range, r As Range, AddressName As String
'added variable for exchange user object
Dim exchangeUser As Outlook.exchangeUser
Set o = CreateObject("Outlook.Application")
Set AddressList = o.Session.AddressLists("Global Address List")
Set r = Range("a1:a3")
For Each c In r
AddressName = Trim(c.Value) ' & ", " & Trim(c.Offset(0, 1).Value)
For Each AddressEntry In AddressList.AddressEntries
If AddressEntry.Name = AddressName Then
'set the exchange user object
Set exchangeUser = AddressEntry.GetExchangeUser
'get the smtp addresss
c.Offset(0, 2).Value = exchangeUser.PrimarySmtpAddress
'release
Set exchangeUser = Nothing
Exit For
End If
Next AddressEntry
Next c
End Sub
That looks like a perfectly valid address of type EX (as opposed to SMTP). Use AddressEntry.GetExchangeUser().PrimarySmtpAddress to retrieve the SMTP address.
Do not loop through all items in an address list that can potentially contains tens of thousands of entries. Use Aplication.Sesssion.CreateRecipient, then call Recipient.Resolve. If successful, you can retrieve the AddressEntry object from Recipient.AddressEntry.
If you need to make sure the name is resolved against GAL only (by the way, you should not hardcode the GAL name, it will differ based on locale), you can use Redemption (I am its author) and its AddreessList.ResolveName method - all you need to do is call RDOSession.AddressBook.GAL.ResolveName

Resources