I've been having a little weekend project for myselff which involves getting all my ToDo tasks from Outlook, put them in a DataGridView and me being able to edit and export them.
The only problem I've been running into is me being unable to get the task specific properties for flagged emails while they still exist, I just don't see any way to access them.
Here is a portion of my current code.
private void retrieveTasks()
{
//Clear datagrid so we won't have duplicate information
taskList.Rows.Clear();
//Define some properties so we can use these to retrieve the tasks
Outlook.Application app = null;
_NameSpace ns = null;
Store outlookStore = null;
Outlook.MAPIFolder taskFolder = null;
Outlook.MAPIFolder specialFolder = null;
TaskItem task = null;
DateTime nonDate = new DateTime(4501, 1, 1);
try
{
//Connect to Outlook via MAPI
app = new Outlook.Application();
ns = app.GetNamespace("MAPI");
ns.Logon(null, null, false, false);
/*
outlookStore = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox).Store;
taskFolder = outlookStore.GetSpecialFolder(Microsoft.Office.Interop.Outlook.OlSpecialFolders.olSpecialFolderAllTasks);
*/
//Get the taskfolder containing the Outlook tasks
//taskFolder = ns.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderTasks);
outlookStore = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox).Store;
taskFolder = outlookStore.GetSpecialFolder(OlSpecialFolders.olSpecialFolderAllTasks);
//Console.WriteLine(specialFolder.Items[1].Subject.ToString());
/*for (int i = 1; i <= specialFolder.Items.Count; i++)
{
//task = (Outlook.TaskItem)specialFolder.Items[i];
Console.WriteLine(specialFolder.Items[i].ToString());
}*/
for (int i = 1; i <= taskFolder.Items.Count; i++)
{
//Get task from taskfolder
Object item = taskFolder.Items[i];
if (item is Outlook.MailItem)
{
MailItem mail = (Outlook.MailItem)item;
if (mail.TaskCompletedDate.Equals(nonDate))
{
string percentComplete = "";
string taskPrio = "";
if (mail.UserProperties.Find("Prio") == null)
{
taskPrio = "";
}
else
{
taskPrio = mail.UserProperties.Find("Prio").Value.ToString();
}
//mail.UserProperties.
if (mail.UserProperties.Find("% Complete") == null)
{
percentComplete = "";
}
else
{
percentComplete = mail.UserProperties.Find("% Complete").Value.ToString();
}
//Add the tasks details to the datagrid
taskList.Rows.Add(
i.ToString(),
checkForNull(mail.Subject),
parseDate(mail.TaskStartDate.ToString()),
parseDate(mail.TaskDueDate.ToString()),
percentComplete, "Taak voltooid",
//statusToFriendlyName(mail.Status.ToString()),
taskPrio
);
}
}
else if (item is Outlook.TaskItem)
{
task = (Outlook.TaskItem)item;
Console.WriteLine(task.Subject);
if (task.Complete == false)
{
string taskPrio = "";
//Make sure custom task property is failed or set it to empty text to prevent crashes
if (task.UserProperties.Find("Prio") == null)
{
taskPrio = "";
}
else
{
taskPrio = task.UserProperties.Find("Prio").Value.ToString();
}
//Add the tasks details to the datagrid
taskList.Rows.Add(
i.ToString(),
task.Subject.ToString(),
parseDate(task.StartDate.ToString()),
parseDate(task.DueDate.ToString()),
task.PercentComplete.ToString(),
statusToFriendlyName(task.Status.ToString()),
taskPrio
);
}
}
}
}
catch (System.Runtime.InteropServices.COMException ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
//Release Outlook sources
ns = null;
app = null;
}
}
So specifically I am looking for the "% complete" property and the status property, everything else I am able to work around on.
It would make my life SO MUCH EASIER if I can get this to work :)
Hope to hear from anyone on here!
Jeffrey
Do you mean you need to retrieve the task specific properties from a MailItem object (as opposed to the TaskItem object)?
Take a look at the message with OutlookSpy (I am its author) - click IMessage button, look at the MAPI properties in the GetProps tab. Any property can be accessed using MailItem.PropertyAccessor.GetProperty. The DASL name to be used by GetProperty is displayed by OutlookSpy (select the property in the IMessage window, see the DASL edit box).
Related
I have following requirement :
Allow the user to drag & drop an email from outlook to a datagrid
Prepare a reply mail, and show it so the user can review and send
After sending, also fetch the send mail and put it into the datagrid
The drag/drop I have working
Preparing the replay email and showing it to the user I also have working, with this code :
MailItem mail = GetMailBySubject(dateReceived, subject);
if (mail != null)
{
MailItem mailReply = mail.ReplyAll();
// add text and stuff to mailReply...
mailReply.Display();
}
This will open a window in outlook, as if the user clicked reply in outlook.
Now I am stuck with the 3th requirement,
after the user send the reply email, I need somehow to find this email in outlook to add it to my datagrid.
But I have no clue on how to do that.
All I have is the original mail that is been used to prepare the reply.
Is there a way to find the reply with only this, or is this maybe a complete wrong approach ?
To make it more difficult is that I have to show the reply email NON Modal, so I have no trigger when the user clicked on send in outlook.
for reference, here is the code for GetMailBySubject
private MailItem GetMailBySubject(DateTime dateReceived, string subject)
{
MailItem Result = null;
Microsoft.Office.Interop.Outlook.Application OutlookIns = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.NameSpace olNamespace = OutlookIns.GetNamespace("MAPI");
MAPIFolder myInbox = olNamespace.GetDefaultFolder(OlDefaultFolders.olFolderInbox);
Items items = myInbox.Items;
int count = items.Count;
MailItem mail = null;
int i = 1; //DO NOT START ON 0
while ((i < count) && (Result == null))
{
if (items[i] is MailItem)
{
mail = (MailItem)items[i];
if ((mail.ReceivedTime.ToString("yyyyMMdd hh:mm:ss") == dateReceived.ToString("yyyyMMdd hh:mm:ss")) && (mail.Subject == subject))
{
Result = mail;
}
}
i++;
}
return Result;
}
EDIT
I tried this code as suggested, but the Items.ItemAdd event is not firing.
So I must still be doing something wrong but I cant see it
this is my code now
MailItem mail = GetMailBySubject((DateTime)sentOn, msg.Subject);
if (mail != null)
{
MailItem mailReply = mail.ReplyAll();
mailReply.HTMLBody = GetDefaultReplyText() + Environment.NewLine + mailReply.HTMLBody;
Guid guid = Guid.NewGuid();
UserProperties mailUserProperties = null;
UserProperty mailUserProperty = null;
mailUserProperties = mailReply.UserProperties;
mailUserProperty = mailUserProperties.Add("myproperty", OlUserPropertyType.olText);
mailUserProperty.Value = guid.ToString(); ;
// the code below gives error "The property cannot be parsed or has an invalid format"
//mailReply.PropertyAccessor.SetProperty("myproperty", guid.ToString());
mailReply.Display();
}
private MailItem GetMailBySubject(DateTime dateReceived, string subject)
{
MailItem Result = null;
Microsoft.Office.Interop.Outlook.Application OutlookIns = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.NameSpace olNamespace = OutlookIns.GetNamespace("MAPI");
MAPIFolder myInbox = olNamespace.GetDefaultFolder(OlDefaultFolders.olFolderInbox);
Items items = myInbox.Items;
int count = items.Count;
MailItem mail = null;
int i = 1; //DO NOT START ON 0
while ((i < count) && (Result == null))
{
if (items[i] is MailItem)
{
mail = (MailItem)items[i];
if ((mail.ReceivedTime.ToString("yyyyMMdd hh:mm:ss") == dateReceived.ToString("yyyyMMdd hh:mm:ss")) && (mail.Subject == subject))
{
Result = mail;
MAPIFolder mySent = olNamespace.GetDefaultFolder(OlDefaultFolders.olFolderSentMail);
mySent.Items.ItemAdd += Items_SentMailItemAdd;
}
}
i++;
}
return Result;
}
and finally
private void Items_SentMailItemAdd(object Item)
{
//throw new NotImplementedException();
; // this event is never fired
}
You can use the Items.ItemAdd event on the Sent Items folder. To check if that is your message, set a custom property on the message that you create and display. You can use MailItem.UserProperties.Add, but that can force the message to be sent in the TNEF format. To prevent that from happening, you can use MailItem.PropertyAccessro.SetProperty to set a named MAPI property without using the UserProperties collection. You can set a test user property and look at its DASL name (to be used by SetProperty) with OutlookSpy (I am its author - select the message, click IMessage button, select your custom property, see the DASL edit box).
I am working on addin to Outlook 2013. I added custom field to MailItem and used AdvancedSearch to find item. The last missing part is showing the results.
How can I show results of custom search in search results?
private void Application_AdvancedSearchComplete(Outlook.Search SearchObject)
{
string dx = SearchObject.Tag;
int x = SearchObject.Results.Count;
//What next?
}
private void button1_Click(object sender, RibbonControlEventArgs e)
{
Object selObject = Globals.ThisAddIn.Application.ActiveExplorer().Selection[1];
Outlook.MailItem mail = selObject as Outlook.MailItem;
if (mail != null)
{
Outlook.UserProperties mailUserProperties = null;
Outlook.UserProperty mailUserProperty = null;
mailUserProperties = mail.UserProperties;
foreach (var i in mailUserProperties)
{
var xx = i;
}
mailUserProperty = mailUserProperties.Add("TestUserProperty", Outlook.OlUserPropertyType.olText, true);
mailUserProperty.Value = "Eugene Astafiev";
mail.Save();
}
string str = "http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/TestUserProperty LIKE '%ugene%'";
Outlook.MAPIFolder inbox = Globals.ThisAddIn.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
Globals.ThisAddIn.Application.AdvancedSearch("Inbox", str, false, "TestUserProperty");
}
You cannot display the results of Application.AdvancedSearch. To show search results in the UI you will have to use Explorer.Search, which is effectively just automating the user performing the search in the UI. However, you will not get the results back in code to work with. See here for an overview of your options:
https://msdn.microsoft.com/en-us/library/ff869846.aspx
You can save results to a search folder and then display it to a user. The Save method of the Search class saves the search results to a Search Folder. Note, the Save method displays an error if a Search Folder with the same name already exists.
Outlook.Results advancedSearchResults = advancedSearch.Results;
if (advancedSearchResults.Count > 0)
{
if (HostMajorVersion > 10)
{
object folder = advancedSearch.GetType().InvokeMember("Save",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.InvokeMethod |
System.Reflection.BindingFlags.Public,
null, advancedSearch,
new object[] { advancedSearchTag });
}
else
{
strBuilder = new System.Text.StringBuilder();
strBuilder.AppendLine("Number of items found: " +
advancedSearchResults.Count.ToString());
for (int i = 1; i < = advancedSearchResults.Count; i++)
{
resultItem = advancedSearchResults[i]
as Outlook.MailItem;
if (resultItem != null)
{
strBuilder.Append("#" + i.ToString());
strBuilder.Append(" Subject: " + resultItem.Subject);
strBuilder.Append(" \t To: " + resultItem.To);
strBuilder.AppendLine(" \t Importance: " +
resultItem.Importance.ToString());
Marshal.ReleaseComObject(resultItem);
}
}
if (strBuilder.Length > 0)
System.Diagnostics.Debug.WriteLine(strBuilder.ToString());
else
System.Diagnostics.Debug.WriteLine(
"There are no Mail items found.");
}
}
else
{
System.Diagnostics.Debug.WriteLine("There are no items found.");
}
You may find the Advanced search in Outlook programmatically: C#, VB.NET article helpful.
I'm trying to convert a bit of VBA (outlook) to c# for an addin. Struggling with it. The code changes the account dropdown on the reply/replyall/forward form in outlook. Now I have to type more useless stuff because the editor is moaning about more code than text. I have converted 99% of it.
public string Set_Account(string AccountName, Outlook.MailItem M)
{
string tempSet_Account = null;
Outlook.Inspector OLI = null;
string strAccountBtnName = null;
int intLoc = 0;
const int ID_ACCOUNTS = 31224;
Office.CommandBars CBs = null;
Office.CommandBarPopup CBP = null;
Office.CommandBarControl MC = null;
M.Display();
OLI = M.GetInspector;
if (OLI != null)
{
CBs = OLI.CommandBars;
CBP = CBs.FindControl(, ID_ACCOUNTS); // This line errors and I can't find what goes in it to make it work
CBP.Reset();
if (CBP != null)
{
foreach (Office.CommandBarControl MCWithinLoop in CBP.Controls)
{
MC = MCWithinLoop;
intLoc = MCWithinLoop.Caption.IndexOf(" ") + 1;
if (intLoc > 0)
{
strAccountBtnName = MCWithinLoop.Caption.Substring(intLoc);
}
else
{
strAccountBtnName = MCWithinLoop.Caption;
}
if (strAccountBtnName == AccountName)
{
MCWithinLoop.Execute();
tempSet_Account = AccountName;
break;
}
}
}
}
tempSet_Account = "";
MC = null;
CBP = null;
CBs = null;
OLI = null;
return tempSet_Account;
}
I am having trouble with this line specifically:
CBP = CBs.FindControl(, ID_ACCOUNTS);
It fails and says it needs a type "MsoControlType" but I cannot find any control type that fits. It seems in VBA you can findcontrol with just one entry (the other being blank) In C# you need 2. Every thing I put it the compiler moans about
cannot convert Microsoft.Office.Core.MsoControlType.msoCommandBarControl to ...msoCommandBarPopup
But I cannot find a reference to it anywhere.
In C#, you can't leave out a parameter like you're doing here: ".FindControl(,". If you have to pass "null" or a null object.
I want to import contacts from Outllok via Mapi.
First step with standard contact is no problem:
MAPIFolder contactObjects =
outlookObj.Session.GetDefaultFolder(OlDefaultFolders.olFolderContacts);
foreach (ContactItem contactObject in contactObjects.Items) {
... import contact ...
}
In a second step I additionally want to import shared contacts. Only thing I found was using
OpenSharedItem(sharedContacts.vcf)
but I don't know the name of the file (shared item) I want to open.
Does someone know how to access shared contacts and can help me out?
Tobi
Update:
Thanks for the hint with the vcf-Files. But where do I find them?
Update2:
I played around with OutlookSpy. I got access to the folder with shared contacts, but only by knowing the id (which is of course different for other users):
var ns = outlookObj.GetNamespace("MAPI");
var flr = ns.GetFolderFromID("00000000176A90DED92CE6439C1CB89AFE3668F90100D1AD8F66B576B54FB731302D9BB9F6C40007E4BAC5020000");
foreach (var contactObject in flr.Items) {
...
}
How do I get access to the folder without knowing the id?
You will need to either explicitly parse the vCard files or you can use Redemption (I am its author) - it allows to import vCard files using RDOContactItem.Import - http://www.dimastr.com/redemption/RDOMail.htm#methods
Well the solution to the question as it is asked in the title is almost simple.
You just need to call:
Recipient recip = Application.Session.CreateRecipient("Firstname Lastname");
MAPIFolder sharedContactFolder = Application.Session.GetSharedDefaultFolder(recip, OlDefaultFolders.olFolderContacts);
Because this does not solve my problem I will ask another question!
I have done some programming to get contact from outlook.
I am giving you some example code to help you up with this ..It is not excatly want you want but i think this will help you with your problem...
using System.Collections.Generic;
// ...
private List<Outlook.ContactItem> GetListOfContacts(Outlook._Application OutlookApp)
{
List<Outlook.ContactItem> contItemLst = null;
Outlook.Items folderItems =null;
Outlook.MAPIFolder mapiFoldSuggestedConts = null;
Outlook.NameSpace nameSpc = null;
Outlook.MAPIFolder mapiFoldrConts = null;
object itemObj = null;
try
{
contItemLst = new List<Outlook.ContactItem>();
nameSpc = OutlookApp.GetNamespace("MAPI");
// getting items from the Contacts folder in Outlook
mapiFoldrConts =
nameSpc.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
folderItems = mapiFoldrConts.Items;
for (int i = 1; folderItems.Count >= i; i++)
{
itemObj = folderItems[i];
if (itemObj is Outlook.ContactItem)
contItemLst.Add(itemObj as Outlook.ContactItem);
else
Marshal.ReleaseComObject(itemObj);
}
Marshal.ReleaseComObject(folderItems);
folderItems = null;
// getting items from the Suggested Contacts folder in Outlook
mapiFoldSuggestedConts = nameSpc.GetDefaultFolder(
Outlook.OlDefaultFolders.olFolderSuggestedContacts);
folderItems = mapiFoldSuggestedConts.Items;
for (int i = 1; folderItems.Count >= i; i++)
{
itemObj = folderItems[i];
if (itemObj is Outlook.ContactItem)
contItemLst.Add(itemObj as Outlook.ContactItem);
else
Marshal.ReleaseComObject(itemObj);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
finally
{
if (folderItems != null)
Marshal.ReleaseComObject(folderItems);
if (mapiFoldrConts != null)
Marshal.ReleaseComObject(mapiFoldrConts);
if (mapiFoldSuggestedConts != null)
Marshal.ReleaseComObject(mapiFoldSuggestedConts);
if (nameSpc != null) Marshal.ReleaseComObject(nameSpc);
}
return contItemLst;
}
var outlook = new Microsoft.Office.Interop.Outlook.Application();
NameSpace mapiNamespace = outlook.Application.GetNamespace("MAPI");
foreach (Store store in mapiNamespace.Stores)
{
try
{
var folder = store.GetRootFolder();
foreach(MAPIFolder subfolder in folder.Folders)
{
if ( subfolder.Name == "Inbox")
{
foreach(dynamic message in subfolder.Items.Restrict("[MessageClass]='IPM.Sharing'"))
{
if (message.Class == 104)//SharingItem
{
Folder sharedFolder = message.OpenSharedFolder();
if (sharedFolder.DefaultMessageClass == "IPM.Contact")
{
//this is your folder
}
}
}
}
}
}
catch (System.Exception ex)
{
continue;
}
}
I have the following code to enumerate all the email address of my contacts but I am not able to do so with the exception point at this line
Outlook.ContactItem oAppt = (Outlook.ContactItem)oItems;
Could someone help me out so that I can enumerate all the email address of my contacts in Microsoft outlook ?
namespace RetrieveContacts
{
public class Class1
{
public static int Main(string[] args)
{
try
{
Outlook.Application oApp = new Outlook.Application();
Outlook.NameSpace oNS = oApp.GetNamespace("mapi");
Outlook.MAPIFolder oContacts = oNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
Outlook.Items oItems = oContacts.Items;
Outlook.ContactItem oAppt = (Outlook.ContactItem)oItems;
for (int i = 0; i <= oItems.Count; i++)
{
System.IO.StreamWriter file = new System.IO.StreamWriter("C:\\EmailAddress.txt");
file.WriteLine(oAppt.Email1Address);
file.Close();
}
oNS.Logoff();
oAppt = null;
oItems = null;
oContacts = null;
oNS = null;
oApp = null;
}
catch (Exception e)
{
System.IO.StreamWriter file = new System.IO.StreamWriter("C:\\Exception.txt");
file.WriteLine(e);
file.Close();
}
return 0;
}
}
}
Without knowing Outlook's object model by heart I'd guess that Outlook.Items is a collection of ContactItems and thus cannot directly be cast to a single ContactItem. Other concerns:
You're creating and writing to your file within the loop, so it would get overwritten each time
You need to validate that the item is a ContactItem or your cast will throw an error.
the file access should be in a using block so it gets closed automatically if an Exception is thrown
Your for loop is going from 0 to Count when it should be going from 0 to Count-1 if the collection is 0-based
Try changing your loop to something like this:
Outlook.Items oItems = oContacts.Items;
using(System.IO.StreamWriter file = new System.IO.StreamWriter("C:\\EmailAddress.txt"))
{
for (int i = 0; i < oItems.Count; i++) // Note < instead of <=
{
// Will be null if oItems[i] is not a ContactItem
Outlook.ContactItem oAppt = oItems[i] as Outlook.ContactItem;
if(oAppt != null)
file.WriteLine(oAppt.Email1Address);
}
}
file.Close();
oNS.Logoff();