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.
Related
I need to get the "Sent Date" from the Sent Items e-mails, but I am following the example from Microsoft's documentation, for the implementation, making changes to get it from the sent items folder, it doesn't give any errors, but it doesn't work. Microsoft Documentation.
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
outlookNameSpace = this.Application.GetNamespace("MAPI");
inbox = outlookNameSpace.GetDefaultFolder(
Microsoft.Office.Interop.Outlook
.OlDefaultFolders.olFolderSentMail);
items = inbox.Items;
items.ItemAdd +=
new Outlook.ItemsEvents_ItemAddEventHandler(items_ItemAdd);
}
UPDATE
It doesn't even give an error, I have done tests to check, what seems to happen is that it is picking up from submitted items 1, and I need it to pick up from submitted items 2.
The code in the ThisAddIn_Startup event handler looks good. Try to set a breakpoint in the code and see how it works and whether any exception is thrown at runtime.
Be aware, Outlook may block add-ins that throw exceptions at runtime. So, if your breakpoint is not hit in the ThisAddIn_Startup event handler it makes sense to check the Disabled Items list in Outlook. Most probably your add-in was disabled automatically by the host application. See Support for keeping add-ins enabled for more information.
To get the default folder from another store you need to use the Store.GetDefaultFolder method instead of the following code:
inbox = outlookNameSpace.GetDefaultFolder(
Microsoft.Office.Interop.Outlook
.OlDefaultFolders.olFolderSentMail);
This method is similar to the GetDefaultFolder method of the NameSpace object. The difference is that this method gets the default folder on the delivery store that is associated with the account, whereas NameSpace.GetDefaultFolder returns the default folder on the default store for the current profile.
You can iterate over all stores in the profile in the following way:
private void EnumerateStores()
{
Outlook.Stores stores = Application.Session.Stores;
foreach (Outlook.Store store in stores)
{
if (store.IsDataFileStore == true)
{
Debug.WriteLine(String.Format("Store: "
+ store.DisplayName
+ "\n" + "File Path: "
+ store.FilePath + "\n"));
}
}
}
Looking a bit of advise or direction in regards to VSTO Ribbons with Outlook in C#.
So far I’ve built an Outlook 2010 Ribbon (using TabMail), this Ribbon opens a WinForms window which allows my users to select contacts from a Custom Built Address Book from a SQL database, via a DataGridView.
The users basically select from a datagridview who they want to email, which then gets added to a toLine list.
Application app = new Microsoft.Office.Interop.Outlook.Application();
Mail item item = app.CreateItem((OlItemType.olMailItem));
item.To = toLine;
Item.Display();
This.close();
The downside of using this approach is the user has to build their To List before they actually compose their email.
I’m now trying to make use of TabMailNewMessage. This should allow the user to compose their email, then click the Ribbon icon within the new message and add to their To List from there.
I’ve got the icon showing okay in the TabMailNewMessage, and I’ve got it to open a 2nd Win Form [currently as a test].
I’m a little unsure how to add to the To List of an already open existing mailItem.
At present all I have on the 2nd Win Form is a button, can someone explain how I can click that button and simply add someone to the To List [of this already composed email]. (I don’t have any code behind the button click as I’m not sure what to do)
I also need to make sure that it doesn’t send the email, but simply adds the user to the To List.
Currently using Office 2010, and VS 2013 (with C#).
Hopefully I’m making some sense here.
Thanks
EDIT:
Not sure if its a simple as
Application app = Globals.ThisAddIn.Application;
MailItem mi = (Outlook.MailItem)app.ActiveInspector().CurrentItem;
Mi.Recipients.Add(“joe#email.com”);
This.Close();
The MailItem.Recipients.Add method allows creating a new recipient in the Recipients collection. The name of the recipient can be a string representing the display name, the alias, or the full SMTP email address of the recipient.
using System.Runtime.InteropServices;
// ...
private bool AddRecipients(Outlook.MailItem mail)
{
bool retValue = false;
Outlook.Recipients recipients = null;
Outlook.Recipient recipientTo = null;
Outlook.Recipient recipientCC = null;
Outlook.Recipient recipientBCC = null;
try
{
recipients = mail.Recipients;
// first, we remove all the recipients of the e-mail
while(recipients.Count != 0)
{
recipients.Remove(1);
}
// now we add new recipietns to the e-mail
recipientTo = recipients.Add("Eugene Astafiev");
recipientTo.Type = (int)Outlook.OlMailRecipientType.olTo;
recipientCC = recipients.Add("Somebody");
recipientCC.Type = (int)Outlook.OlMailRecipientType.olCC;
recipientBCC = recipients.Add("eugene.astafiev#somedomain.com");
recipientBCC.Type = (int)Outlook.OlMailRecipientType.olBCC;
retValue = recipients.ResolveAll();
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
finally
{
if (recipientBCC != null) Marshal.ReleaseComObject(recipientBCC);
if (recipientCC != null) Marshal.ReleaseComObject(recipientCC);
if (recipientTo != null) Marshal.ReleaseComObject(recipientTo);
if (recipients != null) Marshal.ReleaseComObject(recipients);
}
return retValue;
}
You may find the following articles helpful:
How To: Fill TO,CC and BCC fields in Outlook programmatically
How To: Create and send an Outlook message programmatically
Also, I'd suggest developing an Outlook form region which can be displayed on the same window instead of using a standalone window, see Create Outlook form regions for more information.
Yes, it is as simple as using Application.ActiveInspector.CurrentItem. But you can do better than that - your ribbon button event handler takes IRibbonControl object as a parameter. Cast the IRibbonControl.Context property to Inspector or Explorer (depending on where the button is hosted).
Also keep in mind that in a COM addin there is no reason to create a new instance of the Outlook.Application object (it will be crippled with the security prompts anyway) - use Globals.ThisAddIn.Application
I have an instance of Microsoft.Office.Interop.Outlook.Application and I add itemSendHandler to the Application.itemSend
Application.itemSend += itemSendHandler
I'm assuming inside the itemSendHandler, the email should already be sent and a Message Id should exist for the email. Yet the following code produces a null messageId:
private void itemSendEventHandler(object sentItem, ref bool Cancel)
}
string PR_INTERNET_MESSAGE_ID_W_TAG = "http://schemas.microsoft.com/mapi/proptag/0x1035001F";
PropertyAccessor propertyAccessor = ((MailItem)sentItem).PropertyAccessor;
// This is null? Why?
string messageId = (string)propertyAccessor.GetProperty(PR_INTERNET_MESSAGE_ID_W_TAG);
ThisAddIn.attemptToReleaseComObject(propertyAccessor);
{
But when I inspect the sent item immediately afterwards, through the code or through a tool (like OutlookSpy or MFCMAPI), the property exists. Why is messageId null during the send event handler?
You need to save the message first before accessing that property. Even then, PR_INTERNET_MESSAGE_ID might not be accessible in the cached mode - Outlook will not synchronize the item in the Sent Items folder with its online replica for the performance reasons, and only the online version of the message will have that property. You can open that message in the online mode using Extended MAPI (C++ or Delphi) or Redemption (any language) by using the MAPI_NO_CACHE flag, but there is no way to do that in OOM.
I am trying to develop an add-in for Outlook. In that I want remove attachment when a new mail is received.
So I call my function on the NewMailEx event. It is working fine. In that function I try to find unread mails in the Outlook inbox. From those I remove the attachments.
My problem is: when I open Outlook, the first mail I received is not showing in the inbox (not in outlook c# code), so I can't remove the attachment from that mail.
From the second mail (after the first mail), I can get the received mail, so I can remove the attachments.
Whenever I close and reopen Outlook, the first mail I receive gives this problem.
At the first mail received there is no unread mail.
private void Application_NewMailEx(object Item)
{
string senderEmailid = string.Empty;
outlookNameSpace = this.Application.GetNamespace("MAPI");
Outlook.Application myApp = new Outlook.Application();
Outlook.NameSpace mapiNameSpace = myApp.GetNamespace("MAPI");
Outlook.MAPIFolder myInbox = mapiNameSpace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
Outlook.Attachments attachments;
int test = myInbox.Items.Count;
Microsoft.Office.Interop.Outlook.Recipients recipients = ((Outlook.MailItem)myInbox.Items[test]).Recipients;
Outlook.Items unreadItems = myInbox.Items.Restrict("[Unread]=true");
if (unreadItems.Count > 0)
{
foreach (Outlook.MailItem mail in unreadItems)
{
Outlook.Recipient recip;
Outlook.ExchangeUser exUser;
string sAddress;
if (mail.SenderEmailType.ToLower() == "ex")
{
recip = Globals.ThisAddIn.Application.GetNamespace("MAPI").CreateRecipient(mail.SenderEmailAddress);
exUser = recip.AddressEntry.GetExchangeUser();
sAddress = exUser.PrimarySmtpAddress;
}
else
{
sAddress = mail.SenderEmailAddress.Replace("'", "");
}
string[] emailAddressPart = sAddress.Split('#');
string strSenderDomain = emailAddressPart[1];
if (lstDomain.Any(item => item.Contains(strSenderDomain)))
{
attachments = mail.Attachments;
int nAttachmentCount = mail.Attachments.Count;
if (nAttachmentCount > 0)
{
int j = nAttachmentCount;
for (int i = 1; i <= nAttachmentCount; i++)
{
mail.Attachments[j].Delete();
j--;
}
}
}
}
}
}
The NewMailEx event of the Application class is not the best place for searching unread emails. This event fires once for every received item that is processed by Microsoft Outlook. The item can be one of several different item types, for example, MailItem, MeetingItem, or SharingItem. The EntryIDsCollection string contains the Entry ID that corresponds to that item. The alternative way is to handle the ItemAdd event of the Items class.
Instead, you can wait until Outlook finishes synchronizing items and run your code for searching unread emails. The SyncEnd event of the SyncObject class is fired immediately after Microsoft Outlook finishes synchronizing a user’s folders using the specified Send/Receive group.
Also you may consider handling the Startup or MAPILogonComplete events. But they are fired before the synchronization is completed. Consider using a timer to run the procedure a bit after Outlook is started.
You can read about possible ways of handling incoming emails in the following series of articles:
Outlook NewMail event unleashed: the challenge (NewMail, NewMailEx, ItemAdd)
Outlook NewMail event: solution options
Outlook NewMail event and Extended MAPI: C# example
Outlook NewMail unleashed: writing a working solution (C# example)
Also I'd recommend breaking the chain of calls and delaclaring each property or method call on a separate line of code. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. This is particularly important if your add-in attempts to enumerate more than 256 Outlook items in a collection that is stored on a Microsoft Exchange Server. If you do not release these objects in a timely manner, you can reach the limit imposed by Exchange on the maximum number of items opened at any one time. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object. Read more about that in the Systematically Releasing Objects article in MSDN.
There is nothing you can do about that. From the documentation on NewMailEx:
Also, the event will fire only if Outlook is running. In other words, it will not fire for the new items that are received in the Inbox when Outlook was not open.
That means you have to manually call your method to walk down all the unread emails when opening Outlook.
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.