【问题标题】:Open default mail client along with a attachment打开默认邮件客户端以及附件
【发布时间】:2013-12-18 04:17:38
【问题描述】:

您好,我正在开发一个 WPF 应用程序(使用 c#)。

我需要一个功能,用户可以通过电子邮件将文件(音频文件)作为附件发送。 我尝试使用 Microsoft.Office.Interop.Outlook.Application 命名空间,但它会打开 Outlook,如果客户端计算机上未安装 Outlook,它将无法工作。

我尝试使用 System.Net.Mail 命名空间的 SmtpClient() 和 MailMessage() 类,但它没有打开电子邮件客户端。 它通过预定义的服务器发送邮件(可能是一个问题,因为我不知道我的客户的默认电子邮件域是什么。这个link 拥有我需要的所有东西并且它工作正常。

但是他们在那里使用了 DllImport 属性,并且使用这种方法可能会出现很多问题(据我所知)。我不知道托管和非托管代码,所以我无法理解问题所在。是否可以按照上面链接中的示例进行操作。如果不是为什么?

您能否告诉或提供有关如何解决我的问题的链接

【问题讨论】:

标签: c# wpf email


【解决方案1】:

我们可以利用大多数电子邮件客户端支持加载 .EML 文件格式这一事实。

因此,如果我们扩展 System.Net.Mail.MailMessage 类,使其可以作为 .EML 文件保存到文件系统。 可以使用默认邮件客户端使用 Process.Start(filename) 打开生成的文件

为了使其正常工作,我们必须在 .EML 文件中添加包含“X-Unsent: 1”的行。这一行告诉加载 .EML 文件的电子邮件客户端,消息必须以“新消息”模式呈现。

使用扩展方法的“addUnsentHeader”bool参数将此行添加到.EML文件中

扩展方法如下:

using System;
using System.IO;
using System.Net.Mail;
using System.Reflection;

namespace Fsolutions.Fbase.Common.Mail
{
    public static class MailUtility
    {
        //Extension method for MailMessage to save to a file on disk
        public static void Save(this MailMessage message, string filename, bool addUnsentHeader = true)
        {
            using (var filestream = File.Open(filename, FileMode.Create))
            {
                if (addUnsentHeader)
                {
                    var binaryWriter = new BinaryWriter(filestream);
                    //Write the Unsent header to the file so the mail client knows this mail must be presented in "New message" mode
                    binaryWriter.Write(System.Text.Encoding.UTF8.GetBytes("X-Unsent: 1" + Environment.NewLine));
                }

                var assembly = typeof(SmtpClient).Assembly;
                var mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");

                // Get reflection info for MailWriter contructor
                var mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);

                // Construct MailWriter object with our FileStream
                var mailWriter = mailWriterContructor.Invoke(new object[] { filestream });

                // Get reflection info for Send() method on MailMessage
                var sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);

                sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { mailWriter, true, true }, null);

                // Finally get reflection info for Close() method on our MailWriter
                var closeMethod = mailWriter.GetType().GetMethod("Close", BindingFlags.Instance | BindingFlags.NonPublic);

                // Call close method
                closeMethod.Invoke(mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { }, null);
            }
        }
    }
}

像这样使用扩展方法:

        var mailMessage = new MailMessage();
        mailMessage.From = new MailAddress("someone@yourdomain.com");
        mailMessage.Subject = "Your subject here";
        mailMessage.IsBodyHtml = true;
        mailMessage.Body = "<span style='font-size: 12pt; color: red;'>My HTML formatted body</span>";

        mailMessage.Attachments.Add(new Attachment("C://Myfile.pdf"));

        var filename = "C://Temp/mymessage.eml";

        //save the MailMessage to the filesystem
        mailMessage.Save(filename);

        //Open the file with the default associated application registered on the local machine
        Process.Start(filename);

【讨论】:

  • 这不行,它会在线抛出异常:sendMethod.Invoke(message, BindingFlags.Instance | .......
  • 我遇到了与@Marjan 相同的错误-但我尝试将其实现为一个“块”(而不是扩展System.Net.Mail.MailMessage)。该解决方案的正确实施方式是什么?
  • @derekantrican 这个方法在你知道发件人地址的情况下有效,mailMessage.From 必须在 mailMessage.Save 之前设置,所以使用这个时我们似乎应该先从默认的电子邮件客户端获取帐户方法?
  • Windows 10 邮件似乎不使用 X-Unsent 标头,只是将其作为非外发电子邮件打开
  • .eml 文件不可编辑。如何使其可编辑?
【解决方案2】:

您是否尝试过将System.Diagnostics.Process.Start() 与适当的命令行一起使用?

mailto:some.guy@someplace.com?subject=an email&body=see attachment&attachment="/files/audio/attachment.mp3"

&amp;attachment 开关可让您指定文件名。

好的,我正在努力完成这项工作,但据说可以完成。我目前正在阅读through this monster 并会尽快回复您。

【讨论】:

  • 我无法让我的电子邮件客户端处理attachment,正如我所指出的,RFC 6068 没有定义对附件的任何支持。
  • 我忘了提这个。我也尝试使用 mailto... 方法,但它的作用是在我的浏览器中打开一个新标签。从朋友的计算机上尝试时,Outlook 正在打开,但没有附件。
  • @Raj123 将来,消除您已经尝试过的明显答案将是一个很大的优势:D
  • @Gudsor 我尝试了 mailto 方法,但我的印象是这种方法无法发送附件。我尝试了您建议的方式,但正如我在浏览器中打开一个新标签之前所说的那样(我不知道 mailto 和我的浏览器之间的关系是什么)。我正在浏览您提供的链接,但我认为我的计算机可能有问题,因为其他系统正在打开它们的默认邮件客户端。
【解决方案3】:

您可以要求 Windows shell 打开一个 mailto URL:

var url = "mailto:someone@somewhere.com";
Process.Start(url);

你需要是using System.Diagnostics。

您可以设置邮件的各个部分,如RFC 6068 中所述的主题和正文

var url = "mailto:someone@somewhere.com?subject=Test&body=Hello";

不幸的是,mailto 协议不支持附件,尽管某些电子邮件客户端可能有处理附件的方法。

【讨论】:

  • 不幸的是,OP专门询问了附件。
  • 这不是 OP 的完美答案。但它做了我需要的。 :)
  • 它只是在 Chrome 浏览器中打开一个空白页面。
【解决方案4】:

我使用了以下帮助类。

class MAPI
{
    public bool AddRecipientTo(string email)
    {
        return AddRecipient(email, HowTo.MAPI_TO);
    }

    public bool AddRecipientCC(string email)
    {
        return AddRecipient(email, HowTo.MAPI_TO);
    }

    public bool AddRecipientBCC(string email)
    {
        return AddRecipient(email, HowTo.MAPI_TO);
    }

    public void AddAttachment(string strAttachmentFileName)
    {
        m_attachments.Add(strAttachmentFileName);
    }

    public int SendMailPopup(string strSubject, string strBody)
    {
        return SendMail(strSubject, strBody, MAPI_LOGON_UI | MAPI_DIALOG);
    }

    public int SendMailDirect(string strSubject, string strBody)
    {
        return SendMail(strSubject, strBody, MAPI_LOGON_UI);
    }


    [DllImport("MAPI32.DLL")]
    static extern int MAPISendMail(IntPtr sess, IntPtr hwnd,
        MapiMessage message, int flg, int rsv);

    int SendMail(string strSubject, string strBody, int how)
    {
        MapiMessage msg = new MapiMessage();
        msg.subject = strSubject;
        msg.noteText = strBody;

        msg.recips = GetRecipients(out msg.recipCount);
        msg.files = GetAttachments(out msg.fileCount);

        m_lastError = MAPISendMail(new IntPtr(0), new IntPtr(0), msg, how,
            0);
        if (m_lastError > 1)
            MessageBox.Show("MAPISendMail failed! " + GetLastError(), 
                "MAPISendMail");

        Cleanup(ref msg);
        return m_lastError;
    }

    bool AddRecipient(string email, HowTo howTo)
    {
        MapiRecipDesc recipient = new MapiRecipDesc();

        recipient.recipClass = (int)howTo;
        recipient.name = email;
        m_recipients.Add(recipient);

        return true;
    }

    IntPtr GetRecipients(out int recipCount)
    {
        recipCount = 0;
        if (m_recipients.Count == 0)
            return IntPtr.Zero;

        int size = Marshal.SizeOf(typeof(MapiRecipDesc));
        IntPtr intPtr = Marshal.AllocHGlobal(m_recipients.Count * size);

        int ptr = (int)intPtr;
        foreach (MapiRecipDesc mapiDesc in m_recipients)
        {
            Marshal.StructureToPtr(mapiDesc, (IntPtr)ptr, false);
            ptr += size;
        }

        recipCount = m_recipients.Count;
        return intPtr;
    }

    IntPtr GetAttachments(out int fileCount)
    {
        fileCount = 0;
        if (m_attachments == null)
            return IntPtr.Zero;

        if ((m_attachments.Count <= 0) || (m_attachments.Count >
            maxAttachments))
            return IntPtr.Zero;

        int size = Marshal.SizeOf(typeof(MapiFileDesc));
        IntPtr intPtr = Marshal.AllocHGlobal(m_attachments.Count * size);

        MapiFileDesc mapiFileDesc = new MapiFileDesc();
        mapiFileDesc.position = -1;
        int ptr = (int)intPtr;

        foreach (string strAttachment in m_attachments)
        {
            mapiFileDesc.name = Path.GetFileName(strAttachment);
            mapiFileDesc.path = strAttachment;
            Marshal.StructureToPtr(mapiFileDesc, (IntPtr)ptr, false);
            ptr += size;
        }

        fileCount = m_attachments.Count;
        return intPtr;
    }

    void Cleanup(ref MapiMessage msg)
    {
        int size = Marshal.SizeOf(typeof(MapiRecipDesc));
        int ptr = 0;

        if (msg.recips != IntPtr.Zero)
        {
            ptr = (int)msg.recips;
            for (int i = 0; i < msg.recipCount; i++)
            {
                Marshal.DestroyStructure((IntPtr)ptr,
                    typeof(MapiRecipDesc));
                ptr += size;
            }
            Marshal.FreeHGlobal(msg.recips);
        }

        if (msg.files != IntPtr.Zero)
        {
            size = Marshal.SizeOf(typeof(MapiFileDesc));

            ptr = (int)msg.files;
            for (int i = 0; i < msg.fileCount; i++)
            {
                Marshal.DestroyStructure((IntPtr)ptr,
                    typeof(MapiFileDesc));
                ptr += size;
            }
            Marshal.FreeHGlobal(msg.files);
        }

        m_recipients.Clear();
        m_attachments.Clear();
        m_lastError = 0;
    }

    public string GetLastError()
    {
        if (m_lastError <= 26)
            return errors[m_lastError];
        return "MAPI error [" + m_lastError.ToString() + "]";
    }

    readonly string[] errors = new string[] {
    "OK [0]", "User abort [1]", "General MAPI failure [2]",
            "MAPI login failure [3]", "Disk full [4]",
            "Insufficient memory [5]", "Access denied [6]",
            "-unknown- [7]", "Too many sessions [8]",
            "Too many files were specified [9]",
            "Too many recipients were specified [10]",
            "A specified attachment was not found [11]",
    "Attachment open failure [12]",
            "Attachment write failure [13]", "Unknown recipient [14]",
            "Bad recipient type [15]", "No messages [16]",
            "Invalid message [17]", "Text too large [18]",
            "Invalid session [19]", "Type not supported [20]",
            "A recipient was specified ambiguously [21]",
            "Message in use [22]", "Network failure [23]",
    "Invalid edit fields [24]", "Invalid recipients [25]",
            "Not supported [26]"
    };


    List<MapiRecipDesc> m_recipients = new
        List<MapiRecipDesc>();
    List<string> m_attachments = new List<string>();
    int m_lastError = 0;

    const int MAPI_LOGON_UI = 0x00000001;
    const int MAPI_DIALOG = 0x00000008;
    const int maxAttachments = 20;

    enum HowTo { MAPI_ORIG = 0, MAPI_TO, MAPI_CC, MAPI_BCC };
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiMessage
{
    public int reserved;
    public string subject;
    public string noteText;
    public string messageType;
    public string dateReceived;
    public string conversationID;
    public int flags;
    public IntPtr originator;
    public int recipCount;
    public IntPtr recips;
    public int fileCount;
    public IntPtr files;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiFileDesc
{
    public int reserved;
    public int flags;
    public int position;
    public string path;
    public string name;
    public IntPtr type;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiRecipDesc
{
    public int reserved;
    public int recipClass;
    public string name;
    public string address;
    public int eIDSize;
    public IntPtr entryID;
}

使用MAPI 类,如下所示。

MAPI mapi = new MAPI();

mapi.AddAttachment("c:\\temp\\file1.txt");
mapi.AddAttachment("c:\\temp\\file2.txt");
mapi.AddRecipientTo("person1@somewhere.com");
mapi.AddRecipientTo("person2@somewhere.com");
mapi.SendMailPopup("testing", "body text");

// Or if you want try and do a direct send without displaying the 
// mail dialog mapi.SendMailDirect("testing", "body text");

参考:Code Project

【讨论】:

  • 这种方式在我的环境中不起作用(W7,VS2013,从测试中执行):没有错误(code=0),没有打开 Outlook
  • 同样的问题,这段代码不起作用。 Win 10,VS2010,没有错误,没有打开。
  • 我们正在使用 MAPI 进行生产,我完全不推荐它。它会导致 HTML 邮件出现问题,并且如果某些内容与应有的不完全一致,通常会引发错误,从而使您搜索很少起作用的注册表修复程序。我们现在正在尝试 EML 方法。
  • 似乎 To、CC 和 BCC 做的事情都是一样的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-02
  • 1970-01-01
  • 1970-01-01
  • 2014-08-14
  • 1970-01-01
  • 2011-06-27
  • 1970-01-01
相关资源
最近更新 更多