Match EWS Conversation* to Outlook Add-in Conversation* - exchange-server

I wrote an add-in for Outlook years ago that adds entries to a database based on the Item's ConversationIndex/ConversationId properties. This works great and remains uniform across all clients interacting with the messages (e.g. "Bob" can see that "Mary" already processed this message because an entry with the ConversationIndex already exists).
I'm now trying to move this piece to a service (and connect via the EWS API) but I'm not having good luck matching these properties with the values coming from Outlook. For example:
The Outlook Add-In will give me the following values for a specific email I'm targeting:
ConversationID: 6B6369F5023EA646AA7BC161274BDAE8
ConversationIndex: 0101CF3C7EEC6B6369F5023EA646AA7BC161274BDAE8
However, from the EWS API I get the following:
ConversationID: AAQkADFhZThkNmJmLTlkODItNDQyZS1hM2YxLTQ2NWNkMTllYjhjOQAQAGtjafUCPqZGqnvBYSdL2ug=
ConversationIndex: new byte[]{1,1,207,60,126,236,107,99,105,245,2,62,166,70,170,123,193,97,39,75,218,232}
I recognize the first as a Base64 encoded string, however what I get decoded doesn't look like anything I recognize (or can decipher). Is there anyone familiar with this, or who can help to get these two values to align? I can only imagine these properties come from the exchange server is some fashion, but the Client probably performs some cleansing whereas the EWS API just gives me the raw value (less the Base64 for what I presume transport purposes given the XML medium).
If anyone is familiar with this or has done it before I would greatly appreciate any guidance.
Side Note:
There are probably better ways to identify emails but for now I'm stuck with trying to keep these two synonymous. Modifying the outlook add-in isn't really an option, and once I migrate a 1:1 translation to the server (and drop the add-in) I'll have flexibility changing how it work. But for now I need them to run side-by-side. I need to be able to see processes made within Outlook from the web server and vise-versa.

Just found out (I think).
Breakdown
With more Googling and a bit more effort I believe I was able to make them align 1:1 using the following:
ConversationId
This is apparently an assembled value made up of several properties. Luckily I was able to find a method Woodman posted re-implementing the original algorithm used by Outlook here. With some minor modifications (to work with EWS instead of Outlook) I was able to get it to work.
ConversationIndex
This turned out to simply be a matter of using the BitConverter (and removing the hyphens). Easy peasy.
Final Result:
public static class EwsEmailMessageExtensions
{
private const int c_ulConvIndexIDOffset = 6;
private const int c_ulConvIndexIDLength = 16;
private static ExtendedPropertyDefinition PidTagConversationIndexTracking = new ExtendedPropertyDefinition(0x3016, MapiPropertyType.Boolean);
// HUGE props to Woodman
// https://stackoverflow.com/a/21625224/298053
public static string GetOutlookConversationId(this EmailMessage emailMessage)
{
Boolean convTracking;
if (!emailMessage.TryGetProperty(PidTagConversationIndexTracking, out convTracking))
{
convTracking = true;
}
var convIndex = emailMessage.ConversationIndex;
byte[] idBytes;
if (convTracking && convIndex != null && convIndex.Length > 0)
{
// get Id from Conversation index
idBytes = new byte[c_ulConvIndexIDLength];
Array.Copy(convIndex, c_ulConvIndexIDOffset, idBytes, 0, c_ulConvIndexIDLength);
}
else
{
// get Id from Conversation topic
var topic = emailMessage.ConversationTopic;
if (string.IsNullOrEmpty(topic))
{
return string.Empty;
}
if (topic.Length >= 265)
{
topic = topic.Substring(0, 256);
}
topic = topic.ToUpper();
using (var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider())
{
idBytes = md5.ComputeHash(Encoding.Unicode.GetBytes(topic));
}
}
return BitConverter.ToString(idBytes).Replace("-", string.Empty);
}
public static String GetOutlookConversationIndex(this EmailMessage emailMessage)
{
var convIndex = emailMessage.ConversationIndex;
return BitConverter.ToString(convIndex).Replace("-", String.Empty);
}
}
Usage:
// Prep
ExchangeService service = new ExchangeService(...);
Folder inbox = Folder.bind(service, WellKnownFolderName.Inbox);
Item item = /* inbox.FindItems(...).First() */
// Implmentation
EmailMessage emailMessage = item as EmailMessage;
if (emailMessage != null)
{
String conversationId = emailMessage.GetOutlookConversationId();
String conversationIndex = emailMessage.GetOutlookConversationIndex();
/* ... */
}

Related

Change Outlook Personal Stationery via c#

I have just started learning how to program add-ins for Outlook using Visual Studio. I am having a hard time finding resources to read up on VSTO. How can I cannot modify the default fonts for "New mail messages" and "Replying or forwarding messages" under Personal Stationery in Outlook?
Revised my post to include the solution:
I am using code from this link (https://pcloadletter.co.uk/2010/05/19/outlook-default-font-and-signature/) and converted to c#. For those that are trying to do the same, here is how I did it.
private void SetFont()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\Office\16.0\Common\MailSettings", true);
// set the font in Outlook and then export it from the registry. Use that value for our code.
string ComposeFontSimple = #"3c,00,00,00,1f,00,00,f8,00,00,00,00,c8,00,00,00,00,00,
00,00,ff,ff,00,dd,00,22,41,72,69,61,6c,00,00,00,00,00,00,00,00,00,00,00,00,
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00";
byte[] arrComposeFontSimpleBin = ArrayHexToDec(ComposeFontSimple.Split(','));
key.SetValue("ComposeFontSimple", arrComposeFontSimpleBin, RegistryValueKind.Binary);
}
public static byte[] ArrayHexToDec(string[] arrHex)
{
byte[] arrDec = new byte[arrHex.GetUpperBound(0)];
for (int i = 0; i < arrHex.GetUpperBound(0); i++)
{
if (arrHex[i] == "00")
{
arrDec[i] = 0;
}
else
{
arrDec[i] = byte.Parse(arrHex[i], System.Globalization.NumberStyles.HexNumber);
}
}
return arrDec;
}
The settings are stored at HKCU\Software\Microsoft\Office\%s.0\Common\MailSettings.
The value you want is ReplyFontSimple - font size starts at offset 12, and name start at offset 0x1A (0x0 terminated string).
Outlook keeps its setting in the windows registry. Try using the Process Monitor for tracking where exactly Outlook saves its settings.
If we speak about making changes to Outlook mail items at runtime, the message body can be modified using three different ways:
Body.
HTMLBody.
The Word editor. The WordEditor property of the Inspector class returns an instance of the Word Document which represents the message body.
See Chapter 17: Working with Item Bodies for more information.

Send a mail from outlook by getting To list from SQl server

I am stuck with a issue from 5 days.
I need a way to attain following requirement.
mailing list is present in Database(SQL server)
I have a mail in Outlook
now i have to send mail to all the 200,000 mail ids in Database
**Note one mail can have only 200 mail IDs so
200,000/200=1000 mails **Note: this 200,000 count is not fixed it will decrease and increase>
like jhon#xyz.com will be present today , next day we may need not send to him
his name might be completely removed (so DL is not an option)
I need a way to automate this
All i have a sleep less nights and coffee cups on my desk
I work in ASP.net any PL which meets this need is fine.
I assume that you know how to create sql statement for what you need and how to retrieve data from database in .NET. This means that only issue is actually sending this from outlook.
Here is an article that describes this in detail and piece of code copied from there.
using System;
using System.Text;
using Outlook = Microsoft.Office.Interop.Outlook;
namespace OutlookAddIn1
{
class Sample
{
public static void SendEmailFromAccount(Outlook.Application application, string subject, string body, string to, string smtpAddress)
{
// Create a new MailItem and set the To, Subject, and Body properties.
Outlook.MailItem newMail = (Outlook.MailItem)application.CreateItem(Outlook.OlItemType.olMailItem);
newMail.To = to;
newMail.Subject = subject;
newMail.Body = body;
// Retrieve the account that has the specific SMTP address.
Outlook.Account account = GetAccountForEmailAddress(application, smtpAddress);
// Use this account to send the e-mail.
newMail.SendUsingAccount = account;
newMail.Send();
}
public static Outlook.Account GetAccountForEmailAddress(Outlook.Application application, string smtpAddress)
{
// Loop over the Accounts collection of the current Outlook session.
Outlook.Accounts accounts = application.Session.Accounts;
foreach (Outlook.Account account in accounts)
{
// When the e-mail address matches, return the account.
if (account.SmtpAddress == smtpAddress)
{
return account;
}
}
throw new System.Exception(string.Format("No Account with SmtpAddress: {0} exists!", smtpAddress));
}
}
}
What I would suggest is to skip using outlook unless that’s really necessary and send email by directly communicating with SMTP server.
Just search for “how to send email from C#” or something similar and you’ll find a ton of examples.

Outlook VSTO - determine correct 'email' signature based on user

I'm writing an Outlook 2010 add-in with VSTO, one part of which will automatically add the correct email signature to a new AppointmentItem. The issue I've come across is how to determine which signature is the correct one. For example, I have 2 email signatures set up in Outlook, which have rules on use based on which address my email is coming from. How can I access these rules?
My issue is not with finding the signature files, but in applying the correct rules based on the user's settings. Any ideas?
You can access the rules by using the code below. You can loop through them and get the rule type and actions
app is the current instance of the Outlook.Application
Outlook.Rules rules= app.Session.DefaultStore.GetRules();
foreach (Outlook.Rule r in rules)
{
}
I ended up solving this by creating a dictionary object with the key being the email address and the value as the filepath:
private Dictionary<string, string> signatureDictionary()
{
string sigDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Microsoft\Signatures";
Dictionary<string, string> returnValue = new Dictionary<string,string>();
Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676", false);
string[] str = key.GetSubKeyNames();
foreach (string s in str)
{
Microsoft.Win32.RegistryKey subKey = key.OpenSubKey(s, false);
if (subKey.GetValue("New Signature") != null)
{
returnValue.Add(System.Text.Encoding.Unicode.GetString(subKey.GetValue("Account Name") as
Byte[],0,(subKey.GetValue("Account Name") as Byte[]).Length - 2)
, Path.Combine(sigDataDir,System.Text.Encoding.Unicode.GetString(
subKey.GetValue("New Signature") as Byte[], 0, (subKey.GetValue("New Signature") as
Byte[]).Length - 2) + #".rtf"));
}
}
key.Close();
return returnValue;
}
This answer to a similar question initially pointed me in the right direction, and figuring out that the "New Signature" key is only populated when a signature has been set for that account. Undoubtedly there will be situations where this doesn't work, but it sorts it out for my current issue. Since I use the WordEditor when I'm editing emails in VSTO I use the RTF files in this function, but there are also .HTM and .TXT files in the same directory so you can use those if you prefer.

Windows Workflow Foundation 4.0 and Tracking

I'm working with the Beta 2 version of Visual Studio 2010 to get some advanced learning using WF4. I've been working with the SqlTracking Sample in the WF_WCF_Samples SDK, and have gotten a pretty good understanding of how to emit and store tracking data in a SQL Database, but haven't seen anything on how to query the data when needed. Does anyone know if there are any .Net classes that are to be used for querying the tracking data, and if so are there any known samples, tutorials, or articles that describe how to query the tracking data?
According to Matt Winkler, from the Microsoft WF4 Team, there isn't any built in API for querying the tracking data, the developer must write his/her own.
These can help:
WorkflowInstanceQuery Class
Workflow Tracking and Tracing
Tracking Participants in .NET 4 Beta 1
Old question, I know, but there is actually a more or less official API in AppFabric: Windows Server AppFabric Class Library
You'll have to find the actual DLL's in %SystemRoot%\AppFabric (after installing AppFabric, of course). Pretty weird place to put it.
The key classes to look are at are SqlInstanceQueryProvider, InstanceQueryExecuteArgs. The query API is asynchronous and can be used something like this (C#):
public InstanceInfo GetWorkflowInstanceInformation(Guid workflowInstanceId, string connectionString)
{
var instanceQueryProvider = new SqlInstanceQueryProvider();
// Connection string to the instance store needs to be set like this:
var parameters = new NameValueCollection()
{
{"connectionString", connectionString}
};
instanceQueryProvider.Initialize("Provider", parameters);
var queryArgs = new InstanceQueryExecuteArgs()
{
InstanceId = new List<Guid>() { workflowInstanceId }
};
// Total ruin the asynchronous advantages and use a Mutex to lock on.
var waitEvent = new ManualResetEvent(false);
IEnumerable<InstanceInfo> retrievedInstanceInfos = null;
var query = instanceQueryProvider.CreateInstanceQuery();
query.BeginExecuteQuery(
queryArgs,
TimeSpan.FromSeconds(10),
ar =>
{
lock (synchronizer)
{
retrievedInstanceInfos = query.EndExecuteQuery(ar).ToList();
}
waitEvent.Set();
},
null);
var waitResult = waitEvent.WaitOne(5000);
if (waitResult)
{
List<InstanceInfo> instances = null;
lock (synchronizer)
{
if (retrievedInstanceInfos != null)
{
instances = retrievedInstanceInfos.ToList();
}
}
if (instances != null)
{
if (instances.Count() == 1)
{
return instances.Single();
}
if (!instances.Any())
{
Log.Warning("Request for non-existing WorkflowInstanceInfo: {0}.", workflowInstanceId);
return null;
}
Log.Error("More than one(!) WorkflowInstanceInfo for id: {0}.", workflowInstanceId);
}
}
Log.Error("Time out retrieving information for id: {0}.", workflowInstanceId);
return null;
}
And just to clarify - this does NOT give you access to the tracking data, which are stored in the Monitoring Database. This API is only for the Persistence Database.

Saving/Organizing/Searching Outlook E-mail outside of Outlook

My company requires me to use Outlook for my E-mail. Outlook does virtually nothing the way I want to do it and it frustrates me greatly. (I'm not trying to start a flame war here, it must do exactly what thousands of CEO's want it to do, but I'm not a CEO.)
I would like to be able to automatically extract the thousands of E-mails and attachments currently in my Outlook account and save them in my own alternative storage format where I can easily search them and organize them the way I want. (I'm not requesting suggestions for the new format.)
Maybe some nice open source program already can do this... that would be great. Please let me know.
Otherwise, how can I obtain the message content and the attachments without going through the huge collection manually? Even if I could only get the message content and the names of the attachments, that would be sufficient. Is there documentation of the Outlook mail storage format? Is there a way to query Outlook for the data?
Maybe there is an alternative approach I haven't considered?
My preferred language to do this is C#, but I can use others if needed.
Outlook Redemption is the best thing currently to use that I have found. It will allow you to get into the messages and extract the attachments and the message bodies. i am using it now to do just that.
Here is some code I use in a class. I included the constructor and the processing function I use to save off the attachments. I cut out the code that is specific to my needs but you can get an idea of what to use here.
private RDOSession _MailSession = new RDOSession();
private RDOFolder _IncommingInbox;
private RDOFolder _ArchiveFolder;
private string _SaveAttachmentPath;
public MailBox(string Logon_Profile, string IncommingMailPath,
string ArchiveMailPath, string SaveAttPath)
{
_MailSession.Logon(Logon_Profile, null, null, true, null, null);
_IncommingInbox = _MailSession.GetFolderFromPath(IncommingMailPath);
_ArchiveFolder = _MailSession.GetFolderFromPath(ArchiveMailPath);
_SaveAttachmentPath = SaveAttPath;
}
public void ProcessMail()
{
foreach (RDOMail msg in _IncommingInbox.Items)
{
foreach (RDOAttachment attachment in msg.Attachments)
{
attachment.SaveAsFile(_SaveAttachmentPath + attachment.FileName);
}
}
if (msg.Body != null)
{
ProcessBody(msg.Body);
}
}
}
edit:
This is how I call it and what is passed
MailBox pwaMail = new MailBox("Self Email User", #"\\Mailbox - Someone\Inbox",
#"\\EMail - Incomming\Backup", #"\\SomePath");
If you want to extract your e-mails take a look at
Outlook Email Extractor
at codeproject
http://69.10.233.10/KB/dotnet/OutlookEmailExtractor.aspx
rob
www.filefriendly.com

Resources