65.9K
CodeProject 正在变化。 阅读更多。
Home

以编程方式向电子邮件添加附件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.63/5 (18投票s)

2006年1月18日

CPOL

3分钟阅读

viewsIcon

372054

downloadIcon

3456

一种以编程方式向电子邮件添加附件的技术。

引言

在阅读了 Stephane Rodriguez 的出色且非常巧妙的以编程方式向 Outlook 电子邮件添加附件的解决方案后,我意识到此功能是多么有用。很多时候我都需要一种方法将应用程序的文档发送给另一个用户。 显然,可以通过编写自己的对话框来模拟邮件客户端对话框来完成,但是您必须连接地址簿等等。 最好利用PC上已安装的现有邮件客户端软件。 我正好需要这个功能,以便应用程序具有文件->发送到功能 - 显然,它需要附件才能工作。

SendTo 或 MailTo

Stephane的文章提出了一个完全有效的观点,即使用 SendTo 方法在功能上优于使用 mailto 技巧。 不使用 mailto 的另一个令人信服的理由是它不支持附件。 RFC mailto 协议很简单,没有指定附件。

但是,您经常会看到代码试图像这样使用 mailto

mailto:shiver@metimbers.com?Subject=Ahoy there 
  shipmate&Body=Here's the shipping 
  manifest&Attach="D:\manifest.doc"

它可能不会附加文档,因为您可以自由使用电子邮件客户端来实施 mailto 协议并包含附件子句的解析。 您可能不知道PC上安装了什么邮件客户端,因此它可能并非总是有效 - Outlook 肯定不支持使用 mailto 添加附件。

那么,做这件事最好的(也是最简单的)方法是什么?

Stephane的解决方案很简洁。它通过使用未发布的邮件帮助程序 COM 对象来模拟拖放到 Outlook 中。但是,通过使用未发布的功能,您将受制于供应商(在本例中为 Microsoft)更改内容! 这就是发生的事情。 在 Win2K 上使用 Outlook 2002/2003 时,拖放代码可以毫无问题地工作,但在 XP 上运行时会引发“特权异常”。 推测是此操作系统和 Outlook 版本组合存在权限问题。

我需要一个支持附件的与邮件客户端版本无关的解决方案,因此我选择研究 MAPI。 事实证明,答案非常简单,它被包装在 CSendFileTo 类中。

#ifndef __SENDFILETO_H__
#define __SENDFILETO_H__

#include <mapi.h>

class CSendFileTo
{
public:
    bool SendMail(HWND hWndParent, 
         CString const &strAttachmentFileName, 
         CString const &strSubject=_T(""))
    {
        // The attachment must exist as a file on the system
        // or MAPISendMail will fail, so......
        if (strAttachmentFileName.IsEmpty())
            return false;

        // You may want to remove this check, but if a valid
        // HWND is passed in, the mail dialog will be made
        // modal to it's parent.
        if (!hWndParent || !::IsWindow(hWndParent))
            return false;

        HINSTANCE hMAPI = ::LoadLibraryA(_T("MAPI32.DLL"));
        if (!hMAPI)
            return false;

        // Grab the exported entry point for the MAPISendMail function
        ULONG (PASCAL *SendMail)(ULONG, ULONG_PTR, 
                      MapiMessage*, FLAGS, ULONG);
        (FARPROC&)SendMail = GetProcAddress(hMAPI, 
                              _T("MAPISendMail"));

        if (!SendMail)
            return false;

        TCHAR szFileName[_MAX_PATH];
        TCHAR szPath[_MAX_PATH];
        TCHAR szSubject[_MAX_PATH];
        ::StrCpy(szFileName, strAttachmentFileName.GetString());
        ::StrCpy(szPath, strAttachmentFileName.GetString());
        ::StrCpy(szSubject, strSubject.GetString());

        MapiFileDesc fileDesc;
        ::ZeroMemory(&fileDesc, sizeof(fileDesc));
        fileDesc.nPosition = (ULONG)-1;
        fileDesc.lpszPathName = szPath;
        fileDesc.lpszFileName = szFileName;

        MapiMessage message;
        ::ZeroMemory(&message, sizeof(message));
        message.lpszSubject = szSubject;
        message.nFileCount = 1;
        message.lpFiles = &fileDesc;

        // Ok to send
        int nError = SendMail(0, (ULONG_PTR)hWndParent, 
               &message, MAPI_LOGON_UI|MAPI_DIALOG, 0);

        if (nError != SUCCESS_SUCCESS && 
            nError != MAPI_USER_ABORT && 
            nError != MAPI_E_LOGIN_FAILURE)
              return false;

        return true;
    }
};

#endif

示例用法

这个微不足道的代码片段显示了它有多么容易使用。

#include "SendFileTo.h"
...
...

CSendFileTo sendTo;
sendTo.(m_hWnd, _T("c://documents//menu.doc"), 
                _T("Here's the lunch menu"));

...
...

一切都很简单,但有几点需要注意。

  • 如果附件不存在于文件系统上的文件中,则对 MAPISendMail 的调用将失败,并显示 MAPI_E_ATTACHMENT_NOT_FOUND。 因此,请在 SendMail 调用开始时进行检查。
  • 通过将父 HWND 传递给 MAPISendMail 函数,电子邮件客户端应该使发送邮件对话框模式化为给定的 HWND。 您可能想要删除此模态性(?)并简单地使用 HWND_DESKTOP
  • 当进行 MAPISendMail 调用时,它不会发送邮件,而只是弹出一个电子邮件客户端对话框,其中设置了可选的主题行并附加了附件。
  • 此代码编写为使用 WTL(它很棒)进行编译和工作,但同样也可以很好地使用 MFC。

我已经成功地在 Win2K 和 XP 上的 Outlook 2002 和 2003 上对此进行了测试。 我很想知道它是否可以与其他我无法访问的邮件客户端(如 Eudora 等)一起使用。

此类可能需要进行更多的错误检查,但我将其留给读者练习。

© . All rights reserved.