Retrieve StoreID for MailItem (within Outlook Selection) - c#

I need to iterate through the MailItem items within a Selection and read their EntryID and StoreID values in order to be able to retrieve the mail items again later (for background processing through Redemption). Is there a way of retrieving the StoreID for a MailItem directly?
I know that it may be retrieved through the StoreID property of its parent Folder; however, this seems quite inefficient, since it must instantiate (and release) the COM object for the parent folder for each item. Is there a more efficient way to achieve this?
Edit: I cannot use the Selection.Parent property since it was only introduced in Outlook 2007, and I need to support Outlook 2003. Additionally, Sue Mosher states:
The Parent object of a Selection would be an Explorer, so you could use Selection.Parent.CurrentFolder.Store.
However, when one performs a search across “All Outlook Items”, it seems that CurrentFolder would always point to a search folder within the main store, which is typically the Exchange mailbox:
\\Mailbox - <username>\search folders\All Outlook Items
Since the search may match items in other stores, the CurrentFolder.Store would not necessarily correspond to the store of the selected items, making this approach unreliable.

Instead of getting the parent for each item, could you get the parent once by using Selection.Parent?

Related

How To Only Retrieve Top X Items from Outlook Folder - Interop C#

I am writing a C# application where I want to loop through the 500 most recent emails in a given folder. The reason is because getting all the emails takes a long time using the below lines:
MAPIFolder folder = outlookApp.GetNamespace("MAPI").GetDefaultFolder(OlDefaultFolders.olFolderInbox);
List<MailItem> items = folder.Items.OfType<MailItem>().ToList();
However, I know that what I am searching for each time is always going to be a recent email, so there's no need to get an entire year's worth of emails each time (over 8000, and I get less emails than the average employee at my job).
So, is there a way to only retrieve a certain amount of emails from a folder with Microsoft.Office.Interop.Outlook? Thanks in advance.
Iterating over all items in the folder is not really a good idea:
List<MailItem> items = folder.Items.OfType<MailItem>().ToList();
Instead, you need to use the Find/FindNext or Restrict methods of the Items class. They allow getting only items that correspond to your conditions. Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Instead of getting recent 500 emails you may retrieve emails for a day, few days, week and etc. So, you can process items in bunch. For example:
criteria = "[ReceivedTime] > '" _
& Format$("6/12/20 3:30PM","General Date") & "'"
You may find the Filtering Items Using a Date-time Comparison article helpful.

Read/Write SharePoint User Field [Word 2010 VSTO]

I've been experimenting with reading SharePoint 2013 Site Column metadata from within a Word 2010 Application-level C# VSTO.
For testing I've set-up Site Columns for every type that SharePoint uses, then created a Document Content Type that ties to them all -- thus all these columns are embedded into the Word document (looks to be stored within customXml within the document file).
By reading from the _Document.ContentTypeProperties property within the VSTO's code, I can access most types, but I'm having difficulty accessing a 'Person or Group' Site Column's data -- I'm getting COM Exceptions attempting to read or write to an item's .Value property.
By looking at the XSD schema in customXml, I can see a single-value User column is made up of three values: DisplayName (type string), AccountType (type string) and AccountId (type UserId) -- however I don't see a way to read/write from/to this within the VSTO? Multi-value User columns appear to be completely different, and are made up of two string values: an ID (appears to be the SharePoint user's ID) and a string-based ID (or at least that's what I think the i:0#.w|domain\userid is, anyway).
Word itself can edit both single- and multi-valued User column data via the Document Panel, but only if Word is currently connected to SharePoint -- otherwise the functionality is disabled. I'd assume the same would be true for the VSTO, if I could access the values at all...
My two questions are:
Is there a way to read/write single- and multi-value User fields from within VSTO code (even if it's not via the _Document.ContentTypeProperties property)?
Is there a way to do Q1 when if not connected to SharePoint (if, say, the values are known to the code)?
(I've been somewhat overly verbose in case my workings so far are useful to someone else even if I get no answers; there doesn't seem to be a great amount of information about this anywhere)
With some provisos, I believe you can do read/update these fields using VSTO - although I haven't actually created a working example using VSTO, the same objects as I'd use in Word VBA are available - the code snippets below are VBA.
The person/group values that are displayed in the DIP are stored in a Custom XML Part, even when the SharePoint server is unavailable. So the problem is not modifying the values - it's a CRUD operation, in essence - but knowing what values you can use, particularly in the multi-valued case. If you know how to construct valid values (let's say you have an independent list of email addresses) then you can make the modifications locally. Personally, I don't know how I would construct a valid value for the multi-valued case so I'd basically have to contact the server.
So assuming you have the data you need to update locally...
When SharePoint serves a Word Document, it inserts/updates several Custom XML Parts. One contains a set of schemas (as you have discovered). Another contains the data. All you really need to do is access the correct Custom XML Part, find the XML Element corresponding to your SharePoint user/group column, then it's a CRUD operation on the subElements of that Element.
You can find the correct Custom XML Part using the appropriate namespace name, e.g.
Const metaPropDataUri as String = _
"http://schemas.microsoft.com/office/2006/metadata/properties"
Dim theDoc as Word.Document
Dim cxp as Office.CustomXMLPart
Dim cxps as Office.CustomXMLParts
Set theDoc = ActiveDocument
Set cxps = theDoc.CustomXMLParts.SelectByNamespace(metaPropDataUri)
If there is more than one part associate with that Namespace, I don't know for sure how to choose the correct one. AFAIK Word/Sharepoint only ever creates one, and experiments suggest that if there is another one, SharePoint works with the first one. So I use
Set cxp = cxps(1)
At this point you need to know the XML Element name of the person/group column. It may be the same as the external name (the one you can see in the SharePoint list), but if for example someone called the Sharepoint column "person group", the Element name will be "person_x0020_group". If the name isn't going to vary, you can get it from the schema XML as a one-off task. Or it may be easy to generate the correct element name from any given SharePoint name. Otherwise, you can get it dynamically from the Schema XML, which you can get (as a string) using
theDoc.ContentTypeProperties.SchemaXML
What you need to do then is find the element with attribute ma:displayName="the external name" and get the value of the name attribute. I would imagine that's quite straightforward using c#, a suitable XML object, and a bit of XPath, say
//xsd:element[#ma:displayName='person group'][1]/#name
which should return 'person_x0020_group'
You can then get the Element node for your data, e.g. something along the lines of
Dim cxn As Office.CustomXMLNode
Set cxn = cxp.SelectSingleNode("//*[name()='person_x0020_group'][1]")
Or you may find it's preferable to get the namespace Uri of the Elements in this Custom XML Part and use that to help you locate the correct node. The name is a long hex string assigned by SharePoint. You can get it from the Schema XML using, e.g.
//xsd:schema[1]/#targetNamespace
Once you have your node, you would use the known structures (i.e. the ones you have found in the Schemas) to get/modify/create child nodes as required.
of course you can. You should use the SharePoint Client-side Object model (CSOM) to manipulate SharePoint data from a location away from the server. The only thing you will need is the URL of your SharePoint site.
You can then connect through CSOM like this:
ClientContext context = new ClientContext("SITEURL");
Site site = context.Site;
Web web = context.Web;
context.Load(site);
context.Load(web);
context.ExecuteQuery();
See here an example to set a single user field:
First get the ID of the user through ensuring the username
u = context.Web.EnsureUser(UserOrGroupName);
context.Load(u);
context.ExecuteQuery();
To set the value, you can use this string format:
userid;#userloginname;#
To set the field use this:
item[myusercolumn] = "userid;#userloginname;#";
item.Update();
context.ExecuteQuery();
To set a multi user field, you can use the same code, just use ;# to concat the different usernames, such as:
item[myusercolumn] = "userid1;#userloginname1;#userid2;#userloginname2;#userid3;#userloginname3;#";
item.Update();
context.ExecuteQuery();
Hope this helps

Error using NameSpace.GetItemFromID with Outlook EntryID from Windows Search Index URL

When I extract an EntryID from Windows Search Index email items some of the EntryIDs always start with zeros eg
000000005559804AB84A6E49A3EBDF11D66E111724A33D00
000000005559804AB84A6E49A3EBDF11D66E111744A03D00
and I can supply call NameSpace.GetEntryFromID with the entryID as a single parameter and open the mail item
Where the EntryID does not start with zeros eg
EF0000005DF0733903ACFB4A90E9F84DF839CED8A42D5300
EF0000005DF0733903ACFB4A90E9F84DF839CED824E75200
The emails come from a different mailbox and calling NameSpace.GetEntryFromID with these strings as a single parameter gives a Message Not Found error, presumably because the StoreID for the different Mailbox is not the default. Can I extract the StoreID from the EntryID strings returned from Windows Search URLs to pass as the second parameter for GetEntryFromID?
I suppose the cause of the issue is not the store IDs. Each store have their own IDs. Even if you try to move items between stores in Outlook the EntryID property valued can be changed. Here is what MSDN states for the EntryID property:
A MAPI store provider assigns a unique ID string when an item is created in its store. Therefore, the EntryID property is not set for a Microsoft Outlook item until it is saved or sent. The EntryID changes when an item is moved into another store, for example, from your Inbox to a Microsoft Exchange Server public folder, or from one Personal Folders (.pst) file to another .pst file.
Anyway, if you do not specify the StoreID, GetItemFromID searches the default message store.
Can I extract the StoreID from the EntryID strings returned from Windows Search URLs to pass as the second parameter for GetEntryFromID?
Nope. Instead, you can get the Parent object which is represented by the Folder class and use the StoreID property.

Slow iterating thru Outlook.Items to find contacts

I am having an issue where I am trying to find at least one contact inside any of the outlook folders. I have a recursive function that iterates thru the items inside a folder and if the item is of type contact then we add it to a list. However, this code runs extremely slow when folders have a large number of records say 4000 items.
Is there any way just to get contacts or is there a way to make this code more efficient?
foreach (var item in folderBase.Items)
{
if (returnFirst && result.Count > 0)
break;
if ((item is Outlook.ContactItem))
{
result.Add((Outlook.ContactItem)item);
}
}
Firstly, storing 4000 live Outlook objects in a list is a bad idea: you will run out of RPC channels in case of online Exchange store at item 255. Store the entry ids and use them to call Namespace.GetItemFromID() when you actuqlly need it; then release it as soon as you are done.
Secondly, use MAPIFolder.GetTable - it will let you retrieve values from multiple items without actually opening them; perfect in your case. Try something like the following (VB script):
set Folder = Application.ActiveExplorer.CurrentFolder
set Table = Folder.GetTable("[MessageClass] = 'IPM.Contact' ")
Table.Columns.Add("EntryID")
while not Table.EndOfTable
set Row = Table.GetNextRow()
vEntryId = Row.Item(1)
Debug.Print vEntryId
wend
You need to use the Find/FindNext or Restrict methods of the Items class instead.
Read more about these methods and see the sample code in the following articles:
How To: Use Restrict method in Outlook to get calendar items
How To: Retrieve Outlook calendar items using Find and FindNext methods
You can use the MessageClass property of Outlook items to get the contact items only.

How to differentiate "Note" and "Mail" item in Lotus notes? Using Domino.dll

I have a Nsf file in which in one of folder there are some mail and notes items.
I am differentiating each item with "form" property.
In case of mails form type is "memo" but in above scenario Notes "form" type is again "memo"
How can i differentiate these two items?
thanx
Using the "form" item is not a safe way to distinguish emails from other "documents". In fact, a Notes data store does not really distinguish where a document came from - whether from a delivered email or a created document. However, you can use the fields RouteTimes and RouteServers to take a pretty good guess as to whether a given document was initiated from an email message. (The existence of either field will generally mean the document was delivered by the mail router).
If you want to compare the fields, I think the best way is to compare $ fields because they are usually reserved for internal use and should not be updated without a good reason. Therefore they are the most accurate fields of a form.
Check $MessageID, $MIMETrack or $UpdatedBy. This last one should be used with care but it should contains the mail server(s) if it is a mail.
In LN you can mock a the mail template in a note. So there is no sure way to distinguish a note from a mail UNLESS you know how the notes were created and use that info to build a test based on specific conditions. If you can control the note creation, I suggest to use a specific form to be able to distinguish each record type.
Of course you can check the properties (fields and values) of your notes and build a test "heuristically" (meaning rule-of-thumb)

Categories