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.
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 have created an Outlook mail item and want to save it in a folder as a sent mail. I was able to set sender mail using mail.SentOnBehalfofName. How do I add Date field to this. I have some eml emails which I want to add to folder without a paid library. I was able to parse and save it to an outlook folder but the date field is set to None. Can someone help either to set the date field to the outlook mailitem object or a way to create mail Items that can be saved in outlook with all the properties?
Firstly, item's sent state can only be changed before it is saved for the very first time (MAPI limitation). Secondly, Outlook always creates olMailItem objects in the unsent state. The only item created in the sent state is the PostItem (olPostItem). You can create a PostItem, change its MessageClass property to "IPM.Note", save it, then release it using Marshal.ReleaseComObject (to make sure Outlook forgets about it), then reopen it by calingNamespace.GetItemFromID - this time Outlook should return back a MailItem object (instead of the original PostItem).
Keep in mind that the icon will be wrong, hence the post icon needs to be removed - delete the PR_ICON_INDEX property (DASL name http://schemas.microsoft.com/mapi/proptag/0x10800003) using MailItem.PropertyAccessor.DeleteProperty.
Also keep in mind that Outlook will not let you set some properties it considers "important" - such as the message dates, sender entry id, etc. And setting just the SentOnBehalfOfName property won't be enough - the sender entry ids must be set, or the user won't be able to correctly reply to that message.
If using Redemption (I am its author) is an option, creating a message in the sent state is as easy as
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set Inbox = Session.GetDefaultFolder(olFolderInbox)
set Msg = Inbox.Items.Add
Msg.Sent = true
set CU = Session.CurrentUser
set recip = Msg.Recipients.AddEx(CU.Name, CU.SmtpAddress, "SMTP", olTo)
Msg.Subject = "fake received message"
Msg.Body = "just a test"
vSenderEntryId = Session.AddressBook.CreateOneOffEntryID("Joe The Sender", "SMTP", "joe#domain.demo", false, true)
set vSender = Session.AddressBook.GetAddressEntryFromID(vSenderEntryId)
Msg.Sender = vSender
Msg.SentOnBehalfOf = vSender
Msg.SentOn = Now
Msg.ReceivedTime = Now
Msg.Save
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 am currently working on a former colleague's project that deals with saving emails from Exchange to our ERP system.
But I have run into a strange problem. Sometimes when the system receives an email, that contains an email as an attachment. It throws an error because the DataTimeReceived date is not set. Even after a .Load();
private int HandleEmail(Item item, Folder moveToFolder, Folder moveToFailedFolder, Mailbox mailbox, int fatherId = 0, string uploaderEmail = "", bool isEmbeddedMail = false)
The way it work. is that the HandleEmail() method goes through the original email until it gets the if-check.
If the attachment is not a FileAttachment, and is an ItemAttachment. The following code is called.
else if (attachment is ItemAttachment)
{
var itemAttachment = attachment as ItemAttachment;
itemAttachment.Load(new PropertySet(ItemSchema.Attachments, ItemSchema.TextBody, EmailMessageSchema.Sender, EmailMessageSchema.DisplayCc, EmailMessageSchema.DateTimeReceived, EmailMessageSchema.From, ItemSchema.MimeContent, ItemSchema.Body, ItemSchema.TextBody, EmailMessageSchema.BccRecipients, ItemSchema.Attachments));
var item_ = itemAttachment.Item;
HandleEmail(item_, null, null, mailbox, (int)mailid, uploaderEmail, true); // The attached email is then looped through like it is an regular email instead of like an image.
}
In this check. It takes the item attachment and loads an additional property set, which contains some data that is needed.
Finally the method calls it self, but now with the Item Attachment as to read it like a regular email.
My issue is the fact, that some attached emails do not have the DatetimeReceived property set.
Instead this error is shown in its place.
Microsoft.Exchange.Webservice.Data.ServicesObjectPropertyException
And this expection message is thrown
You must load or assign this property before you can read its value
And I can't quite figure out why it only happens to some attached emails. When I look at the emails it self, it does have an received date. But for some reason I can't get it using .Load()
I've tried a few things, such as using .Load for both Item and ItemAttachment, both without getting anything worthwhile.
Tried looking into using the service.loadpropertiesforitems()
I've forgotten a few of the other things I've looked into, since I have been looking at this for a few days before the weekend aswell.
Two suggestions i would have is first try the EmailMessageSchema.DateTimeSent (which should be the same as DateTimeReceived) the difference maybe being caused when attaching a messages that was sent. The other thing would be enable tracing and have a look at the traces to see what is actually coming back from the server also maybe just try loading the BasePropertySet.FirstClassProperties rather then your custom set (just to test that property anyway)
I’m trying to write a C# application that opens up a outlook custom mail item and fill in multiple user properties. I was able to do so by using the Microsoft Outlook Interop. But I had the annoying security warning each time I tried to change some user property. I found out that Redemption is the perfect tool to avoid this. But when I try to change a user property, Redemption creates a new one instead of using the already existing one.
This is the code I used to change the property with Outlook Interop (Pops up a security warning) :
string customPropertyNamespace = "http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/";
Outlook._Items oItems = oPublicFolder.Folders["Babillards"].Folders["SYSOTI"].Folders["MEP"].Items;
Outlook._MailItem oMep = oItems.Add("ipm.note.mep");
oMep.PropertyAccessor.SetProperty(customPropertyNamespace + "prop1", "SomeText");
oMep.Display(false);
This all works fine, except for the security warning...
This is the redemption code I'm trying to use :
string customPropertyGUID = "{00020329-0000-0000-C000-000000000046}";
Outlook._Items oItems = oPublicFolder.Folders["Babillards"].Folders["SYSOTI"].Folders["MEP"].Items;
Outlook._MailItem oMep = oItems.Add("ipm.note.mep");
Redemption.SafeMailItem Mep = new Redemption.SafeMailItem();
Mep.Item = oMep;
Mep.set_Fields(Mep.GetIDsFromNames(customPropertyGUID, "prop1"), "SomeText");
oMep.Display(false);
From what I understood this should work. But instead, my mail page opens with all my fields empty. By using OutlookSpy I found out that Redemption creates a new property with this DASL :
http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/prop1/0x0000001F
instead of :
http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/prop1
Can somebody help me? I also tried using the Redemption.MAPIUtils but I ended up with exactly the same result. Is there a way to change a user property by passing the DASL instead of a GUID and ID ?
The two property names are exactly the same - the last part (0x0000001F) is simply property type (= PT_UNICODE). What exactly do you mean by "my mail page opens with all my fields empty"? Do you have a custom form with controls bound to user fields?
Also keep in mind that Outlook might not see all the latest changes made with MAPI until the item is completely dereferenced and reopened. Do o use the data after you restart Outlook and reopen the existing item?
To avoid Outlook caching problem, try to create the message using Redemption, set the property, and only then open the message using Outlook. Somethiong like the following (off the top of my head):
string customPropertyNamespace = "http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/";
Outlook.Forlder oFolder = oPublicFolder.Folders["Babillards"].Folders["SYSOTI"].Folders["MEP"];
Redemption.RDOSession session = new RDOSesssion();
session.MAPUIOBJECT = Application.Session.MAPIOBJECT;
Redemption.RDOFolder rFolder = session.(RDOFolder)session.GetRDOObjectfromOutlookObject(oFolder);
Redemption.RDOMail rMsg = rFolder.Items.Add("ipm.note.mep");
rMsg.Fields[customPropertyNamespace + "prop1"] = "SomeText";
rMsg.Save();
//reopen in Outlook and display. Or you can use rMsg.Display()
Outlook._MailItem oMep = Application.Session.GetItemFromID(rMsg.EntryID);
oMep.Display(false);