Send Message with Custom Properties with Redemption - c#

I’m using Redemption.dll to set custom properties to my messages with set_Filed() and get_field() in C#. Everything works perfectly until the moment I send my messages.
From Outlook I use RDOMail.Send() and this sent the message to the Drafts folder. Then I read in the Redemption FAQ that I should use the IMessage::Submit() method (that I couldn’t find anywhere in the dll for .NET) and then use DeliverNow(), method that I did use but to my surprise when I receive my messages I lose the properties I had set.
This is really completely critical to our project since if Outlook can’t send mails it’s worth nothing.
Here is part of my code.
private void adxOutlookEvents_ItemSend(object sender, AddinExpress.MSO.ADXOlItemSendEventArgs e)
{
try
{
RDOSessionClass _RDOSession= MessagesActions.GetRDOSession();
Outlook.MailItem _MailItem= e.Item as Outlook.MailItem;
RDOMail _RdoMail = MessagesActions.GetRDOMail(_RDOSession, _MailItem);
_RdoMail.Send(); // Send using Redeption
e.Cancel = true; // Only send using Redeption
if (_RdoMail != null && Marshal.IsComObject(_RdoMail))
Marshal.ReleaseComObject(_RdoMail);
Redemption.MAPIUtils _MAPIUtils = new MAPIUtils();
_MAPIUtils.DeliverNow(0, 0);
if (_MAPIUtils != null && Marshal.IsComObject(_MAPIUtils))
Marshal.ReleaseComObject(_MAPIUtils);
CurrentInspector.Close(Outlook.OlInspectorClose.olDiscard);
}
catch
{
}
}
Thanks.

When a message is sent through SMTP (unlike between 2 Exchange mailboxes in the same domain), the message is converted to MIME, and all MAPI specific properties are lost.
You can force Outlook to send the message in the TNEF (the infamous winmail.dat) format if yo uset a special named property called UseTnef:
RDOMail.Fields["http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/8582000B"] = true;

Related

Message Id (PR_INTERNET_MESSAGE_ID_W_TAG) Not Available in Event Handler After Sending An Email

I have an instance of Microsoft.Office.Interop.Outlook.Application and I add itemSendHandler to the Application.itemSend
Application.itemSend += itemSendHandler
I'm assuming inside the itemSendHandler, the email should already be sent and a Message Id should exist for the email. Yet the following code produces a null messageId:
private void itemSendEventHandler(object sentItem, ref bool Cancel)
}
string PR_INTERNET_MESSAGE_ID_W_TAG = "http://schemas.microsoft.com/mapi/proptag/0x1035001F";
PropertyAccessor propertyAccessor = ((MailItem)sentItem).PropertyAccessor;
// This is null? Why?
string messageId = (string)propertyAccessor.GetProperty(PR_INTERNET_MESSAGE_ID_W_TAG);
ThisAddIn.attemptToReleaseComObject(propertyAccessor);
{
But when I inspect the sent item immediately afterwards, through the code or through a tool (like OutlookSpy or MFCMAPI), the property exists. Why is messageId null during the send event handler?
You need to save the message first before accessing that property. Even then, PR_INTERNET_MESSAGE_ID might not be accessible in the cached mode - Outlook will not synchronize the item in the Sent Items folder with its online replica for the performance reasons, and only the online version of the message will have that property. You can open that message in the online mode using Extended MAPI (C++ or Delphi) or Redemption (any language) by using the MAPI_NO_CACHE flag, but there is no way to do that in OOM.

Open new email window in Windows desktop e-mail client from C#

I am trying to open a filled email window with the method below which is called from a separate STA thread.
private void SendMailMessage(object ignore)
{
MAPIHelperInterop.MapiMessage message = new MAPIHelperInterop.MapiMessage();
using(RecipientCollection.InteropRecipientCollection interopRecipients
= fRecipientCollection.GetInteropRepresentation())
{
message.Subject = fSubject;
message.NoteText = fBody;
message.Recipients = interopRecipients.Handle;
message.RecipientCount = fRecipientCollection.Count;
// Check if we need to add attachments
if(fFiles.Count > 0)
{
// Add attachments
message.Files = AllocateFileAttachments(out message.FileCount);
}
// Signal the creating thread (make the remaining code async)
fManualResetEvent.Set();
int error = MAPIHelperInterop.MAPISendMailW(IntPtr.Zero, IntPtr.Zero, message, 0x8, 0);
if(fFiles.Count > 0)
{
// Deallocate the files
DeallocateFileAttachments(message);
}
// Check for error
if(error != SUCCESS_SUCCESS)
{
LogMAPIError(error);
}
}
}
I have been testing this with Outlook and I keep getting error code 2 (MAPI_E_FAILURE) and nothing visible happens in Outlook (eventually it should work with any mail client, but Outlook is the main use case and so an extendable solution that works for Outlook would be a good first step). It works only if I either start Outlook as an administrator, or have Outlook closed when I run the code. I have tried calling MAPISendMailW with a handle to Outlook and with different combinations of flags but that did not work either.
The closest I have found to my problem is this https://social.msdn.microsoft.com/Forums/office/en-US/63e9f5b2-f5f2-4cf8-bdc2-ca1fad88ebe5/problem-with-outlook-and-mapisendmail-returns-mapiefailure-when-outlook-is-running?forum=outlookdev. In order to follow the suggested solution tried to run the SendMailMessage method in a separate AppDomain like this:
public void ShowDialog()
{
Evidence e = new Evidence();
e.AddHostEvidence(new Zone(SecurityZone.MyComputer));
AppDomain appDomain = AppDomain.CreateDomain("Outlook Launcher", e);
appDomain.DoCallBack(SendMailMessage);
}
If I use "SecurityZone.MyComputer" then I get the same error as before and if I use any other SecurityZone I get this error message: "Request for the permission of type 'System.Security.Permissions.ReflectionPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed." Maybe that is not what they suggested in the above post but this is anything I can think off.
Thank you for your help.
I have used MAPI in the past and it can be a pain to get working well across different Outlook versions.
A very simple work around is to use mailto links. The advantage is that it will work with different mail clients automatically.
Process.Start("mailto:hello#test.com&subject=This is the subject&body=This is the body");
This will open up your default mail client with the subject and body filled in.

Prevent MailItem.Reply from opening Inspector window in Outlook 2010?

I am building a shared-addin for Outlook.
Inside the code, I am creating a reply email using MailItem.Reply() method
and discarding it later. I am using this to get the sender email address
for emails coming through Exchange server.
It was working fine for Outlook 2007.
But for Outlook 2010, the Reply method seems to opening the mail editor window.
I am on windows 7.
Is there any way to suppress that window or write seperate code based on Outlook version?
If you plan on discarding the message - don't create it to begin with (don't use Reply() unless you intend on sending the message). You can use the Recipient class to resolve an Exchange users email address with minimal resource utilization.
string senderEmail = string.Empty;
Outlook.Recipient recipient = mailItem.Application.Session.CreateRecipient(mailItem.SenderEmailAddress);
if (recipient != null && recipient.Resolve() && recipient.AddressEntry != null)
{
Outlook.ExchangeUser exUser = recipient.AddressEntry.GetExchangeUser();
if (exUser != null && !string.IsNullOrEmpty(exUser.PrimarySmtpAddress))
senderEmail = exUser.PrimarySmtpAddress;
}

Outlook Addin accessing exchange recipient in offline mode?

I'm creating an Outlook addin using VS 2008 and C#. In order to function this addin goes through all the e-mail using Redemption and parses it.
I've recently run into the issue of somebody opening outlook without a network connection (network offline, unplugged, or it's mobile like a laptop and happens to not have connectivity at the moment). It seems to be in getting a list of the recipients.
System.Runtime.InteropServices.COMException (0x80040115): Error in IAddrBook::OpenEntry: MAPI_E_NETWORK_ERROR
Error: The connection to Microsoft Exchange is unavailable. Your network adapter does not have a default gateway.
Component: Microsoft Exchange Address Book
at Redemption.RDOAddressEntryClass.get_SMTPAddress()
This is happening within this code:
/// <summary>
/// Retrieves a list of recipient addresses from an RDOMail object
/// </summary>
/// <param name="rdoItem">The email to analyze</param>
/// <returns>A list of e-mail addresses</returns>
protected List<string> GetRecipients(RDOMail rdoItem)
{
RDORecipients recipients = rdoItem.Recipients;
List<string> recipientList = new List<string>();
if (recipients != null && recipients.Count > 0)
{
for (int i = 1; i <= recipients.Count; i++)
{
RDOAddressEntry addressEntry = recipients[i].AddressEntry;
if (addressEntry != null)
{
string recipient = addressEntry.SMTPAddress;
recipient = recipient.Trim();
if (recipient != null && recipient != String.Empty)
{
recipientList.Add(recipient);
}
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(addressEntry);
addressEntry = null;
}
}
}
if (recipients != null)
{
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(recipients);
recipients = null;
}
return recipientList;
}
So the question is, how do I get the recipients of an e-mail without needing to authenticate to or resolve from Exchange and it dying because there's no network connection?
EDIT: Alternatively - is there a way to cache the smtp e-mail addresses within outlook so that if it later goes offline it doesn't have to resolve the e-mail addresses?
I believe some store providers are wrappers around underlying PST stores. Therefore when accessing certain properties the provider will attempt to sync with the remote server. You should be able to stop this by unwrapping the store from the provider.
Note: that adding an item to an unwrapped store shouldn't persist that change to the server (in the case of IMAP4) for example.
Read more about the UnwrapStore property here at the Redemption website
In most cases PR_SMTP_ADDRESS property should be available in the recipient table (which is stored on the message itself rather than in GAL). You can access that property using RDORecipient.Fields[] - there is no reason to use RDORecipient.AddressEntry (which causes Redemption to call IAddrbook::OpenEntry, and the call can fail in the offline mode).
Look at the recipient table with OutlookSpy (I am its author) - click IMessage, go to the GetRecipientTable tab to make sure the PR_SMTP_ADDRESS property is present.

Outlook MailItem: How to distinguish whether mail is incoming or outgoing?

I am writing VSTO Outlook addin in C#, and I need to distinguish, whether given MailItem is incoming or outgoing (or neither, when it is for example a draft).
Is there some foolproof way to do this? Best solution I have now would be getting a list of recipients, cc's, and bcc's, loading email adresses from active accounts, and checking if those two lists intersect, but this seems quite fragile to me, and I hope that there is a better solution.
Use case: I'd like to get a relevant date for an email, which could be either ReceivedTime, or SentOn, but to know which one I should use, I beed to know whether a mail was sent or received.
Thank you for ideas :)
Came to this page because I was having same issue in VBA. Checking the parent folders is cumbersome, as a message can be held either several folders deep (and therefore you have to iterate up several folders) or the user may have changed the folder. An extreme example: the deleted items folder contains both incoming and outgoing mail items.
I have chosen a similar solution to another person (Adi Kini) above where I check the ReceivedByName (I think he chose ReceivedEntryID). The ReceivedByName property is always Null ("") for a sent message, wherever it currently lays. This method can find a sent item that has been dragged to the inbox!. It seems a fairly reliable method of checking.
It seems odd that such an apparently straightforward thing as checking whether mail is incoming or outgoing can trip us up!
I came here with the same problem. Since I will explicitly suggest that user moves the mail to some folder in my usecase, checking Parent would not help...
Regarding MailItem.Sent: are you sure that MailItem.Sent works this way? I just wrote a simple code to run through both my Inbox and SentItems and for almost all of them Sent is true. I assume this is really just an indication whether the mail has been sent (= is not draft)...
I resolved this problem by adding a new UserProperty after e-mail was sent. So when I need to check if e-mail was sent I check this property. This works even if e-mail was moved out of Sent folder. Of course, this works only for newly created e-mails, but you may add this property to all e-mails in Sent folder during first start. The only bug is that UserProperties are printed by default, but this can be overridden.
This is how I check mail type and it works even if mail is moved to any folder. This solution uses PROPERTY ACCESSOR which is available in outlook 2007. Below is the code
string PR_MAIL_HEADER_TAG = "http://schemas.microsoft.com/mapi/proptag/0x007D001E";
Outlook.PropertyAccessor oPropAccessor = mItemProp.PropertyAccessor;
string strHeader = (string)oPropAccessor.GetProperty(PR_MAIL_HEADER_TAG);
if (strHeader == "")
{
// MAIL IS OF TYPE SENTBOX
}
else
{
// MAIL IS OF TYPE INBOX
}
MailItem.sent is true for incoming too.
How about checking MailItem.ReceivedByEntryID. But i am sure this will fail (ReceivedByEntryID will be null for mails in inbox) if you say import from outlook express or maybe some other email program
Iterating thru mail accounts/senderemail may help as you said, but its not fool proof (like if you rename the email address)
Take a look at the MailItem's .Parent property - examine the folder properties to determine if it is the inbox, outbox, drafts, sent items, etc.
You can check if it's inside the Outlook.OlDefaultFolders.olFolderInbox or olFolderOutbox, then there should be other methods you can use to check if it's inside either of these folders!
Outlook.Application _application = new Outlook.Application();
Outlook.MAPIFolder folder = _application.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
For a simple sended/received control by SMTP address, i suggest an address-check approach.
It can be done in this way:
'Get mail address sender
Dim mailSender As String = GetSenderSMTPAddress(outMailItem)
'Get current user mail address
Dim mailUser As String = OutlookMail2DocScriba.GetUserSMTPAddress(oNameSpace.CurrentUser.AddressEntry)
'If sender and current user matches is a sended mail, otherwise received
If String.Compare(mailSender, mailUser, True) = 0 Then
Return "Sended"
Else
Return "Received"
End If
Public Shared Function GetSenderSMTPAddress(ByVal mail As Outlook.MailItem) As String
'http://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.oladdresslisttype.aspx
If mail Is Nothing Then
Throw New ArgumentNullException()
End If
If mail.SenderEmailType = "EX" Then
Dim sender As Outlook.AddressEntry = Nothing
Try
sender = mail.Sender
Catch ex As Exception
'Se non è stato in grado di reperire il sender (Outlook 2007),
'ignoro l'eccezione e procedo.
End Try
If sender IsNot Nothing Then
Return GetUserSMTPAddress(sender)
Else
Return Nothing
End If
Else
Return mail.SenderEmailAddress
End If
End Function
Public Shared Function GetUserSMTPAddress(ByVal sender As Outlook.AddressEntry) As String
'Now we have an AddressEntry representing the Sender
'http://msdn.microsoft.com/en-us/library/office/ff868214(v=office.15).aspx
Const EXCHANGE_USER_ADDRESS_ENTRY As Int32 = 0
Const EXCHANGE_REMOTE_USER_ADDRESS_ENTRY As Int32 = 5
Dim PR_SMTP_ADDRESS As String = "http://schemas.microsoft.com/mapi/proptag/0x39FE001E"
If sender.AddressEntryUserType = EXCHANGE_USER_ADDRESS_ENTRY OrElse _
sender.AddressEntryUserType = EXCHANGE_REMOTE_USER_ADDRESS_ENTRY Then
'Use the ExchangeUser object PrimarySMTPAddress
Dim exchUser As Object = sender.GetExchangeUser()
If exchUser IsNot Nothing Then
Return exchUser.PrimarySmtpAddress
Else
Return Nothing
End If
Else
Return TryCast(sender.PropertyAccessor.GetProperty(PR_SMTP_ADDRESS), String)
End If
End Function
I contradict SenderName vs CurrentUser, to distinguish between emails in inbox or sent folder.
Did you try MailItem.Sent property?
Its true for incoming, and false for outgoing.

Categories