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.
Related
I am exporting Outlook contacts with a custom form to CSV from a specific Contacts folder. This folder is not the default location. It is a large folder with almost 7,000 contacts. This is a shared mailbox in Office 365, but we can't use the Outlook or Graph APIs because of the custom data.
My code below works fine, except that after iterating through 200-800 contacts, I get this error: "RPC Server is unavailable. (Exception from HRESULT: 0x800706BA)."
I also tried exporting the folder to a .pst and accessing that folder on my local machine. The result is essentially the same. UPDATE: I've been watching Outlook.exe in the task manager, and it steadily increases its memory consumption to around 300MB before crashing. I've tried the same code on a laptop running Office 2010, and I get an "Out of Memory" error.
I get the error whether Outlook is open or closed, but closing Outlook during program operation will immediately trigger this error. Outlook either starts (or restarts) following this error. I've read a number of SO posts and articles related to this error, but I'm not sure exactly what's going on. Any help is appreciated.
UPDATE: Code has been updated to use for loops instead of foreach loops per Dmitry Streblechenko's suggestion.
using System;
using Microsoft.Office.Interop.Outlook;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace OutlookContacts
{
class Program
{
static void Main(string[] args)
{
var encoding = Encoding.UTF8;
string csvPath = #"myCSVPath";
Application outlook = new Application();
NameSpace ns = outlook.GetNamespace("MAPI");
MAPIFolder sharedContacts;
string recipientName = "myEmail#myDomain";
StringBuilder headerRow = new StringBuilder();
Recipient recip = ns.CreateRecipient(recipientName);
StreamWriter writer = new StreamWriter(csvPath, false, encoding);
recip.Resolve();
if (recip.Resolved)
{
try
{
//EntryID and StoreID of my folder
sharedContacts =
ns.GetFolderFromID(
"myEntryID",
"myStoreID"
);
Items contacts = sharedContacts.Items;
//Writing header row
ContactItem first = contacts.GetFirst();
var properties = first.ItemProperties;
for(int i = 0; i < properties.Count; i++)
{
try
{
headerRow.Append(string.Format("\"{0}\",", properties[i].Name));
}
catch (System.Exception ex)
{
headerRow.Append(string.Format("{0},", ex.Message));
}
}
headerRow.AppendLine(Environment.NewLine);
WriteToCSV(writer, headerRow);
Console.WriteLine("Header row written;");
Marshal.ReleaseComObject(properties);
Marshal.ReleaseComObject(first);
//Writing Records
for (int i = 1; i <= contacts.Count; i++)
{
object o = contacts[i];
if (o is ContactItem)
{
ContactItem contact = (ContactItem)o;
if (contact != null)
{
Console.Write(contact.FullName);
StringBuilder dataRow = new StringBuilder();
ItemProperties contactProps = contact.ItemProperties;
for (int j = 0; j < contactProps.Count; j++)
{
ItemProperty property = contactProps[j];
try
{
if (property.Value == null)
{
string value = "null,";
dataRow.Append(value);
}
//else if (property.Name == "Attachments")
//{
// //Attachment file names
// string attachment = "";
// for (int k = 1; k < contact.Attachments.Count; k++)
// {
// attachment = (string.Format("\"{0}\"; ", contact.Attachments[k].FileName));
// dataRow.Append(attachment);
// }
// dataRow.Append(",");
//}
else
{
string value = property.Value.ToString();
value = value.Replace("\r\n\r\n\r\n", "\r\n")
.Replace("\r\n\r\n", "\r\n")
.Replace("\"", "'");
value = (string.Format("\"{0}\",", value));
dataRow.Append(value);
}
}
catch (System.Exception ex)
{
string value = string.Format("{0}: {1},", property.Name, ex.Message);
dataRow.Append(value);
}
Marshal.ReleaseComObject(property);
}
dataRow.Append(Environment.NewLine);
WriteToCSV(writer, dataRow);
Marshal.ReleaseComObject(contactProps);
Marshal.ReleaseComObject(contact);
}
Marshal.ReleaseComObject(o);
counter++;
Console.WriteLine(": Written " + counter);
}
}
}
catch (System.Exception ex)
{
Console.WriteLine(dataRow.ToString(), ex.Message);
Console.ReadKey();
}
}
}
static void WriteToCSV(StreamWriter writer, StringBuilder row)
{
var data = row.ToString();
writer.WriteAsync(data);
}
}
}
Looks like you are running out of RPC channels. You need to avoid using foreach loops (they tend to keep all collection members referenced until the loop exits) and explicitly release all COM objects are soon as you are done with them.
Off the top of my head:
for(int i = 1; i <= contacts.Count; i++)
{
obejct o = contacts[i];
ContactItem contact = o as ContactItem;
if (o != null)
{
ItemProperties properties = contact.ItemProperties;
StringBuilder newLine = new StringBuilder();
for (int j = 1; j <= properties.Count; j++)
{
ItemProperty property = properties[j];
var value = "";
if (property.Value == null)
{
value = "null,";
Console.WriteLine(value);
newLine.Append(value);
}
else
{
value = property.Value.ToString() + ",";
newLine.Append(value);
}
Marshal.ReleaseComObject(property);
}
newLine.Append(Environment.NewLine);
WriteToCSV(writer, newLine);
Marshal.ReleaseComObject(properties);
Marshal.ReleaseComObject(contact);
}
Marshal.ReleaseComObject(o);
}
I am currently working on the below method which iterates through emails in my Inbox, but wonder how to filter them to focus on the ones with static subject such as : nice weather. Following searches I've thought folders.Items.Restrict() could help, however I've got condition is not valid on var fi = folder.Items.Restric("weather"). I've also tried
var fi = folder.Items.Find("[Subject] = weather"), to ensure the condition is valid,... but it ends up with no improvement.
Thanks in advance
static void IterateMessages(Outlook.Folder folder)
{
string[] extensionsArray = {".csv"};
//var fi = folder.Items;
var fi = folder.Items.Restrict("weather");
if (fi != null)
{
foreach (Object item in fi)
{
Outlook.MailItem mi = (Outlook.MailItem)item;
var attachments = mi.Attachments;
if (attachments.Count != 0)
{
if (!Directory.Exists(basePath)
{
Directory.CreateDirectory(basePath);
}
// Loop through each attachment
for (int i = 1; i <= mi.Attachments.Count; i++)
{
Console.WriteLine("Processing: {0}", mi.Attachments[i].FileName);
mi.Attachments[i].SaveAsFile(basePath);
Console.WriteLine("Attachment: {0}" + " Saved to Path", mi.Attachments[i].FileName);
}
}
}
}
}
Your Restrict parameter seems to be in a wrong format. Try this:
var fi = folder.Items.Restrict("#SQL=\"urn:schemas:httpmail:subject\" like '%weather%'";
More information
Hello i try to write an application for a club that makes it easier for older member to use outlook and to send emails. Just a friendly way so elderly People can easy write and see the stuff on screen. But i am a novice programmer and never used the EWS managed API before. What i want is to Synchronice the Global Address List to my programm so i can assign them to an object and do more stuff. But i dont have any clues anymore.
What i tried:
For my own local Contact List (works)
private void AsignValuetoClass(object sender, RoutedEventArgs e)
{
//For the connection
es.UseDefaultCredentials = true;
es.AutodiscoverUrl("max.mustermann#muster.at", RedirectionUrlValidationCallback);
//How many Contacts are in the folder
ContactsFolder contactsfolder = ContactsFolder.Bind(es, WellKnownFolderName.Contacts);
//To get a specific number of contacts
int numItems = contactsfolder.TotalCount < 50 ? contactsfolder.TotalCount : 50;
//object of the Itemview
ItemView view = new ItemView(numItems);
//return the stuff
FindItemsResults<Item> contactIds = es.FindItems(WellKnownFolderName.Contacts, view);
//loop throug the item
foreach (Item item in contactIds)
{
if (item is Contact)
{
//assign of the contact items
Contact contact = item as Contact;
//new list
List<Contact> testlist = new List<Contact>();
//Add the contacts
testlist.Add(contact);
//loop through contact list
foreach (Contact Liste in testlist)
{
//new object on every run
TestKlasse test = new TestKlasse();
//assign
test.id = Convert.ToString(contact.Id);
test.Vorname = contact.GivenName;
test.Nachname = contact.Surname;
}
Console.WriteLine("Some stupid Text");
}
}
}
To get the contacts from the GAL (dont work).
private void SearchContacts(object sender, EventArgs e)
{
//For the connection
es.UseDefaultCredentials = true;
es.AutodiscoverUrl("max.mustermann#muster.at", RedirectionUrlValidationCallback);
NameResolutionCollection nameResolutions = es.ResolveName(
"Contacts",
ResolveNameSearchLocation.DirectoryThenContacts,
true);
foreach (NameResolution nameResolution in nameResolutions)
{
ExpandGroupResults groupResults = es.ExpandGroup(nameResolution.Mailbox.Address);
foreach (EmailAddress member in groupResults.Members)
{
Console.WriteLine(member.Name + " <" + member.Address + ">");
}
}
}
I tried also the resolvename() stuff but its only for one contact or matching contacts. I need every Contact. Here is the code:
private void SearchContacts(object sender, EventArgs e)
{
//For the connection
es.UseDefaultCredentials = true;
es.AutodiscoverUrl("max.mustermann#muster.at", RedirectionUrlValidationCallback);
// Identify the mailbox folders to search for potential name resolution matches.
List<FolderId> folders = new List<FolderId>() { new FolderId(WellKnownFolderName.Contacts) };
// Search for all contact entries in the default mailbox contacts folder and in Active Directory Domain Services (AD DS). This results in a call to EWS.
NameResolutionCollection coll = es.ResolveName("Anderl", folders, ResolveNameSearchLocation.ContactsThenDirectory, false);
foreach (NameResolution nameRes in coll)
{
Console.WriteLine("Contact name: " + nameRes.Mailbox.Name);
Console.WriteLine("Contact e-mail address: " + nameRes.Mailbox.Address);
Console.WriteLine("Mailbox type: " + nameRes.Mailbox.MailboxType);
}
}
Any help would be great so thx for your time. And sorry for my bad english.
This might come a bit late but one way to overcome the 100 user limit would be to simply append each character of the alphabet to "SMTP:" within a loop like this:
private _exchangeSvc = new ExchangeService();
const string SMTP_PREFIX = "SMTP:";
const string ABC = "abcdefghijklmnopqrstuvwxyz";
public List<NameResolution> GetGAL()
{
var gal = new List<NameResolution>();
foreach (char c in ABC)
{
string ambiguousName = SMTP_PREFIX + c;
var nameResCollection = _exchangeSvc.ResolveName(
ambiguousName,
ResolveNameSearchLocation.DirectoryOnly,
false);
gal.AddRange(nameResCollection);
}
//Uncomment the line below if you find duplicates.
// gal = gal.Distict().ToList()
return gal;
}
This worked for me when I needed the GAL via EWS, I only had to retrieve ~400 users though.
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).
I am trying to access Attachment names form "$File" (Lotus Notes).
NotesView inbox = _serverDatabase.GetView("($Inbox)");
NotesDocument docInbox = inbox.GetFirstDocument();
NotesItem file = docInbox.GetFirstItem("$File");
String fileType = file.type.ToString();
( getting fileType value "ATTACHMENT" for mail containing attachments)
I am not getting solution given in:
How to Access attachments from Notes mail?
I got solution as:
object[] items = (object[])docInbox.Items;
foreach (NotesItem nItem in items)
{
if (nItem.Name == "$FILE")
{
NotesItem file = docInbox.GetFirstItem("$File");
string fileName = ((object[])nItem.Values) [0].ToString();
NotesEmbeddedObject attachfile = (NotesEmbeddedObject)docInbox.GetAttachment(fileName);
if (attachfile != null)
{
attachfile.ExtractFile("C:\\test\\" + fileName);
}
}
But here I am getting only first attachment value.
Can anyone help me out in this?
Try something like this:
NotesView inbox = _serverDatabase.GetView("($Inbox)");
NotesDocument docInbox = inbox.GetFirstDocument();
if(docInbox.HasEmbedded ) {
foreach (NotesEmbeddedObject o in docInbox.EmbeddedObjects) {
if ( o.Type == 1454 ) {
o.ExtractFile( "c:\samples\" & o.Source )
}
}
}
Here is a link to Lotus Notes Designer Help - Really good as you can search for Classes etc to find out what options you have.
http://publib.boulder.ibm.com/infocenter/domhelp/v8r0/index.jsp?topic=/com.ibm.help.domino.designer85.doc/DOC/H_WHAT_S_NEW_IN_RNEXT_CHAP.html
Show you all the methods and properties of various class.
Hi Preeti,
OK from the other code sample you are returning an array:
string fileName = ((object[])nItem.Values) [0].ToString();
Yet you are only selecting the first value, you need to recurse through the collection.
Try something like this.
foreach (object attachment in (object[])nItem.Values)
{
NotesEmbeddedObject attachfile = (NotesEmbeddedObject)docInbox.GetAttachment(attachment.ToString());
if (attachfile != null)
{
attachfile.ExtractFile("C:\\test\\" + attachment.ToString());
}
}
Josh
Your above code snippet is very helpful to me. So, I tried the to save all attachments and finally found the below solution.
NotesView nInboxDocs = NDb.GetView("$Inbox");
NDoc=nInboxDocs.GetFirstDocument();
while (NDoc != null)
{
if (NDoc.HasEmbedded && NDoc.HasItem("$File"))
{
// To save only first attachment //
//pAttachment = ((object[])NDoc.GetItemValue("$File"))[0].ToString();
//pAttachment = CurItem.ToString();
//NDoc.GetAttachment(pAttachment).ExtractFile(#"C:\Documents and Settings\Administrator\Desktop\" + pAttachment);
// To save all attachment //
object[] AllDocItems = (object[])NDoc.Items;
foreach (object CurItem in AllDocItems)
{
NotesItem nItem = (NotesItem)CurItem;
if (IT_TYPE.ATTACHMENT == nItem.type)
{
pAttachment = ((object[])nItem.Values)[0].ToString();
NDoc.GetAttachment(pAttachment).ExtractFile(#"C:\Documents and Settings\Administrator\Desktop\" + pAttachment);
}
}
}
NDoc = nInboxDocs.GetNextDocument(NDoc);
}