I'm creating an Outlook addin using VS 2008 and C#. In order to function this addin goes through all the e-mail using Redemption and parses it.
I've recently run into the issue of somebody opening outlook without a network connection (network offline, unplugged, or it's mobile like a laptop and happens to not have connectivity at the moment). It seems to be in getting a list of the recipients.
System.Runtime.InteropServices.COMException (0x80040115): Error in IAddrBook::OpenEntry: MAPI_E_NETWORK_ERROR
Error: The connection to Microsoft Exchange is unavailable. Your network adapter does not have a default gateway.
Component: Microsoft Exchange Address Book
at Redemption.RDOAddressEntryClass.get_SMTPAddress()
This is happening within this code:
/// <summary>
/// Retrieves a list of recipient addresses from an RDOMail object
/// </summary>
/// <param name="rdoItem">The email to analyze</param>
/// <returns>A list of e-mail addresses</returns>
protected List<string> GetRecipients(RDOMail rdoItem)
{
RDORecipients recipients = rdoItem.Recipients;
List<string> recipientList = new List<string>();
if (recipients != null && recipients.Count > 0)
{
for (int i = 1; i <= recipients.Count; i++)
{
RDOAddressEntry addressEntry = recipients[i].AddressEntry;
if (addressEntry != null)
{
string recipient = addressEntry.SMTPAddress;
recipient = recipient.Trim();
if (recipient != null && recipient != String.Empty)
{
recipientList.Add(recipient);
}
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(addressEntry);
addressEntry = null;
}
}
}
if (recipients != null)
{
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(recipients);
recipients = null;
}
return recipientList;
}
So the question is, how do I get the recipients of an e-mail without needing to authenticate to or resolve from Exchange and it dying because there's no network connection?
EDIT: Alternatively - is there a way to cache the smtp e-mail addresses within outlook so that if it later goes offline it doesn't have to resolve the e-mail addresses?
I believe some store providers are wrappers around underlying PST stores. Therefore when accessing certain properties the provider will attempt to sync with the remote server. You should be able to stop this by unwrapping the store from the provider.
Note: that adding an item to an unwrapped store shouldn't persist that change to the server (in the case of IMAP4) for example.
Read more about the UnwrapStore property here at the Redemption website
In most cases PR_SMTP_ADDRESS property should be available in the recipient table (which is stored on the message itself rather than in GAL). You can access that property using RDORecipient.Fields[] - there is no reason to use RDORecipient.AddressEntry (which causes Redemption to call IAddrbook::OpenEntry, and the call can fail in the offline mode).
Look at the recipient table with OutlookSpy (I am its author) - click IMessage, go to the GetRecipientTable tab to make sure the PR_SMTP_ADDRESS property is present.
Related
I get accounts from Outlook like below.
Outlook.NameSpace ns = null;
Outlook.Accounts accounts = null;
Outlook.Account account = null;
string accountList = string.Empty;
try
{
ns = OutlookApp.Session;
accounts = ns.Accounts;
for (int i = 1; i <= accounts.Count; i++)
{
account = accounts[i];
accountList += String.Format("{0} - {1}{2}",
account.UserName,
account.SmtpAddress,
Environment.NewLine);
if (account != null)
Marshal.ReleaseComObject(account);
}
MessageBox.Show(accountList);
}
finally
{
if (accounts != null)
Marshal.ReleaseComObject(accounts);
if (ns != null)
Marshal.ReleaseComObject(ns);
}
However, Outlook return accounts, including those that have been removed.
It seems that there are no events that occur when the account is removed.
After the account is removed, is there a way to get the accounts excluding the removed account? How I can get accounts excluding removed account?
On the MAPI level (C++ or Delphi), account events are implemented through IOlkAccountManager::Advise method. You can see the events fire in OutlookSpy (I am its author - click IOlkAccountManager button, go to the Advise tab.
Outlook Object Model does not expose these events. If using Redemption (I am also its author) is an option, it exposes all account events through the RDOAccounts object - AccountChange, AccountAdd, AccountRemove, AccountBeforeRemove, AccountOrderChange.
The Outlook object model doesn't provide any event for that. The best what you can do is to handle the Stores.BeforeStoreRemove event which is fired when a Store is about to be removed from the current session either programmatically or through user action. Here is what MSDN says for the event:
Outlook must be running in order for this event to fire. This event will fire when any of the following occurs:
A store is removed by the user clicking the Close command on the Shortcut menu.
A store is removed programmatically by calling Namespace.RemoveStore.
This event will not fire when any of the following occurs:
When Outlook shuts down and closes a primary or delegate store.
If a store is removed through the Mail applet in the Microsoft Windows Control Panel and Outlook is not running.
A delegate store is removed on the Advanced tab of the Microsoft Exchange Server dialog box.
A store is removed through the Data Files tab of the Account Manager dialog box when Outlook is not running.
An IMAP Store is removed from the profile.
You can use this event to determine that a store has been removed, and take appropriate actions if the store is required for your application (such as remounting the store). Otherwise you would have to resort to polling the Stores collection.
I wrote a simple VSTO addin for Outlook 2016(part of office 365) to check if there is some email addresses outside our company are mixed in the recipients list.
I simplified the code like below:
int countExternalAddress;
string externalAddresses;
string internalDomain=“#example.com”;
//indicates the email domain of our company, we use exchange server.
private void Application_ItemSend(object Item, ref bool Cancel)
{
countExternalAddress = 0;
externalAddresses="";
Outlook.MailItem item = (Outlook.MailItem)Item;
foreach (Outlook.Recipient recp in item.Recipients)
{
ConvertExchangeAddrToSMTPAddr(recp.AddressEntry.Address);
//by access ExchangeUser.PrimarySmtpAddress
CheckTheAddress(recp.AddressEntry.Address);
}
if (countExternalAddress > 0)
{
Warn();
}
}
The code works with no problem most of time, but some times Warn() function shows warnings based on (part of) the recipients of the LAST email, not the CURRENT one.
The problem cannot be reproduced all the time, but when it occurs, the procedure is like:
Send an email to internal recipients(my colleagues, exchange addresses), the exchange addresses are successfully converted to SMTP address, because they are predefined as "internal" address, the mail will be sent without warning.
send another email to some other internal recipients, the program shows warning of "external address mixed", and the "external" address are from the previously sent email(mentioned in 1 above, not all addresses just one of them), and, the detected external address is in Exchange address format which is supposed to be converted to SMTP format.
Strangely, I cannot find the address in the recipients list of the current email.
If I save the current email and restart outlook, when I pick the saved email and try to resend it, no such "external" address will be detected again.
It looks like the Item object passed by ItemSend event contains some recipient(s) not only belong to the current email but also from the previously sent one, but they are not visible in the current email, moreover I cannot find such recipients in the sent email as well.
The work PC I am using doesn't have debug environment so I am running out of means. Please give me your helps, thanks in advance.
Could it have anything to do with you doing >
if (countExternalAddress > 0)
{
Warn();
}
and not ==
if (countExternalAddress == 0)
{
Warn();
}
Seems odd but it would leave one more email to warn you about.
I have the problem that when i add a custom UserProperty to an Outlook MailItem, it does not get synced to other connected Outlooks.
What can i do to force Outlook to sync the whole email?
My overal problem:
I've got a shared Exchange Mailbox, opened on two clients (in Outlook)
I would like to lock a mail item, if it gets opened in one Outlook and show the second Outlook user a message like "The user XX is currently reading this email"
My way to solve the problem:
Creating a Outlook Plugin.
When user "A" is opening the Email, I am adding a "LockingUser" UserProperty to the MailItem object. If user "B" is trying to open the Email, I am first looking if a "LockingUser" Property exists.
I have disabled the cached mode.
I have tried to update the subject of the email: this works perfectly and gets synced immediatly (but is not a solution for my problem)
private void SetLockingUser(Outlook.MailItem mail)
{
var lockingUserProperty = mail.UserProperties.Find("LockingUser");
if (lockingUserProperty != null)
{
MessageBox.Show("Email locked by: " + lockingUserProperty.Value);
return;
}
var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
var username = identity != null ? identity.Name : "";
lockingUserProperty = mail.UserProperties.Add("LockingUser", Outlook.OlUserPropertyType.olText, false, 1);
lockingUserProperty.Value = username;
mail.Save();
}
Please show the relevant snippet of your code and make sure you call MailItem.Save. Also keep in mind that there will always be lag since the changes will take up to a couple minutes to sync to Exchange and then to another user if cached mode is used. You'd be better off using some external sync mechanism instead of a user property.
I am building a shared-addin for Outlook.
Inside the code, I am creating a reply email using MailItem.Reply() method
and discarding it later. I am using this to get the sender email address
for emails coming through Exchange server.
It was working fine for Outlook 2007.
But for Outlook 2010, the Reply method seems to opening the mail editor window.
I am on windows 7.
Is there any way to suppress that window or write seperate code based on Outlook version?
If you plan on discarding the message - don't create it to begin with (don't use Reply() unless you intend on sending the message). You can use the Recipient class to resolve an Exchange users email address with minimal resource utilization.
string senderEmail = string.Empty;
Outlook.Recipient recipient = mailItem.Application.Session.CreateRecipient(mailItem.SenderEmailAddress);
if (recipient != null && recipient.Resolve() && recipient.AddressEntry != null)
{
Outlook.ExchangeUser exUser = recipient.AddressEntry.GetExchangeUser();
if (exUser != null && !string.IsNullOrEmpty(exUser.PrimarySmtpAddress))
senderEmail = exUser.PrimarySmtpAddress;
}
I’m using Redemption.dll to set custom properties to my messages with set_Filed() and get_field() in C#. Everything works perfectly until the moment I send my messages.
From Outlook I use RDOMail.Send() and this sent the message to the Drafts folder. Then I read in the Redemption FAQ that I should use the IMessage::Submit() method (that I couldn’t find anywhere in the dll for .NET) and then use DeliverNow(), method that I did use but to my surprise when I receive my messages I lose the properties I had set.
This is really completely critical to our project since if Outlook can’t send mails it’s worth nothing.
Here is part of my code.
private void adxOutlookEvents_ItemSend(object sender, AddinExpress.MSO.ADXOlItemSendEventArgs e)
{
try
{
RDOSessionClass _RDOSession= MessagesActions.GetRDOSession();
Outlook.MailItem _MailItem= e.Item as Outlook.MailItem;
RDOMail _RdoMail = MessagesActions.GetRDOMail(_RDOSession, _MailItem);
_RdoMail.Send(); // Send using Redeption
e.Cancel = true; // Only send using Redeption
if (_RdoMail != null && Marshal.IsComObject(_RdoMail))
Marshal.ReleaseComObject(_RdoMail);
Redemption.MAPIUtils _MAPIUtils = new MAPIUtils();
_MAPIUtils.DeliverNow(0, 0);
if (_MAPIUtils != null && Marshal.IsComObject(_MAPIUtils))
Marshal.ReleaseComObject(_MAPIUtils);
CurrentInspector.Close(Outlook.OlInspectorClose.olDiscard);
}
catch
{
}
}
Thanks.
When a message is sent through SMTP (unlike between 2 Exchange mailboxes in the same domain), the message is converted to MIME, and all MAPI specific properties are lost.
You can force Outlook to send the message in the TNEF (the infamous winmail.dat) format if yo uset a special named property called UseTnef:
RDOMail.Fields["http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/8582000B"] = true;