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.
Related
I am currently working on a former colleague's project that deals with saving emails from Exchange to our ERP system.
But I have run into a strange problem. Sometimes when the system receives an email, that contains an email as an attachment. It throws an error because the DataTimeReceived date is not set. Even after a .Load();
private int HandleEmail(Item item, Folder moveToFolder, Folder moveToFailedFolder, Mailbox mailbox, int fatherId = 0, string uploaderEmail = "", bool isEmbeddedMail = false)
The way it work. is that the HandleEmail() method goes through the original email until it gets the if-check.
If the attachment is not a FileAttachment, and is an ItemAttachment. The following code is called.
else if (attachment is ItemAttachment)
{
var itemAttachment = attachment as ItemAttachment;
itemAttachment.Load(new PropertySet(ItemSchema.Attachments, ItemSchema.TextBody, EmailMessageSchema.Sender, EmailMessageSchema.DisplayCc, EmailMessageSchema.DateTimeReceived, EmailMessageSchema.From, ItemSchema.MimeContent, ItemSchema.Body, ItemSchema.TextBody, EmailMessageSchema.BccRecipients, ItemSchema.Attachments));
var item_ = itemAttachment.Item;
HandleEmail(item_, null, null, mailbox, (int)mailid, uploaderEmail, true); // The attached email is then looped through like it is an regular email instead of like an image.
}
In this check. It takes the item attachment and loads an additional property set, which contains some data that is needed.
Finally the method calls it self, but now with the Item Attachment as to read it like a regular email.
My issue is the fact, that some attached emails do not have the DatetimeReceived property set.
Instead this error is shown in its place.
Microsoft.Exchange.Webservice.Data.ServicesObjectPropertyException
And this expection message is thrown
You must load or assign this property before you can read its value
And I can't quite figure out why it only happens to some attached emails. When I look at the emails it self, it does have an received date. But for some reason I can't get it using .Load()
I've tried a few things, such as using .Load for both Item and ItemAttachment, both without getting anything worthwhile.
Tried looking into using the service.loadpropertiesforitems()
I've forgotten a few of the other things I've looked into, since I have been looking at this for a few days before the weekend aswell.
Two suggestions i would have is first try the EmailMessageSchema.DateTimeSent (which should be the same as DateTimeReceived) the difference maybe being caused when attaching a messages that was sent. The other thing would be enable tracing and have a look at the traces to see what is actually coming back from the server also maybe just try loading the BasePropertySet.FirstClassProperties rather then your custom set (just to test that property anyway)
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;
I'd like a free library for .NET to get attachments from an account (such as gMail, or others) via imap4 (not necessarely), and save them in a folder.
Ideally it would allow me to get a list of them, and download only some given ones (filtering by extension, name, and/or size) and be free.
I've already done this with a trial version of EAGetMail, but for the purpose of what i'm trying to attempt buying the unlimited version of this library isn't quite suitable (i didn't know that this functionality itself was one among the ones with limited time).
---[edit - Higuchi]---
I'm using the following code:
Dim cl As New Pop3Client()
cl.UserName = "marcelo.f.ramires#gmail.com"
cl.Password = "mypassword"
cl.ServerName = "pop.gmail.com"
cl.AuthenticateMode = Pop3AuthenticateMode.Pop
cl.Ssl = False
cl.Authenticate() //takes a while, but passes even if there's a wrong password
Dim mg As Pop3Message = cl.GetMessage(1) //gives me an exception: Message = "Pop3 connection is closed"
UPDATE: Setting the port to 995 gives me a "Response TimeOut" exception
As commented, I am having some issues while trying to connect and get the first e-mail. any help ?
Well, I know you specified IMAP4, but I figured I'd offer this anyway in case POP3 is an option, since it's been useful for me:
http://csharpmail.codeplex.com/
This library provides access to POP3 mail, which many e-mail services (including Gmail) do offer in addition to the newer IMAP.
The core class is Pop3Client, which provides access to POP3 functions such as ExecuteList, ExecuteTop, etc. I have used this for specifically what you are asking about -- scanning for and downloading attachments.
If you decide this is something you could use after all and need further guidance, let me know.
UPDATE: In response to your updated question, I have just a few preliminary suggestions:
Consider setting the Pop3Client.Port property to 995. I know this is what Gmail uses for POP3.
The Pop3Client.Authenticate method returns a bool value indicating whether or not authentication was successful. You can check this value after calling the method to know whether it will be possible to progress further.
UPDATE 2: I tried this at home with the following settings and it worked for me:
Using client As New Pop3Client
client.UserName = "username#gmail.com"
client.Password = "[insert password here]"
client.ServerName = "pop.gmail.com"
client.AuthenticateMode = Pop3AuthenticateMode.Pop
client.Ssl = True ' NOTICE: in your example code you have False here '
client.Port = 995
client.Authenticate()
Dim messageList = client.ExecuteList()
Console.WriteLine("# Messages: {0}", messageList.Count)
End Using
Try these settings and see if they work for you.
UPDATE 3: One more thing! Have you made sure to enable POP for your Gmail account? If not, you need to do that!
From your Gmail inbox, click "Settings" (top right).
From the Settings page, click the tab labeled "Forwarding and POP/IMAP."
In the POP Download section, select one of the radio buttons to enable POP mail.
Click "Save Changes" at the bottom.
The use case is simple. At a certain point of time, I need to be able to show the user his familiar compose email dialog (Outlook or other) with
fields like from, to, Subject already filled up with certain application determined values.
The email would also have an attachment along with it.
The mail should not be sent unless the user explicitly okays it.
I did this once back in the ol' VB6 days.. can't figure out how now.. I just remember that it was quite easy.
Managed app, C#, .net 3.0+
Update#1: Yeah seems like mailto removed support for attachments (as a security risk?). I tried
You need to include ShellExecute signature as described here. All I got from this was a 5 SE_ERR_ACCESSDENIED and a 2 just for some variety
string sMailToLink = #"mailto:some.address#gmail.com?subject=Hey&body= yeah yeah yeah";
IntPtr result = ShellExecute(IntPtr.Zero, "open", sMailToLink, "", "", ShowCommands.SW_SHOWNORMAL);
Debug.Assert(result.ToInt32() > 32, "Shell Execute failed with return code " + result.ToInt32());
The same MailtoLink works perfectly with Process.Start... but as long as thou shalt not mention attachments.
System.Diagnostics.Process.Start(sMailToLink);
The other options are using the Outlook Object model to do this.. but I've been told that this requires you to add assembly references based to the exact version of Outlook installed. Also this would blow up if the user doesn't prefer MS for email.
The next option are Mapi and something called Mapi33.. Status still IN PROGRESS. Ears still open to suggestions.
You can create a process object and have it call "mailto:user#example.com?subject=My+New+Subject". This will cause the system to act on the mailto with its default handler, however, while you can set subjects and such this wont handle adding an attachment. I'll freely admit im not entirely sure how you'd go about forcing an attachment without writing some mail plugin.
The process code is:
System.Diagnostics.Process.Start("mailto:user#example.com?subject=My+New+Subject");
It's probably not the most efficient or elegant way, but shelling a "mailto:" link will do what you want, I think.
EDIT: Sorry, left out a very important "not".
Since mailto does not support attachments, and since MAPI is not supported within managed code, your best bet is to write (or have someone write) a small non-managed program to call MAPI functions that you can call with command-line arguments. Pity that .NET does not have a cleaner alternative.
See also : MAPI and managed code experiences?
Could it be that you used the mailto: protocol?
Almost all of what you highlight can be done, but I am quite sure, that you cant do attachments.
Microsoft MailTo Documentation
Starting process with mailto: arguments is the simplest approach. Yet, it does not allow anything more or less complex.
Slightly different approach involves creating email template and then feeding it to the Process.Start:
var client = new SmtpClient();
var folder = new RandomTempFolder();
client.DeliveryMethod =
SmtpDeliveryMethod.SpecifiedPickupDirectory;
client.PickupDirectoryLocation = folder.FullName;
var message = new MailMessage("to#no.net",
"from#no.net", "Subject","Hi and bye");
// add attachments here, if needed
// need this to open email in Edit mode in OE
message.Headers.Add("X-Unsent", "1");
client.Send(message);
var files = folder.GetFiles();
Process.Start(files[0].FullName);
Scenarios for the default email handler:
Outlook express opens
Windows: Outlook - does not handle by default, Outlook Express is called instead
Windows: The Bat! - message is opened for viewing, hit Shift-F6 and Enter to send
I've also tested with Mono and it worked more or less.
Additional details are available in this post: Information integration - simplest approach for templated emails
PS: in the end I went for slightly more complex scenario:
Defined interface IEmailIntegraton
Code above went into the DefaultEmailIntegration
Added implementations for OutlookEmailIntegration (automation) and theBat! email integration (using their template format).
Allowed users of the SmartClient to select their scenario from the drop-down (alternatively this could've been implemented as "Check the system for the default email handler and decide automatically")
You're making the assumption that they will have an email client installed, of course.
The option I've taken in the past (in a corporate environment where everyone has at least one version of Outlook installed) was to use the Outlook interop - you only need to reference the earliest version you need to support.
You could look at P/Invoking MAPISendDocuments (which I'd try and avoid, personally), or the other option would be to create your own "compose" form and use the objects from the System.Net.Mail namespace.
you can use a trick if you intend to use Outlook[this code is based on outlook 2010[v14.0.0.]]
Create Outlook MailItem
and transmit file (ie download)
if user opens the file (.msg) the compose message dialog opens automatically
here is the code ...
Microsoft.Office.Interop.Outlook.Application outapp = new Application();
try
{
_NameSpace np = outapp.GetNamespace("MAPI");
MailItem oMsg = (MailItem)outapp.CreateItem(OlItemType.olMailItem);
oMsg.To = "a#b.com";
oMsg.Subject = "Subject";
//add detail
oMsg.SaveAs("C:\\Doc.msg", OlSaveAsType.olMSGUnicode);//your path
oMsg.Close(OlInspectorClose.olSave);
}
catch (System.Exception e)
{
status = false;
}
finally
{
outapp.Quit();
}
then transmit the file you created say "Doc.msg"
string filename ="Doc.msg";//file name created previously
path = "C:\\" + filename; //full path ;
Response.ContentType="application/outlook";
Response.AppendHeader("Content-Disposition", "filename=\"" + filename + "\"");
FileInfo fl = new FileInfo(path);
Response.AddHeader("Content-Length", fl.Length.ToString());
Response.TransmitFile(path,0,fl.Length);
Response.End();
I'm working on a windows client written in WPF with C# on .Net 3.5 Sp1, where a requirement is that data from emails received by clients can be stored in the database. Right now the easiest way to handle this is to copy and paste the text, subject, contact information and time received manually using an arthritis-inducing amount of ctrl-c/ctrl-v.
I thought that a simple way to handle this would be to allow the user to drag one or more emails from Outlook (they are all using Outlook 2007 currently) into the window, allowing my app to extract the necessary information and send it to the backend system for storage.
However, a few hours googling for information on this seem to indicate a shocking lack of information about this seemingly basic task. I would think that something like this would be useful in a lot of different settings, but all I've been able to find so far have been half-baked non-solutions.
Does anyone have any advice on how to do this? Since I am just going to read the mails and not send anything out or do anything evil, it would be nice with a solution that didn't involve the hated security pop ups, but anything beats not being able to do it at all.
Basically, if I could get a list of all the mail items that were selected, dragged and dropped from Outlook, I will be able to handle the rest myself!
Thanks!
Rune
I found a great article that should do exactly what you need to.
UPDATE
I was able to get the code in that article working in WPF with a little tweaking, below are the changes you need to make.
Change all references from System.Windows.Forms.IDataObject to System.Windows.IDataObject
In the OutlookDataObject constructor, change
FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);
To
FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("_innerData", BindingFlags.NonPublic | BindingFlags.Instance);
Change all DataFormats.GetFormat calls to DataFormats.GetDataFormat
Change the SetData implementation from
public void SetData(string format, bool autoConvert, object data)
{
this.underlyingDataObject.SetData(format, autoConvert, data);
}
TO
public void SetData(string format, object data, bool autoConvert)
{
this.underlyingDataObject.SetData(format, data, autoConvert);
}
With those changes, I was able to get it to save the messages to files as the article did. Sorry for the formatting, but numbered/bulleted lists don't work well with code snippets.
I found a lot of solutions suggesting you use the “FileGroupDescriptor” for all the file names and the “FileContents” on the DragEventArgs object to retrieve the data of each file. The “FileGroupDescriptor” works fine for the email message names, but “FileContents” returns a null because the implementation of the IDataObject in .Net cannot handle the IStorage object that is returned by COM.
David Ewen has a great explanation, excellent sample and code download that works great at http://www.codeproject.com/KB/office/outlook_drag_drop_in_cs.aspx.
In your Xaml you need to set up your Event:
<TextBlock
Name="myTextBlock"
Text="Drag something into here"
AllowDrop="True"
DragDrop.Drop="myTextBlock_Drop"
/>
Once you have Set AllowDrop = True and Set you drop event then go to the code behind and set up your event:
private void myTextBlock_Drop(object sender, DragEventArgs e)
{
// Mark the event as handled, so TextBox's native Drop handler is not called.
e.Handled = true;
Stream sr;
//Explorer
if (e.Data.GetDataPresent(DataFormats.FileDrop, true))
//Do somthing
//Email Message Subject
if (e.Data.GetDataPresent("FileGroupDescriptor"))
{
sr = e.Data.GetData("FileGroupDescriptor") as Stream;
StreamReader sr = new StreamReader(sr2);//new StreamReader(strPath, Encoding.Default);
//Message Subject
string strFullString = sr.ReadToEnd();
}
}
If you wish to break it down further you can use:
FILEDESCRIPTOR or FILECONTENTS as outline in the following article
your other option is to tie into outlooks MS Office Primary Interop Assemblies and break the message apart that way.
I think Shell Style Drag and Drop in .NET (WPF and WinForms) can help you. Once you can respond to drag drop using the COM Interfaces, you should be able to get the data out of outlook.
I assume that you have an Exchange server running behind Outlook.
What you can do is to retrieve the mail from the Exchange server and store its location in your database based on the mail's EntryID and StoreID. Here's a VB.Net snippet:
Imports Microsoft.Office.Interop
Public Class OutlookClientHandler
Private _application As Outlook.Application
Private _namespace As Outlook.NameSpace
Public Sub New()
If Process.GetProcessesByName("outlook".ToLower).Length > 0 Then
_application = New Outlook.Application
Else
Dim startInfo As ProcessStartInfo = New ProcessStartInfo("outlook.exe")
startInfo.WindowStyle = ProcessWindowStyle.Minimized
Process.Start(startInfo)
_application = New Outlook.Application
End If
End Sub
' Retrieves the specified e-mail from Outlook/Exchange via the MAPI
Public Function GetMailItem(ByVal entryID as String, ByVal storeID as String) As Outlook.MailItem
_namespace = _application.GetNamespace("MAPI")
Dim item As Outlook.MailItem
Try
item = _namespace.GetItemFromID(entryID, storeID)
Catch comex As COMException
item = Nothing ' Fugly, e-mail wasn't found!
End Try
Return item
End Function
End Class
I guess you are comfortable with using the MAPI, otherwise you can read up here:
http://msdn.microsoft.com/en-us/library/cc765775(v=office.12).aspx
To retrieve the selected e-mails from outlook:
Public Function GetSelectedItems() As List(Of Object)
Dim items As List(Of Object) = New List(Of Object)
For Each item As Object In _application.ActiveExplorer().Selection
items.Add(item)
Next
Return items
End Function
After you've retrieved the e-mails from Outlook you can just push them into your database! Save their EntryID and StoreID (you might want to store their parent's (the folder's) EntryID and StoreID as well).