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
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 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 am using below code to retrieve the different mail parameter from MS outlook 2010. but I am not able to get CC email address. CC property of MailItem class is returning Name, not email address.
NameSpace _nameSpace;
ApplicationClass _app;
_app = new ApplicationClass();
_nameSpace = _app.GetNamespace("MAPI");
object o = _nameSpace.GetItemFromID(EntryIDCollection);
MailItem Item = (MailItem)o;
string HTMLbpdyTest = Item.HTMLBody;
CreationTime = Convert.ToString(Item.CreationTime);
strEmailSenderEmailIdMAPI = Convert.ToString(Item.SenderEmailAddress);
strEmailSenderName = Item.SenderName;
Subject = Item.Subject;
string CCEmailAddress = Item.CC;
Please suggest, how can I get CC email addresses?
Loop through the MailItem.Recipients collection and for each Recipient object check its Type property; olCC is what you want. You can then read the Recipient.Address property.
EDIT: Off the top of my head.
foreach (Recipient recip in Item.Recipients)
{
if (recip.Type == OlMailRecipientType.olCC)
{
if (CCEmailAddress.length > 0) CCEmailAddress += ";";
CCEmailAddress += recip.Address;
}
}
I was inspired by #Dmitry's answer and tried a few things on my own to have these lines of code fix my problems and give me an array of the cc-ed addresses that are present in a given mail item.
public string[] GetCCAddress(MailItem mailItem)
{
string email;
Outlook.ExchangeUser exUser;
List <string> ccEmailAddressList = new List<string>();
foreach (Recipient recip in mailItem.Recipients)
{
if ((OlMailRecipientType)recip.Type == OlMailRecipientType.olCC)
{
email=recip.Address;
if (!email.Contains("#"))
{
exUser = recip.AddressEntry.GetExchangeUser();
email = exUser.PrimarySmtpAddress;
}
ccEmailAddressList.Add(email);
}
}
This statement if (!email.Contains("#")) is to avoid calling exUser.PrimarySmtpAddress on an actual email address and restrict that to entries such as " /O=EXG5/OU=EXCHANGE ADMINISTRATIVE GROUP (FYDIBOHF23SPDLT)/CN=RECIPIENTS/CN=Test88067"
public static int ConnectToOutlook()
{
try
{
Outlook.Application oApp = new Outlook.Application();
Outlook.NameSpace oNS = oApp.GetNamespace("mapi");
oNS.Logon(Missing.Value, Missing.Value, false, true);
Outlook.MAPIFolder oInbox = oNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox).Parent;
List<string> ccRecipient = new List<string>();
foreach (MAPIFolder folder in oInbox.Folders)
{
if (folder.FullFolderPath.Contains("Inbox"))
{
foreach (MAPIFolder subFolder in folder.Folders)
{
try
{
if (subFolder.FullFolderPath.Contains("Folder Name Inside Inbox"))
{
foreach (object folderItems in subFolder.Items)
{
if (folderItems is Outlook.MailItem)
{
Outlook.MailItem email_Msg = (Outlook.MailItem)folderItems;
Console.WriteLine("Subject=>" + email_Msg.Subject);
//Console.WriteLine("From=>" + email_Msg.SenderEmailAddress);
//Console.WriteLine("Cc=>" + email_Msg.CC);
//Console.WriteLine("Recipients=>" + email_Msg.Recipients[1].Address);
foreach (Recipient recipient in email_Msg.Recipients)
{
if ((OlMailRecipientType)recipient.Type == OlMailRecipientType.olCC)
{
Console.WriteLine("Cc=>" + recipient.AddressEntry.GetExchangeUser().PrimarySmtpAddress);
}
}
}
}
}
}
catch (System.Exception error)
{
Console.WriteLine();
Console.WriteLine(error.Message);
}
}
}
}
}
catch (System.Exception e)
{
Console.WriteLine("{0} Exception caught: ", e);
}
return 0;
}
Try
Item.CC.Address
or
((MailAddress)Item.CC).Address
I need to extract/export the lotus notes email attachment into file system. for that I wrote following method but each time I am receiving an error at line foreach (NotesItem nItem in items).. Can anybody please tell me what I am doing wrong ..
Thanks
Jwalin
public void GetAttachments()
{
NotesSession session = new NotesSession();
//NotesDocument notesDoc = new NotesDocument();
session.Initialize("");
NotesDatabase NotesDb = session.GetDatabase("", "C:\\temps\\lotus\\sss11.nsf", false); //Open Notes Database
NotesView inbox = NotesDb.GetView("By _Author");
NotesDocument docInbox = inbox.GetFirstDocument();
object[] items = (object[])docInbox.Items;
**foreach (NotesItem nItem in items)**
{
//NotesItem nItem = (NotesItem)o1;
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:\\temps\\export\\" + fileName);
}
}
}
You don't need to use the $File item to get the attachment name(s). Rather, you can use the HasEmbedded and EmbeddedObject properties of the NotesDocument class.
public void GetAttachments()
{
NotesSession session = new NotesSession();
//NotesDocument notesDoc = new NotesDocument();
session.Initialize("");
NotesDatabase NotesDb = session.GetDatabase("", "C:\\temps\\lotus\\sss11.nsf", false); //Open Notes Database
NotesView inbox = NotesDb.GetView("By _Author");
NotesDocument docInbox = inbox.GetFirstDocument();
// Check if any attachments
if (docInbox.hasEmbedded)
{
NotesEmbeddedObject attachfile = (NotesEmbeddedObject)docInbox.embeddedObjects[0];
if (attachfile != null)
{
attachfile.ExtractFile("C:\\temps\\export\\" + attachfile.name);
}
}
Ed's solution didn't work for me. Something about my Notes db design seems to have left the EmbeddedObjects property null even when the HasEmbedded flag is true. So I sort of combined the Ed's and Jwalin's solutions, modifying them to fetch all attachments from a Notes document.
NotesDocument doc = viewItems.GetNthEntry(rowCount).Document;
if (doc.HasEmbedded)
{
object[] items = (object[])doc.Items;
foreach (NotesItem item in items)
{
if(item.Name.Equals("$FILE"))
{
object[] values = (object[])item.Values;
doc.GetAttachment(values[0].ToString()).ExtractFile(fileSavePath + values[0].ToString());
}
}
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);
}