Andrei Smolin

How to specify account for an outgoing Outlook message

In Outlook 2007-2010 you do this by specifying the MailItem.SendUsingAccount property. It can resemble this code:

using System.Runtime.InteropServices;
using Outlook = Microsoft.Office.Interop.Outlook;
...
void SetAccount_2007_2010(Outlook.MailItem mail, string accountName)
{
    Outlook.NameSpace session = mail.Session;
    Outlook.Accounts accounts = session.Accounts;
    for (int i = 1; i < = accounts.Count; i++)
    {
        Outlook.Account account = accounts[i];
        if (account.DisplayName.ToLower() == accountName.ToLower())
        {
            mail.SendUsingAccount = account;
            Marshal.ReleaseComObject(account); 
            break;
        }
        Marshal.ReleaseComObject(account);
    }
    Marshal.ReleaseComObject(accounts);
    Marshal.ReleaseComObject(session);
}
Imports System.Runtime.InteropServices
Imports Outlook = Microsoft.Office.Interop.Outlook
...
Sub SetAccount_2007_2010(ByRef mail As Outlook.MailItem, _
                         ByVal accountName As String)
    Dim session As Outlook.NameSpace = mail.Session
    Dim accounts As Outlook.Accounts = session.Accounts
    For i As Integer = 1 To accounts.Count
        Dim account As Outlook.Account = accounts(i)
        If account.DisplayName.ToLower() = accountName.ToLower() Then
            mail.SendUsingAccount = account
            Marshal.ReleaseComObject(account)
            Exit For
        End If
        Marshal.ReleaseComObject(account)
    Next i
    Marshal.ReleaseComObject(accounts)
    Marshal.ReleaseComObject(session)
End Sub

Another code sample is available in How to: Send an E-Mail Given the SMTP Address of an Account.

In Outlook 2000-2003, the Outlook object model doesn’t provide a way to set account. But in some situations you can use the workaround below. Here is the idea: since account names are available in the popup control called Accounts, you can find that control in your code, list all items (buttons) showing the account names and “click” the corresponding button programmatically. You do the “click” via CommandBarButton.Execute(). Unfortunately you cannot do this if Word is used as the default email editor because the Accounts control is implemented in the Envelope toolbar in this case and the toolbar is not accessible programmatically. You can see a code sample below. Note that you can specify an account in a composed email only; the code sample deals with this, too.

using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core
using System.Runtime.InteropServices;
using System.Reflection;
...
private void SetAccount_2000_2002_2003(Outlook.Inspector inspector, string accountName)
{
    if (inspector.IsWordMail()) return; // Word is the default email editor
    if (IsItemSent(inspector)) return; // sent or recieved email
 
    Office.CommandBars bars = inspector.CommandBars;
    Office.CommandBarPopup accounts =
        bars.FindControl(Office.MsoControlType.msoControlPopup,
                        31224, //the ID of the Accounts popup
                        Type.Missing,
                        true)//visible only
        as Office.CommandBarPopup;
    if (accounts != null)
    {
        Office.CommandBarControls accountControls = accounts.Controls;
        for (int i = 1; i < = accountControls.Count; i++)
        {
            Office.CommandBarButton account =
                accountControls[i] as Office.CommandBarButton;
            if (account.Caption.ToLower() == accountName.ToLower())
            {
                account.Execute();
                Marshal.ReleaseComObject(account);
                break;
            }
            Marshal.ReleaseComObject(account);
        }
        Marshal.ReleaseComObject(accountControls);
        Marshal.ReleaseComObject(accounts);
    }
    Marshal.ReleaseComObject(bars);
}
 
private static bool IsItemSent(Outlook.Inspector inspector)
{
    object item = inspector.CurrentItem;
    bool isSent = Convert.ToBoolean(item.GetType().InvokeMember("Sent",
        BindingFlags.GetProperty, null, item, null));
    Marshal.ReleaseComObject(item);
    return isSent;
}
Imports Outlook = Microsoft.Office.Interop.Outlook;
Imports Office = Microsoft.Office.Core
Imports System.Runtime.InteropServices;
Imports System.Reflection;
...
Private Sub SetAccount_2000_2002_2003(ByRef inspector As Outlook.Inspector, _
                                      ByVal accountName As String)
    If inspector.IsWordMail() Then Return ' Word is the default email editor
    If IsItemSent(inspector) Then Return ' sent or recieved email
 
    Dim bars As Office.CommandBars = inspector.CommandBars
    '31224 - the ID of the Accounts popup
    'True - visible only
    Dim accounts As Office.CommandBarPopup = CType( _
        bars.FindControl(Office.MsoControlType.msoControlPopup, _
                          31224, Type.Missing, True), Office.CommandBarPopup)
    If accounts IsNot Nothing Then
        Dim accountControls As Office.CommandBarControls = _
            accounts.Controls
        For i As Integer = 1 To accountControls.Count
            Dim account As Office.CommandBarButton = _
                CType(accountControls(i), Office.CommandBarButton)
            If account.Caption.ToLower() = accountName.ToLower() Then
                account.Execute()
                Marshal.ReleaseComObject(account)
                Exit For
            End If
            Marshal.ReleaseComObject(account)
        Next
        Marshal.ReleaseComObject(accountControls)
        Marshal.ReleaseComObject(accounts)
    End If
    Marshal.ReleaseComObject(bars)
End Sub
 
Private Function IsItemSent(ByRef inspector As Outlook.Inspector) As Boolean
    Dim item As Object = inspector.CurrentItem
    Dim isSent As Boolean = _
        Convert.ToBoolean(item.GetType().InvokeMember( _
            "Sent", BindingFlags.GetProperty, Nothing, item, Nothing))
    Marshal.ReleaseComObject(item)
    Return isSent
End Function

Good luck!

4 Comments

  • Boudewijn says:

    The last time I looked for a lot of solutions.
    I found a lot of them, but yours are clear amd with yours I am completely satisfied!
    Thanks!

  • Paul says:

    Thanks a lot! Works very well!

  • David says:

    Regarding the snippet of code that loops through each Outlook.Account until it finds the account it’s looking for: I happen to use this functionality a lot and so I would like to create a function to return the Outlook.Account for a specified smtp address. What I can’t properly figure out is how to release the account comobject inside of the function that is returning it to the caller. See example of my question below in vb, but I also know C#:
    Private Sub SendAnEmail()
    Dim sendingAccount As Outlook.Account
    sendingAccount = GetSenderAccount(“test@gmail.com”)
    If Not IsNothing(sendingAccount) Then
    ‘Code here to send an email using the sendingAccount.

    ‘IS RELEASING THE COMOBJECT HERE SUFFICIENT TO RELEASE THE
    ‘OBJECT WHEN IT WAS ORIGINALLY REFERENCED IN
    ‘GetSenderAccount()”
    Marshal.ReleaseComObject(sendingAccount)
    End If
    End Sub

    Private Function GetSenderAccount(smtpAddress As String) As Outlook.Account
    Dim senderAccount As Outlook.Account
    Dim allSenderAccounts As Outlook.Accounts = OutlookApp.Session.Accounts
    For Each senderAccount In allSenderAccounts
    If senderAccount.SmtpAddress.ToLower = smtpAddress.ToLower Then
    Marshal.ReleaseComObject(allSenderAccounts)
    ‘WHEN/WHERE DO I RELEASE THE senderAccount I’M RETURNING?
    Return senderAccount
    End If
    Marshal.ReleaseComObject(senderAccount)
    Next
    Marshal.ReleaseComObject(allSenderAccounts)
    Return Nothing
    End Function

  • Andrei Smolin (Add-in Express Team) says:

    Hello David,

    The SendAnEmail method is okay. Here’s a version of your other method:

    Private Function GetAccountFromSmtpAddress(smtpAddress As String) As Outlook.Account
    Dim ns As Outlook.NameSpace = OutlookApp.Session
    Dim allAccounts As Outlook.Accounts = ns.Accounts
    Dim anAccount As Outlook.Account = Nothing

    For i As Integer = 1 To allAccounts.Count
    anAccount = allAccounts.Item(i)
    If anAccount.SmtpAddress.ToLower = smtpAddress.ToLower Then
    Exit For
    End If
    Marshal.ReleaseComObject(anAccount) : anAccount = Nothing
    Next
    Marshal.ReleaseComObject(allAccounts)
    Return anAccount ‘ this is either Nothing or a COM object that the caller must release
    End Function

    HTH

Post a comment

Have any questions? Ask us right now!