如何在C#中使用Outlook MAPI打开.eml文件?

6
我有一个读取.msg文件并提取正文和附件的C#应用程序。但是,当我尝试加载.eml文件时,该应用程序会崩溃。我是这样加载文件的:
MailItem mailItem = (MailItem)outlookApp.CreateItemFromTemplate(msgFileName);
mailItem.SaveAs(fullFilename, OlSaveAsType.olHTML); // save body in html format
for(int i = 0; i < mailItem.Attachments.Count; i++)
    mailItem.Attachments[i].SaveAsFile(filename); // save attachments

这对.msg文件可以正常工作,但不适用于.eml文件。我不明白为什么.eml文件不起作用,因为我可以在Outlook 2010中打开.eml文件。
如何使用Outlook主互操作程序集加载.eml文件?

1
为什么需要使用MAPI加载.eml文件?由于.eml文件只是MIME消息,因此自己解析它应该不太难(在CodePlex搜索MIME解析器)。在这方面,您需要MAPI中的特定内容吗? - Kevin Hsu
这是因为我有一个使用MAPI分割.msg文件的应用程序,我认为我不需要进行更改,并且可以像处理.msg文件一样打开我的.eml文件,而无需编写新代码。 - CubaLibre
5个回答

8

1
修订版已经发布:轻松从.EML文件中检索电子邮件信息 -- 修订版 http://www.codeproject.com/Articles/76607/Easily-Retrieve-Email-Information-from-EML-Files-R - Dan Gøran Lunde
@danglund 经过修订的版本看起来更加复杂,由三个较大的 .cs 文件组成(其中一些包含无关紧要的内容),而且甚至无法编译。 - MGOwen

5
CreateItemFromTemplate 只能处理MSG/OFT文件。对于EML文件,您需要在代码中显式解析该文件或使用第三方库(如Redemption - 我是它的作者):
以下代码将使用Redemption (RDOSession对象)创建一个MSG文件,并将EML文件导入其中:
  set Session = CreateObject("Redemption.RDOSession")
  Session.MAPIOBJECT = outlookApp.Session.MAPIOBJECT
  set Msg = Session.CreateMessageFromMsgFile("C:\Temp\temp.msg")
  Msg.Import "C:\Temp\test.eml", 1024
  Msg.Save
  MsgBox Msg.Subject

您可以使用邮件对象(RDOMail)来访问其各种属性(主题,正文等)。


能否在没有Redemption的情况下导入eml文件? - CubaLibre
当然,如果您解析EML文件并逐个设置各种MailItem对象属性,则可以实现。 - Dmitry Streblechenko
嗨,我想使用这段代码,但我没有 .msg 文件。我需要一个虚假的 .msg 文件来导入 eml 文件吗? 如何直接导入/打开 eml 文件? - bobzer
上面的脚本创建了一个临时的MSG文件(您可以稍后删除它或在完成后调用Msg.Delete),以便它将有一些东西来导入EML文件。MAPI或Outlook对象模型不会原生地使用EML文件 - 您需要在Outlook文件夹中拥有一个消息或一个MSG文件。 - Dmitry Streblechenko

0
假设已安装Outlook...
对于仅有msg文件的情况,当Outlook正在运行/已经打开时,您可以使用OpenSharedItem。
Microsoft.Office.Interop.Outlook.Application appOutlook = new Microsoft.Office.Interop.Outlook.Application();
var myMailItem = appOutlook.Session.OpenSharedItem(strPathToSavedEmailFile) as Microsoft.Office.Interop.Outlook.MailItem;

对于 eml 文件或 msg 文件(Outlook 将打开并弹出):

var strPathToSavedEmailFile=@"C:\temp\mail.eml";
//Microsoft.Win32 namespace to get path from registry
var strPathToOutlook=Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\App Paths\outlook.exe").GetValue("").ToString();
//var strPathToOutlook=@"C:\Program Files\Microsoft Office\root\Office16\OUTLOOK.EXE";

string strOutlookArgs;
if(Path.GetExtension(strPathToSavedEmailFile)==".eml")
{
  strOutlookArgs =  @"/eml "+strPathToSavedEmailFile;  // eml is an undocumented outlook switch to open .eml files
}else
    {
     strOutlookArgs =   @"/f "+strPathToSavedEmailFile;
    }

Process p = new System.Diagnostics.Process();
p.StartInfo = new ProcessStartInfo()
{
    CreateNoWindow = false,
    FileName = strPathToOutlook, 
    Arguments = strOutlookArgs
};
p.Start();

//Wait for Outlook to open the file
Task.Delay(TimeSpan.FromSeconds(5)).GetAwaiter().GetResult();

Microsoft.Office.Interop.Outlook.Application appOutlook = new Microsoft.Office.Interop.Outlook.Application();
//Microsoft.Office.Interop.Outlook.MailItem myMailItem  = (Microsoft.Office.Interop.Outlook.MailItem)appOutlook.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);
var myMailItem = (Microsoft.Office.Interop.Outlook.MailItem)appOutlook.ActiveInspector().CurrentItem;

//Get the Email Address of the Sender from the Mailitem 
string strSenderEmail=string.Empty;
if(myMailItem.SenderEmailType == "EX"){
    strSenderEmail=myMailItem.Sender.GetExchangeUser().PrimarySmtpAddress;
}else{
    strSenderEmail=myMailItem.SenderEmailAddress;
}

//Get the Email Addresses of the To, CC, and BCC recipients from the Mailitem   
var strToAddresses = string.Empty;
var strCcAddresses= string.Empty;
var strBccAddresses = string.Empty;
foreach(Microsoft.Office.Interop.Outlook.Recipient recip in myMailItem.Recipients)
    {
    const string PR_SMTP_ADDRESS = @"http://schemas.microsoft.com/mapi/proptag/0x39FE001E";
    Microsoft.Office.Interop.Outlook.PropertyAccessor pa = recip.PropertyAccessor; 
    string eAddress = pa.GetProperty(PR_SMTP_ADDRESS).ToString(); 
    if(recip.Type ==1)
        {
        if(strToAddresses == string.Empty)
            {
            strToAddresses = eAddress;
            }else
                {
                strToAddresses = strToAddresses +","+eAddress;
                }
        };
    if(recip.Type ==2)
        {
        if(strCcAddresses == string.Empty)
            {
            strCcAddresses = eAddress;
            }else
                {
                strCcAddresses = strCcAddresses +","+eAddress;
                }       
        };
    if(recip.Type ==3)
        {
        if(strBccAddresses == string.Empty)
            {
            strBccAddresses = eAddress;
            }else
                {
                strBccAddresses = strBccAddresses +","+eAddress;
                }       
        };
    }
Console.WriteLine(strToAddresses);
Console.WriteLine(strCcAddresses);
Console.WriteLine(strBccAddresses);
foreach(Microsoft.Office.Interop.Outlook.Attachment mailAttachment in myMailItem.Attachments){
    Console.WriteLine(mailAttachment.FileName);
}
Console.WriteLine(myMailItem.Subject);
Console.WriteLine(myMailItem.Body);

0
为了从.eml文件创建MailItem,您可以执行以下两个步骤:首先打开Outlook进程实例,然后使用Outlook API创建MailItem。
  string file = @"C:\TestEML\EmlMail.eml";
  System.Diagnostics.Process.Start(file);
  Outlook.Application POfficeApp = (Outlook.Application)Marshal.GetActiveObject("Outlook.Application");  // note that it returns an exception if Outlook is not running
  Outlook.MailItem POfficeItem = (Outlook.MailItem)POfficeApp.ActiveInspector().CurrentItem; // now pOfficeItem is the COM object that represents your .eml file

这会导致显示EML文件,这可能不是OP想要的。 - Dmitry Streblechenko

0

尽管Outlook可以打开EML文件,但是没有办法通过VBA编程来实现。因此,我创建了这个VBA宏,它循环遍历某个文件夹并使用SHELL EXEC打开每个EML文件。可能需要几毫秒才能打开EML文件,因此VBA会等待直到ActiveInspector中有东西打开。最后,将此电子邮件复制到某个选择的文件夹中,并在成功的情况下删除原始EML文件。

请查看我的完整答案(和代码): https://stackoverflow.com/a/33761441/3606250


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接