Populate Outlook Suggested Contacts from Sent Items

The smart guys over at degree.no wrote this amazing script that helps populate your Outlook Suggested Contacts folder by parsing emails you sent. This works very well, however, there was a minor issue where it does not correctly handle a case where the Display Name is blank. In such a case, the script imports the email address as the Display Name. This is not an issue with the script but an Outlook design that substitutes the display name with the email address.

I re-worked the script to handle this case better so that the final product looks cleaner i.e, your suggested contacts appear just as they would if you were to populate them over time.

Here’s the script:

$outlook = new-object -com outlook.application
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]
$namespace = $outlook.GetNameSpace("MAPI")
$sentItems = $namespace.getDefaultFolder($olFolders::olFolderSentMail)
$alreadyAddedEmails = @() #Empty Array
$counter = 0;
$totalItems = $sentItems.items.count;
Write-Host "Scanning through" $totalItems "emails in SentItems"
$contacts = $outlook.Session.GetDefaultFolder($olFolders::olFolderSuggestedContacts)
##############################################################################################################
# FUNCTION - Adds Name/Email to SuggestedContacts - Unless it has already been added before (by this script).
##############################################################################################################
Function AddToSuggestedContactsIfNotAlreadyAdded ($name, $email)
{    
    if(($name -eq "") -or ($email -eq "") -or ($name -eq $null) -or ($email -eq $null)){
        return;
    }    
	if ($name -like '*@*') {
	$name = $null
	}
	else {
    	$name = $name.Replace("'", "").Replace("""", "")
	}
    $contactAlreadyAdded = $false
    foreach ($elem in $global:alreadyAddedEmails) {
        if(($elem.ToLower() -eq $email.ToLower())){
            $contactAlreadyAdded = $true
	    if ($name -eq $null) { $name = "** No Display Name **" }
            Write-Host  ($global:counter)"/"($totalItems)  "SKIPPED " $name.PadRight(25," ") "-" $email
            return;
        }
    }
    if(!$contactAlreadyAdded )    {
        $newcontact = $contacts.Items.Add()
        $newcontact.FullName = $name
        $newcontact.Email1Address = $email
        $newcontact.Save()
        $global:alreadyAddedEmails += $email
	if ($name -eq $null) { $name = "** No Display Name **" }
        Write-Host ($global:counter)"/"($totalItems)  "ADDED   " $name.PadRight(25," ") "-" $email
    }
}
# Loop through all emails in SentItems
$sentItems.Items | % { 
    #Loop through each recipient
    $_.Recipients | %{
        AddToSuggestedContactsIfNotAlreadyAdded $_.Name $_.Address
    }
    $global:counter = $global:counter + 1
}



12 Comments

When I run this script on ‘s computer, I get the output below. Running Outlook 2010, on Win 7 Pro, through PowerShell, the file is saved to the Desktop. Can anyone help me identify where I’m going wrong? Error:

Exception calling “GetDefaultFolder” with “1” argument(s): “Could not complete the operation. One or more parameter
values are not valid.”
At C:\Users\\desktop\outlook.ps1:13 char:1
+ $sentItems = $namespace.getDefaultFolder($olFolders::olFolderSentMail)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation

Scanning through 0 emails in SentItems
Exception calling “GetDefaultFolder” with “1” argument(s): “Could not complete the operation. One or more parameter
values are not valid.”
At C:\Users\\desktop\outlook.ps1:20 char:1
+ $contacts = $outlook.Session.GetDefaultFolder($olFolders::olFolderSuggestedConta …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation

Done!

I’ve updated the script.
Fixed the comparison, so duplicates are skipped.
Replaced output with progress bar and counters.
The code that actually writes to Suggested Contacts is commented out so you can do a test run first.

#############################################################################################################
# MICROSOFT OUTLOOK 2007/2010 SUGGESTED CONTACTS / AUTOCOMPLETE GENERATOR
#############################################################################################################
#
# For Outlook 2010 - takes contact info from SentItems and populated Suggested Contacts
# Runs against default Outlook profile only
# Run this from PowerShell
# open PowerShell from command prompt:
# powershell –ExecutionPolicy Bypass
#
$outlook = new-object -com outlook.application
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]
$namespace = $outlook.GetNameSpace("MAPI")
$sentItems = $namespace.getDefaultFolder($olFolders::olFolderSentMail)
$alreadyAddedEmails = @() #Empty Array
$counter = 0;
$skippedcounter = 0;
$addedcounter=0;
$totalItems = $sentItems.items.count;
Write-Host "Scanning through" $totalItems "emails in SentItems"
$contacts = $outlook.Session.GetDefaultFolder($olFolders::olFolderSuggestedContacts)

##############################################################################################################
# FUNCTION - Adds Name/Email to SuggestedContacts - Unless it has already been added before (by this script).
##############################################################################################################
Function AddToSuggestedContactsIfNotAlreadyAdded ($name, $email)
{
if(($name -eq "") -or ($email -eq "") -or ($name -eq $null) -or ($email -eq $null)){
return;
}
if ($name -like '*@*') {
$name = $null
}
else {
$name = $name.Replace("'", "").Replace("""", "")
}
$contactAlreadyAdded = $false

foreach ($elem in $global:alreadyAddedEmails) {

if(($elem.ToLower() -Match $email.ToLower())){
$contactAlreadyAdded = $true
if ($name -eq $null) { $name = "** No Display Name **" }
# Write-Host ($global:counter)"/"($totalItems) "SKIPPED " $name.PadRight(25," ") "-" $email
$global:skippedcounter = $global:skippedcounter + 1
return;
} #endif
} #endfor

if(!$contactAlreadyAdded ) {
# comment out next 4 lines for a test run without adding to Outlook
# $newcontact = $contacts.Items.Add()
# $newcontact.FullName = $name
# $newcontact.Email1Address = $email
# $newcontact.Save()
$global:alreadyAddedEmails += $email
if ($name -eq $null) { $name = "** No Display Name **" }
# Write-Host ($global:counter)"/"($totalItems) "ADDED " $name.PadRight(25," ") "-" $email
$global:addedcounter = $global:addedcounter + 1
}
}

#################################################################################
# MAIN PROGRAM - Loops through all emails in SentItems and calls above function #
#################################################################################

# reset counters
$global:counter = 0;
$global:addedcounter = 0;
$global:skippedcounter = 0;

$sentItems.Items | % {

#Loop through each recipient
$_.Recipients | %{
AddToSuggestedContactsIfNotAlreadyAdded $_.Name $_.Address
}
$global:counter = $global:counter + 1

# display progress bar
write-progress -activity "Processing SentItems" -status "Percent Complete: " -percentcomplete (($global:counter / $totalItems) * 100)
}
#display statistics
Write-Host "Total SentItems Processed: " ($global:counter)
Write-Host "Contacts Added: " ($global:addedcounter)
Write-Host "Duplicates Skipped: " ($global:skippedcounter)

I know I’m late to the party but just found this great script.
However, when run as is, no duplicate entries are ever skipped.
Must change -eq to -match in order for the check for already added comparison to work.
Granted, even if run as is, no duplicates are saved because outlook filters them out in suggested contacts.

Got to the end of the script and this happened:
An error occurred while enumerating through a collection: The RPC server is una
vailable. (Exception from HRESULT: 0x800706BA).
At C:\outlook.ps1:44 char:1
+ <<<< $sentItems.Items | % {
+ CategoryInfo : InvalidOperation: (System.Runtime….ewOfEnumVar
iant:EnumeratorViewOfEnumVariant) [], RuntimeException
+ FullyQualifiedErrorId : BadEnumeration

Hi Sheen, for us on win7 pro w/ outlook 2010, this script imports hundres of duplicates, it doesn’t skip already added emails/contacts. Any thoughts?

Hi I get the following error and I have no idea what to do with it 🙁

Windows 8 Pro + Outlook 2010 + Powershell 3.0 =

Exception calling “GetDefaultFolder” with “1” argument(s): “Value does not fall within the expected range.”
At C:\Users\PacsoT\a.ps1:4 char:1
+ $sentItems = $namespace.getDefaultFolder($olFolders::olFolderSentMail)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation

Scanning through 0 emails in SentItems
Exception calling “GetDefaultFolder” with “1” argument(s): “Value does not fall within the expected range.”
At C:\Users\PacsoT\a.ps1:9 char:1
+ $contacts = $outlook.Session.GetDefaultFolder($olFolders::olFolderSuggestedConta …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation

Hi Matt,
The script always runs against the default profile. So, if you set Outlook to always use a particular profile – temporarily – the script will work. Let me know how it goes.

Sheen.

hello – saved this to a ps1 file and did run as powershell but it does not look like it worked. I have multiple profiles – does a part of the script need to get changed to point to a particular profile?

Thanks,
Matt

Leave a Reply

Your email address will not be published. Required fields are marked *

*