简体   繁体   English

打开默认邮件客户端以及附件

[英]Open default mail client along with a attachment

Hi I am working on a WPF application (using c#). 嗨,我正在使用WPF应用程序(使用c#)。

I need to have a functionality where users can send files (audio files) as attachments via email. 我需要有一个功能,用户可以通过电子邮件将文件(音频文件)作为附件发送。 I tried using Microsoft.Office.Interop.Outlook.Application namespace but it opens outlook and wont work if outlook is not installed on the client's computer. 我尝试使用Microsoft.Office.Interop.Outlook.Application命名空间但它打开outlook并且如果客户端的计算机上没有安装outlook,则无法工作。

I tried using SmtpClient() and MailMessage() classes of System.Net.Mail namespace but its not opening email client. 我尝试使用System.Net.Mail命名空间的SmtpClient()MailMessage()类,但它没有打开电子邮件客户端。 Its sending a mail through predefined server (might be a problem since I don't know what my client's default email domain is. This link has all the things I need and its working fine. 它通过预定义的服务器发送邮件(可能是一个问题,因为我不知道我的客户端的默认电子邮件域是什么。这个链接有我需要的所有东西,它的工作正常。

But there they used DllImport attribute and there are many issues that may arise (from what I can understand) from using this method. 但是在那里他们使用了DllImport属性,并且使用这种方法可能会产生很多问题(从我能理解的)。 I have no idea about managed and un-managed code so I am not able to understand what the problem is. 我不知道托管和非托管代码,所以我无法理解问题是什么。 Is it OK to follow the example in the above link. 可以按照上面的链接中的示例进行操作。 If not why? 如果不是为什么?

Can you tell or provide links on how to approach my problem 你能说出或提供如何处理我的问题的链接

We can make use of the fact that most email clients support the .EML file format to be loaded. 我们可以利用大多数电子邮件客户端支持要加载的.EML文件格式这一事实。

So if we Extend the System.Net.Mail.MailMessage Class in a way that it can be saved to the filesystem as an .EML file. 因此,如果我们以一种可以将其作为.EML文件保存到文件系统的方式扩展System.Net.Mail.MailMessage类。 The resulting file can be opened with the default mail client using Process.Start(filename) 可以使用Process.Start(filename)使用默认邮件客户端打开生成的文件

For this to work properly we have to add a line containing "X-Unsent: 1" to the .EML file. 为了使其正常工作,我们必须在.EML文件中添加一行包含“X-Unsent:1”的行。 This line tells the email client loading the .EML file the message must be presented in "New message" mode. 此行告诉电子邮件客户端加载.EML文件,消息必须以“新消息”模式显示。

Use the "addUnsentHeader" bool parameter of the extension method to add this line to the .EML file 使用扩展方法的“addUnsentHeader”bool参数将此行添加到.EML文件中

The extension method looks like this: 扩展方法如下所示:

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);
            }
        }
    }
}

Use the extension method like this: 使用这样的扩展方法:

        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);

Have you tried using System.Diagnostics.Process.Start() with an appropriate command line? 您是否尝试过使用具有适当命令行的System.Diagnostics.Process.Start()

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

The &attachment switch lets you specify a file name. &attachment开关允许您指定文件名。

Ok, I'm struggling to this working but allegedly it can be done. 好吧,我正在努力工作,但据称可以做到。 I'm currently reading through this monster and will get back to you. 我正在阅读这个怪物,并会回复你。

You can ask the Windows shell to open a mailto URL: 您可以要求Windows shell打开mailto URL:

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

You need to be using System.Diagnostics . 您需要using System.Diagnostics

You can set various parts of the message like subject and body as described in RFC 6068 您可以按照RFC 6068中的描述设置消息的各个部分,如主题和正文

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

Unfortunately, the mailto protocol does not support attachments even though some e-mail clients may have a way of handling that. 不幸的是,即使某些电子邮件客户端可能有办法处理它,mailto协议也不支持附件。

I used the following helper class. 我使用了以下助手类。

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;
}

Use the MAPI class as below. 使用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");

Reference: Code Project 参考: 代码项目

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM