Delphi 程序如何通过默认电子邮件客户端发送带有附件的电子邮件?

发布于 2024-08-15 19:42:28 字数 3353 浏览 8 评论 0原文

在我的程序中,我正在撰写一封电子邮件,并使用用户计算机上安装的默认电子邮件客户端软件发送。

我已经编写了邮寄地址、主题、多行正文,并且有几个附件要包含在内。

我几乎使用 mailto 和 ShellExecute 完成了这项工作,如下所示:

  Message := 'mailto:[email protected]'
    + '?subject=This is the subjectBehold Error Report'
    + '&body=This is line 1' + '%0D%0A'
    + 'This is line 2' + '%0D%0A'
    + 'This is line 3'
    + '&Attach=c:\file1.txt';
  RetVal := ShellExecute(Handle, 'open', PChar(Message), nil, nil, SW_SHOWNORMAL);
  if RetVal <= 32 then
    MessageDlg('Cannot find program to send e-mail.', mtWarning, [mbOK], 0);

在 Windows Vista 计算机上使用 Delphi 2009,这将打开一个 Microsoft Mail“创建邮件”窗口,其中“收件人”、“主题”和“正文”填写正确。但是该文件不会附加。

当我对此进行研究时,我注意到一些评论指出该技术并不适用于所有邮件客户端。然而,大多数评论都相当古老,因为我意识到这是一种非常古老的技术。

然后我发现 Zarko Gajic 说“这种方法可以,但是您无法通过这种方式发送附件”。

我还看到过 Windows Simple Mail API (MAPI),但 Zarko 表示,只有最终用户拥有符合 MAPI 的电子邮件软件时,该功能才有效。有关在 Delphi 中使用 MAPI 的技术已有详细记录(例如 发送电子邮件使用mapi发送邮件),但它们都有免责声明,即MAPI并不总是随Windows一起安装。

此外,我真的希望该消息首先出现在用户的默认电子邮件程序中,这样他们就可以将其作为电子邮件记录的一部分,并且可以对其进行编辑并决定是否以及何时发送。我不确定 MAPI 是如何工作的以及它是否能做到这一点。

所以我的要求是:

  1. 在用户的邮件程序中显示电子邮件。

  2. 允许一个或多个附件。

  3. (希望)与 XP 及以上任何 Windows 计算机(即 XP、Vista 或 7)上的所有电子邮件客户端配合使用。

有这样的动物吗?或者也许有人知道如何让附件与 mailto/ShellExecute 技术一起使用?

大多数人做什么?


编辑:

MAPI 解决方案甚至 Indy 解决方案已经有了一些答案。

我遇到的问题是他们不一定使用默认的邮件客户端。例如,在我的 Vista 计算机上,我已将 Windows Mail 设置为默认客户端。当我执行 MAPI 发送时,它不会启动 Windows Mail,而是在 Outlook 中启动并设置电子邮件。我不想要这样。

我的程序的两个用户抱怨道:

您的调试例程无法发送文件,因为它尝试出于自身已知的某种原因启动Windows邮件,而不是使用默认的邮件客户端(在我的例子中是thunderbird)

我试图填补异常 报告但在要求时放弃 这个服务器,那个服务器!然后我得到了 真的很生气,因为它推出了 前景 - 我从来没有使用过它,也不想使用它。

我不需要 MAPI 或 Indy 代码。它们很容易获得。但如果你建议 MAPI 或 Indy,我真正需要的是一种找到默认客户端并确保它是传递要发送的电子邮件的客户端的方法。

另外,我需要知道 MAPI 现在是否通用。 5 年前,并不能保证它可以在所有机器上运行,因为它没有作为操作系统的一部分安装。这仍然是真的,还是 MAPI 现在默认随 Windows XP、Vista 和 7 一起提供?

同样的问题也适用于印地或任何其他建议的解决方案。它可以与默认客户端一起使用吗?它可以在几乎所有 Windows XP 和更高版本的计算机上使用吗?

“mailto”解决方案之所以如此出色,是因为所有机器都必须支持它才能处理网页上的 HTML mailto 语句。现在,如果我可以用它来添加附件就好了……


找到了可能的解决方案:mjustin 指出了一种使用操作系统的 sendto 命令的替代方案。这很可能是要走的路。

mailto 并不像 HTML mailto 那样限制为 256 个字符,但我很震惊地发现它最终被限制为 2048 个字符。幸运的是,几个小时后,穆贾斯汀给出了答案。

如果实施顺利,他的回答对我来说就已经完成了。如果没有,我会在这里添加我的评论。


不会。事实证明,sendto 解决方案并不总是打开默认的电子邮件程序。在我的计算机上,当我的默认邮件程序是 Windows Mail 时,它会打开 Outlook。太糟糕了。尽管有 2048 个字符的限制,我还是不得不返回到 mailto 方法。

然而,我确实在文章中找到了: SendTo 邮件收件人

此时,您可以替换 ::ShellExecutive 深思熟虑 ::WinExec 调用,使用实际的 mailto 命令行中声明 注册并定位当前电子邮件 客户端(例如, “%ProgramFiles%\Outlook Express\msimn.exe" /mailurl:%1)。但是 那么限制是 32 KB。作为一个 结论是没办法发送 大于 32KB 的电子邮件使用 mailto协议。

但随后我必须确定每种情况下的邮件客户端是谁。我预计这会导致进一步的复杂化。

我发现的另一件事是 mailto 允许设置“收件人”、“抄送”、“密件抄送”、“主题”和“正文”,但不允许设置附件。而 sendto 仅允许附件,然后设置带有默认消息的默认电子邮件,并且您无法设置各种字段和正文。

Within my program, I am composing an email to send using the default e-mail client software installed on a user's machine.

I have composed the mailto address, the subject, the multilined body, and I have several attachments to be included.

I almost got this working using mailto and ShellExecute as follows:

  Message := 'mailto:[email protected]'
    + '?subject=This is the subjectBehold Error Report'
    + '&body=This is line 1' + '%0D%0A'
    + 'This is line 2' + '%0D%0A'
    + 'This is line 3'
    + '&Attach=c:\file1.txt';
  RetVal := ShellExecute(Handle, 'open', PChar(Message), nil, nil, SW_SHOWNORMAL);
  if RetVal <= 32 then
    MessageDlg('Cannot find program to send e-mail.', mtWarning, [mbOK], 0);

Using Delphi 2009 on a Windows Vista machine, this will open a Microsoft Mail "Create Mail" window, with the To, Subject and Body filled correctly. However the file does not get attached.

As I researched this, I noticed some commentary stating that this technique does not work with all mail clients. However, most of the commentary is fairly old, as I realize this is a very old technique.

Then I found that Zarko Gajic said that "this approach is ok, but you are unable to send attachments in this way".

I have seen theres also the Windows Simple Mail API (MAPI), but Zarko says that only works if the end-user has MAPI-compliant email software. There are well documented techniques on using MAPI with Delphi (e.g. Sending e-mail using mapi), but they all have the disclaimer that MAPI is not always installed with Windows.

Besides, I really want the message to come up first in the user's default email program, so they will have it as part of their email records and they can edit it and decide if and when they want to send it. I'm not sure how MAPI works and if it will do that.

So my requirements are:

  1. To bring the email up in the user's mail program.

  2. To allow one or more attachments.

  3. To work with (hopefully) all email clients on any Windows machine from XP up (i.e. XP, Vista or 7).

Is there such an animal? Or maybe does someone know how to get attachments to work with the mailto/ShellExecute technique?

What do most people do?


Edit:

There have been a few answers with MAPI solutions and even an Indy solution.

The problem I have with them is that they don't necessarily use the default mail client. On my Vista machine, for example, I have set up Windows Mail as my default client. When I do a MAPI send, it does not bring up Windows Mail, but it brings up and sets up the email in Outlook instead. I don't want that.

Two of my users of my program complained:

Your debug routine fails to send the file, as it tries to start windows mail for some reason known to it's self rather than using the default mail client (in my case thunderbird)

I tried to fill up the exception
report but gave up when it asked for
this server, that server! I then got
really annoyed because it launched
Outlook
- I never, ever use it or want to use it.

I don't need code for MAPI or Indy. They are readily available. But if you suggest MAPI or Indy, what I really need is a way to find the default client and ensure that it is the one that is passed the email to be sent.

Also, I need to know if MAPI is now universal. 5 years ago, it wasn't guaranteed to work on all machines because it wasn't installed as part of the operating system. Is that still true, or does MAPI now come with Windows XP, Vista and 7 by default?

Same questions go for Indy or any other suggested solutions. Can it work with the default client and will it work on almost all Windows XP and later machines?

The reason why the "mailto" solution is so nice, is that all machines have to support it for the purpose of handling the HTML mailto statement found on webpages. Now if only I could use it to add attachments ...


Likely solution found: mjustin pointed out an alternative that makes use of the Operating System's sendto command. That most likely is the way to go.

The mailto was not limited to 256 characters like the HTML mailto is, but I was devastated to find out it ended up being limited to 2048 characters. Fortunately a few hours later, mjustin gave his answer.

If implementation of that goes okay, his answer will have done it for me. If not, I'll add my comments here.


No. As it turns out, the sendto solution will not always open the default email program. On my machine, it opens Outlook when my default mailer is Windows Mail. Too bad. I've had to go back to the mailto method, despite the 2048 character limit.

I did, however, find in the article: SendTo mail recipient that:

At this point, you could replace
::ShellExecute with a well thought
::WinExec call, using the actual
mailto command line declared in the
registry and target the current e-mail
client (for instance,
"%ProgramFiles%\Outlook
Express\msimn.exe" /mailurl:%1). But
then the limitation is 32 KB. As a
conclusion, there is no way to send
e-mails larger than 32KB using the
mailto protocol.

but then I'd have to determine who the mail client is in each case. I expect that would lead to further complications.

The one other thing I found out is that mailto allows setting of "to", "cc", "bcc", "subject" and "body" but no attachments. Whereas sendto ONLY allows attachments and then sets up a default email with a default message and no way for you to set the various fields and body.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

空名 2024-08-22 19:42:28

不要复杂化,只需使用 JCL MAPI 代码即可。它位于 JclMapi.pas 单元中。我认为他们也有这样的例子。该代码非常强大,您可以执行 MAPI 允许的任何操作。

使用 ShellExecute,您无法发送附件,并且邮件正文的字符数也被限制为 255 个字符。

只要 MAPI 存在,旧 Windows 就始终会安装它(2000、XP)。它与 Outlook Express 一起提供,并且几乎总是安装 Outlook Express。对于较新的 Windows(Vista、7),没有 Outlook Express,因此也没有 MAPI。但如果您安装 MS Outlook 或 Mozzila Thunderbird,MAPI 会自动安装。所以你很安全。这是基本 MAPI,而不是扩展 MAPI。但它涵盖了你所需要的一切。

如果安装了 MAPI,您还可以检查您的代码 (JCL) 并采取相应措施。不久前我也做过类似的事情,效果还不错。我还没有找到不支持简单 MAPI 的流行 Windows 邮件客户端。这是 JCL 代码的简单包装,示例用法如下:

unit MAPI.SendMail;

interface

uses
  SysUtils, Classes, JclMapi;

type
  TPrerequisites = class
  public
    function IsMapiAvailable: Boolean;
    function IsClientAvailable: Boolean;
  end;

  TMAPISendMail = class
  private
    FAJclEmail: TJclEmail;
    FShowDialog: Boolean;
    FResolveNames: Boolean;
    FPrerequisites: TPrerequisites;
    // proxy property getters
    function GetMailBody: string;
    function GetHTMLBody: Boolean;
    function GetMailSubject: string;
    // proxy property setters
    procedure SetMailBody(const Value: string);
    procedure SetHTMLBody(const Value: Boolean);
    procedure SetMailSubject(const Value: string);
  protected
    function DoSendMail: Boolean; virtual;
  public
    constructor Create;
    destructor Destroy; override;
    // properties of the wrapper class
    property MailBody: string read GetMailBody write SetMailBody;
    property HTMLBody: Boolean read GetHTMLBody write SetHTMLBody;
    property ShowDialog: Boolean read FShowDialog write FShowDialog;
    property MailSubject: string read GetMailSubject write SetMailSubject;
    property ResolveNames: Boolean read FResolveNames write FResolveNames;
    property Prerequisites: TPrerequisites read FPrerequisites;
    // procedure and functions of the wrapper class
    procedure AddRecipient(const Address: string; const Name: string = '');
    procedure AddAttachment(const FileName: string);
    function SendMail: Boolean;
  end;

implementation

{ TMAPISendMail }

constructor TMAPISendMail.Create;
begin
  FPrerequisites := TPrerequisites.Create;
  FAJclEmail := TJclEmail.Create;
  FShowDialog := True;
end;

destructor TMAPISendMail.Destroy;
begin
  FreeAndNil(FAJclEmail);
  FreeAndNil(FPrerequisites);

  inherited;
end;

function TMAPISendMail.DoSendMail: Boolean;
begin
  Result := FAJclEmail.Send(FShowDialog);
end;

function TMAPISendMail.SendMail: Boolean;
begin
  Result := DoSendMail;
end;

function TMAPISendMail.GetMailBody: string;
begin
  Result := FAJclEmail.Body;
end;

procedure TMAPISendMail.SetMailBody(const Value: string);
begin
  FAJclEmail.Body := Value;
end;

procedure TMAPISendMail.AddAttachment(const FileName: string);
begin
  FAJclEmail.Attachments.Add(FileName);
end;

procedure TMAPISendMail.AddRecipient(const Address, Name: string);
var
  LocalName: string;
  LocalAddress: string;
begin
  LocalAddress := Address;
  LocalName := Name;

  if FResolveNames then
    if not FAJclEmail.ResolveName(LocalName, LocalAddress) then
      raise Exception.Create('Could not resolve Recipient name and address!');

  FAJclEmail.Recipients.Add(LocalAddress, LocalName);
end;

function TMAPISendMail.GetMailSubject: string;
begin
  Result := FAJclEmail.Subject;
end;

procedure TMAPISendMail.SetMailSubject(const Value: string);
begin
  FAJclEmail.Subject := Value;
end;

function TMAPISendMail.GetHTMLBody: Boolean;
begin
  Result := FAJclEmail.HtmlBody;
end;

procedure TMAPISendMail.SetHTMLBody(const Value: Boolean);
begin
  FAJclEmail.HtmlBody := Value;
end;

{ TPrerequisites }

function TPrerequisites.IsClientAvailable: Boolean;
var
  SimpleMAPI: TJclSimpleMapi;
begin
  SimpleMAPI := TJclSimpleMapi.Create;
  try
    Result := SimpleMAPI.AnyClientInstalled;
  finally
    SimpleMAPI.Free;
  end;
end;

function TPrerequisites.IsMapiAvailable: Boolean;
var
  SimpleMAPI: TJclSimpleMapi;
begin
  SimpleMAPI := TJclSimpleMapi.Create;
  try
    Result := SimpleMAPI.SimpleMapiInstalled;
  finally
    SimpleMAPI.Free;
  end;
end;

end.

示例用法:

unit f_Main;

interface

uses
  Windows, SysUtils, Classes, Controls, Forms, Graphics, StdCtrls, XPMan,

  // project units
  JclMapi, MAPI.SendMail, Dialogs;

type
  TfMain = class(TForm)
    XPManifest: TXPManifest;
    gbMailProperties: TGroupBox;
    eMailSubject: TEdit;
    stMailSubject: TStaticText;
    stMailBody: TStaticText;
    mmMailBody: TMemo;
    cbHTMLBody: TCheckBox;
    gbAttachments: TGroupBox;
    gbRecipients: TGroupBox;
    btnSendMail: TButton;
    lbRecipients: TListBox;
    eRecipAddress: TEdit;
    StaticText1: TStaticText;
    eRecipName: TEdit;
    btnAddRecipient: TButton;
    stRecipName: TStaticText;
    OpenDialog: TOpenDialog;
    lbAttachments: TListBox;
    btnAddAttachment: TButton;
    stMAPILabel: TStaticText;
    stClientLabel: TStaticText;
    stMAPIValue: TStaticText;
    stClientValue: TStaticText;
    procedure btnSendMailClick(Sender: TObject);
    procedure btnAddRecipientClick(Sender: TObject);
    procedure btnAddAttachmentClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.dfm}

procedure TfMain.btnSendMailClick(Sender: TObject);
var
  I: Integer;
  Name: string;
  Address: string;
  ItemStr: string;
  Pos1, Pos2: Integer;
  MAPISendMail: TMAPISendMail;
begin
  MAPISendMail := TMAPISendMail.Create;
  try
    for I := 0 to lbRecipients.Items.Count - 1 do
    begin
      ItemStr := lbRecipients.Items[I];
      Pos1 := Pos('[', ItemStr);
      Pos2 := Pos(']', ItemStr);

      Name := Trim(Copy(ItemStr, Pos1 + 1, Pos2 - Pos1 - 1));
      Address := Trim(Copy(ItemStr, 1, Pos1 - 1));
      MAPISendMail.AddRecipient(Address, Name);
    end;

    for I := 0 to lbAttachments.Items.Count - 1 do
      MAPISendMail.AddAttachment(lbAttachments.Items[I]);

    MAPISendMail.MailSubject := eMailSubject.Text;
    MAPISendMail.HTMLBody := cbHTMLBody.Checked;
    MAPISendMail.MailBody := mmMailBody.Text;
    MAPISendMail.SendMail;
  finally
    MAPISendMail.Free;
  end;
end;

procedure TfMain.btnAddRecipientClick(Sender: TObject);
begin
  lbRecipients.Items.Add(Format('%s [%s]', [eRecipAddress.Text,
                                            eRecipName.Text]));
end;

procedure TfMain.btnAddAttachmentClick(Sender: TObject);
begin
  if OpenDialog.Execute then
    lbAttachments.Items.Add(OpenDialog.FileName);
end;

procedure TfMain.FormCreate(Sender: TObject);
var
  ValidHost: Boolean;
  MAPISendMail: TMAPISendMail;
begin
  MAPISendMail := TMAPISendMail.Create;
  try
    ValidHost := True;

    if MAPISendMail.Prerequisites.IsMapiAvailable then
    begin
      stMAPIValue.Caption := 'Available';
      stMAPIValue.Font.Color := clGreen;
    end
    else
    begin
      stMAPIValue.Caption := 'Unavailable';
      stMAPIValue.Font.Color := clRed;
      ValidHost := False;
    end;

    if MAPISendMail.Prerequisites.IsClientAvailable then
    begin
      stClientValue.Caption := 'Available';
      stClientValue.Font.Color := clGreen;
    end
    else
    begin
      stClientValue.Caption := 'Unavailable';
      stClientValue.Font.Color := clRed;
      ValidHost := False;
    end;

    btnSendMail.Enabled := ValidHost;
  finally
    MAPISendMail.Free;
  end;
end;

end.

Do not complicate, just use the JCL MAPI code. It is in the unit JclMapi.pas. I think they also have the example for it. The code is very powerfull and you can do anything that MAPI allows you.

With ShellExecute you cannot send the attachment and you are also limited to 255 characters for the mail body.

As long as MAPI goes, with old windows it is always installed (2000, XP). It comes together with Outlook Express and Outlook Express is almost always installed. With newer windows (Vista, 7) there is no Outlook Express and so no MAPI. But MAPI is automatically installed if you install MS Outlook or Mozzila Thunderbird. So you are pretty safe. This is basic MAPI and not extended MAPI. But it covers all you need.

You can also check in your code (JCL) if MAPI is installed and act acordingly. I have done a similar thing not so long ago and it works ok. I have not found a popular windows mail client that does not support simple MAPI. This is a simple wrapper around JCL code and the sample usage bellow:

unit MAPI.SendMail;

interface

uses
  SysUtils, Classes, JclMapi;

type
  TPrerequisites = class
  public
    function IsMapiAvailable: Boolean;
    function IsClientAvailable: Boolean;
  end;

  TMAPISendMail = class
  private
    FAJclEmail: TJclEmail;
    FShowDialog: Boolean;
    FResolveNames: Boolean;
    FPrerequisites: TPrerequisites;
    // proxy property getters
    function GetMailBody: string;
    function GetHTMLBody: Boolean;
    function GetMailSubject: string;
    // proxy property setters
    procedure SetMailBody(const Value: string);
    procedure SetHTMLBody(const Value: Boolean);
    procedure SetMailSubject(const Value: string);
  protected
    function DoSendMail: Boolean; virtual;
  public
    constructor Create;
    destructor Destroy; override;
    // properties of the wrapper class
    property MailBody: string read GetMailBody write SetMailBody;
    property HTMLBody: Boolean read GetHTMLBody write SetHTMLBody;
    property ShowDialog: Boolean read FShowDialog write FShowDialog;
    property MailSubject: string read GetMailSubject write SetMailSubject;
    property ResolveNames: Boolean read FResolveNames write FResolveNames;
    property Prerequisites: TPrerequisites read FPrerequisites;
    // procedure and functions of the wrapper class
    procedure AddRecipient(const Address: string; const Name: string = '');
    procedure AddAttachment(const FileName: string);
    function SendMail: Boolean;
  end;

implementation

{ TMAPISendMail }

constructor TMAPISendMail.Create;
begin
  FPrerequisites := TPrerequisites.Create;
  FAJclEmail := TJclEmail.Create;
  FShowDialog := True;
end;

destructor TMAPISendMail.Destroy;
begin
  FreeAndNil(FAJclEmail);
  FreeAndNil(FPrerequisites);

  inherited;
end;

function TMAPISendMail.DoSendMail: Boolean;
begin
  Result := FAJclEmail.Send(FShowDialog);
end;

function TMAPISendMail.SendMail: Boolean;
begin
  Result := DoSendMail;
end;

function TMAPISendMail.GetMailBody: string;
begin
  Result := FAJclEmail.Body;
end;

procedure TMAPISendMail.SetMailBody(const Value: string);
begin
  FAJclEmail.Body := Value;
end;

procedure TMAPISendMail.AddAttachment(const FileName: string);
begin
  FAJclEmail.Attachments.Add(FileName);
end;

procedure TMAPISendMail.AddRecipient(const Address, Name: string);
var
  LocalName: string;
  LocalAddress: string;
begin
  LocalAddress := Address;
  LocalName := Name;

  if FResolveNames then
    if not FAJclEmail.ResolveName(LocalName, LocalAddress) then
      raise Exception.Create('Could not resolve Recipient name and address!');

  FAJclEmail.Recipients.Add(LocalAddress, LocalName);
end;

function TMAPISendMail.GetMailSubject: string;
begin
  Result := FAJclEmail.Subject;
end;

procedure TMAPISendMail.SetMailSubject(const Value: string);
begin
  FAJclEmail.Subject := Value;
end;

function TMAPISendMail.GetHTMLBody: Boolean;
begin
  Result := FAJclEmail.HtmlBody;
end;

procedure TMAPISendMail.SetHTMLBody(const Value: Boolean);
begin
  FAJclEmail.HtmlBody := Value;
end;

{ TPrerequisites }

function TPrerequisites.IsClientAvailable: Boolean;
var
  SimpleMAPI: TJclSimpleMapi;
begin
  SimpleMAPI := TJclSimpleMapi.Create;
  try
    Result := SimpleMAPI.AnyClientInstalled;
  finally
    SimpleMAPI.Free;
  end;
end;

function TPrerequisites.IsMapiAvailable: Boolean;
var
  SimpleMAPI: TJclSimpleMapi;
begin
  SimpleMAPI := TJclSimpleMapi.Create;
  try
    Result := SimpleMAPI.SimpleMapiInstalled;
  finally
    SimpleMAPI.Free;
  end;
end;

end.

Sample usage:

unit f_Main;

interface

uses
  Windows, SysUtils, Classes, Controls, Forms, Graphics, StdCtrls, XPMan,

  // project units
  JclMapi, MAPI.SendMail, Dialogs;

type
  TfMain = class(TForm)
    XPManifest: TXPManifest;
    gbMailProperties: TGroupBox;
    eMailSubject: TEdit;
    stMailSubject: TStaticText;
    stMailBody: TStaticText;
    mmMailBody: TMemo;
    cbHTMLBody: TCheckBox;
    gbAttachments: TGroupBox;
    gbRecipients: TGroupBox;
    btnSendMail: TButton;
    lbRecipients: TListBox;
    eRecipAddress: TEdit;
    StaticText1: TStaticText;
    eRecipName: TEdit;
    btnAddRecipient: TButton;
    stRecipName: TStaticText;
    OpenDialog: TOpenDialog;
    lbAttachments: TListBox;
    btnAddAttachment: TButton;
    stMAPILabel: TStaticText;
    stClientLabel: TStaticText;
    stMAPIValue: TStaticText;
    stClientValue: TStaticText;
    procedure btnSendMailClick(Sender: TObject);
    procedure btnAddRecipientClick(Sender: TObject);
    procedure btnAddAttachmentClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.dfm}

procedure TfMain.btnSendMailClick(Sender: TObject);
var
  I: Integer;
  Name: string;
  Address: string;
  ItemStr: string;
  Pos1, Pos2: Integer;
  MAPISendMail: TMAPISendMail;
begin
  MAPISendMail := TMAPISendMail.Create;
  try
    for I := 0 to lbRecipients.Items.Count - 1 do
    begin
      ItemStr := lbRecipients.Items[I];
      Pos1 := Pos('[', ItemStr);
      Pos2 := Pos(']', ItemStr);

      Name := Trim(Copy(ItemStr, Pos1 + 1, Pos2 - Pos1 - 1));
      Address := Trim(Copy(ItemStr, 1, Pos1 - 1));
      MAPISendMail.AddRecipient(Address, Name);
    end;

    for I := 0 to lbAttachments.Items.Count - 1 do
      MAPISendMail.AddAttachment(lbAttachments.Items[I]);

    MAPISendMail.MailSubject := eMailSubject.Text;
    MAPISendMail.HTMLBody := cbHTMLBody.Checked;
    MAPISendMail.MailBody := mmMailBody.Text;
    MAPISendMail.SendMail;
  finally
    MAPISendMail.Free;
  end;
end;

procedure TfMain.btnAddRecipientClick(Sender: TObject);
begin
  lbRecipients.Items.Add(Format('%s [%s]', [eRecipAddress.Text,
                                            eRecipName.Text]));
end;

procedure TfMain.btnAddAttachmentClick(Sender: TObject);
begin
  if OpenDialog.Execute then
    lbAttachments.Items.Add(OpenDialog.FileName);
end;

procedure TfMain.FormCreate(Sender: TObject);
var
  ValidHost: Boolean;
  MAPISendMail: TMAPISendMail;
begin
  MAPISendMail := TMAPISendMail.Create;
  try
    ValidHost := True;

    if MAPISendMail.Prerequisites.IsMapiAvailable then
    begin
      stMAPIValue.Caption := 'Available';
      stMAPIValue.Font.Color := clGreen;
    end
    else
    begin
      stMAPIValue.Caption := 'Unavailable';
      stMAPIValue.Font.Color := clRed;
      ValidHost := False;
    end;

    if MAPISendMail.Prerequisites.IsClientAvailable then
    begin
      stClientValue.Caption := 'Available';
      stClientValue.Font.Color := clGreen;
    end
    else
    begin
      stClientValue.Caption := 'Unavailable';
      stClientValue.Font.Color := clRed;
      ValidHost := False;
    end;

    btnSendMail.Enabled := ValidHost;
  finally
    MAPISendMail.Free;
  end;
end;

end.
善良天后 2024-08-22 19:42:28

我使用两种方法发送 MAPI 邮件,具体取决于是否需要附件。对于没有附件的简单情况,我使用以下方法:

function SendShellEmail( ARecipientEmail, ASubject, ABody : string ) : boolean;
// Send an email to this recipient with a subject and a body
var
  iResult : integer;
  S       : string;
begin

 If Trim(ARecipientEmail) = '' then
   ARecipientEmail := 'mail';
 S := 'mailto:' + ARecipientEmail;

 S := S + '?subject=' + ASubject;

 If Trim(ABody) <> '' then
  S := S + '&body=' + ABody;

 iResult := ShellExecute( Application.Handle,'open', PChar(S), nil, nil, SW_SHOWNORMAL);
 Result := iResult > 0;
end;

这使用简单的 shell 执行方法,因此除了最近的警报以使用户确认他们可以接受您的程序发送电子邮件之外,您不应该遇到任何实际问题。

对于附件,我使用以下代码,该代码最初取自 Brian Long 的 Delphi 杂志。
也可以不使用 MAPI 客户端而是使用指定的 SMTP 服务器发送电子邮件,但我认为您明确不希望这样做。如果你愿意的话我可以提供代码。

uses
  SysUtils,
  Windows,
  Dialogs,
  Forms,
  MAPI;

procedure ArtMAPISendMail(
            const Subject, MessageText, MailFromName, MailFromAddress,
                  MailToName, MailToAddress: String;
            const AttachmentFileNames: array of String);
//Originally by Brian Long: The Delphi Magazine issue 60 - Delphi And Email
var
  MAPIError: DWord;
  MapiMessage: TMapiMessage;
  Originator, Recipient: TMapiRecipDesc;
  Files, FilesTmp: PMapiFileDesc;
  FilesCount: Integer;
begin
   FillChar(MapiMessage, Sizeof(TMapiMessage), 0);

   MapiMessage.lpszSubject := PAnsiChar(AnsiString(Subject));
   MapiMessage.lpszNoteText := PAnsiChar(AnsiString(MessageText));

   FillChar(Originator, Sizeof(TMapiRecipDesc), 0);

   Originator.lpszName := PAnsiChar(AnsiString(MailFromName));
   Originator.lpszAddress := PAnsiChar(AnsiString(MailFromAddress));
//   MapiMessage.lpOriginator := @Originator;
   MapiMessage.lpOriginator := nil;


   MapiMessage.nRecipCount := 1;
   FillChar(Recipient, Sizeof(TMapiRecipDesc), 0);
   Recipient.ulRecipClass := MAPI_TO;
   Recipient.lpszName := PAnsiChar(AnsiString(MailToName));
   Recipient.lpszAddress := PAnsiChar(AnsiString(MailToAddress));
   MapiMessage.lpRecips := @Recipient;

   MapiMessage.nFileCount := High(AttachmentFileNames) - Low(AttachmentFileNames) + 1;
   Files := AllocMem(SizeOf(TMapiFileDesc) * MapiMessage.nFileCount);
   MapiMessage.lpFiles := Files;
   FilesTmp := Files;
   for FilesCount := Low(AttachmentFileNames) to High(AttachmentFileNames) do
   begin
     FilesTmp.nPosition := $FFFFFFFF;
     FilesTmp.lpszPathName := PAnsiChar(AnsiString(AttachmentFileNames[FilesCount]));
     Inc(FilesTmp)
   end;

   try
     MAPIError := MapiSendMail(
       0,
       Application.MainForm.Handle,
       MapiMessage,
       MAPI_LOGON_UI {or MAPI_NEW_SESSION},
       0);
   finally
     FreeMem(Files)
   end;

   case MAPIError of
     MAPI_E_AMBIGUOUS_RECIPIENT:
      Showmessage('A recipient matched more than one of the recipient descriptor structures and MAPI_DIALOG was not set. No message was sent.');
     MAPI_E_ATTACHMENT_NOT_FOUND:
      Showmessage('The specified attachment was not found; no message was sent.');
     MAPI_E_ATTACHMENT_OPEN_FAILURE:
      Showmessage('The specified attachment could not be opened; no message was sent.');
     MAPI_E_BAD_RECIPTYPE:
      Showmessage('The type of a recipient was not MAPI_TO, MAPI_CC, or MAPI_BCC. No message was sent.');
     MAPI_E_FAILURE:
      Showmessage('One or more unspecified errors occurred; no message was sent.');
     MAPI_E_INSUFFICIENT_MEMORY:
      Showmessage('There was insufficient memory to proceed. No message was sent.');
     MAPI_E_LOGIN_FAILURE:
      Showmessage('There was no default logon, and the user failed to log on successfully when the logon dialog box was displayed. No message was sent.');
     MAPI_E_TEXT_TOO_LARGE:
      Showmessage('The text in the message was too large to sent; the message was not sent.');
     MAPI_E_TOO_MANY_FILES:
      Showmessage('There were too many file attachments; no message was sent.');
     MAPI_E_TOO_MANY_RECIPIENTS:
      Showmessage('There were too many recipients; no message was sent.');
     MAPI_E_UNKNOWN_RECIPIENT:
       Showmessage('A recipient did not appear in the address list; no message was sent.');
     MAPI_E_USER_ABORT:
       Showmessage('The user canceled the process; no message was sent.');
     SUCCESS_SUCCESS:
       Showmessage('MAPISendMail successfully sent the message.');
   else
     Showmessage('MAPISendMail failed with an unknown error code.');
   end;
end;

I use two methods for sending a MAPI mail, depending on whether an attatchment is needed. For the simple case with no attachment I use the following:

function SendShellEmail( ARecipientEmail, ASubject, ABody : string ) : boolean;
// Send an email to this recipient with a subject and a body
var
  iResult : integer;
  S       : string;
begin

 If Trim(ARecipientEmail) = '' then
   ARecipientEmail := 'mail';
 S := 'mailto:' + ARecipientEmail;

 S := S + '?subject=' + ASubject;

 If Trim(ABody) <> '' then
  S := S + '&body=' + ABody;

 iResult := ShellExecute( Application.Handle,'open', PChar(S), nil, nil, SW_SHOWNORMAL);
 Result := iResult > 0;
end;

This uses a simple shell execute method, so you should not have any real problems other than the more recent alerts to get the User to confirm that they are ok with your program sending an email.

For attatchments I use the following code originally taken from the Delphi Magazine by Brian Long.
It is also possible to send an email WITHOUT using the MAPI client but using a nominated SMTP server but I think you explicitly do not want this. I can provide code for this if you do.

uses
  SysUtils,
  Windows,
  Dialogs,
  Forms,
  MAPI;

procedure ArtMAPISendMail(
            const Subject, MessageText, MailFromName, MailFromAddress,
                  MailToName, MailToAddress: String;
            const AttachmentFileNames: array of String);
//Originally by Brian Long: The Delphi Magazine issue 60 - Delphi And Email
var
  MAPIError: DWord;
  MapiMessage: TMapiMessage;
  Originator, Recipient: TMapiRecipDesc;
  Files, FilesTmp: PMapiFileDesc;
  FilesCount: Integer;
begin
   FillChar(MapiMessage, Sizeof(TMapiMessage), 0);

   MapiMessage.lpszSubject := PAnsiChar(AnsiString(Subject));
   MapiMessage.lpszNoteText := PAnsiChar(AnsiString(MessageText));

   FillChar(Originator, Sizeof(TMapiRecipDesc), 0);

   Originator.lpszName := PAnsiChar(AnsiString(MailFromName));
   Originator.lpszAddress := PAnsiChar(AnsiString(MailFromAddress));
//   MapiMessage.lpOriginator := @Originator;
   MapiMessage.lpOriginator := nil;


   MapiMessage.nRecipCount := 1;
   FillChar(Recipient, Sizeof(TMapiRecipDesc), 0);
   Recipient.ulRecipClass := MAPI_TO;
   Recipient.lpszName := PAnsiChar(AnsiString(MailToName));
   Recipient.lpszAddress := PAnsiChar(AnsiString(MailToAddress));
   MapiMessage.lpRecips := @Recipient;

   MapiMessage.nFileCount := High(AttachmentFileNames) - Low(AttachmentFileNames) + 1;
   Files := AllocMem(SizeOf(TMapiFileDesc) * MapiMessage.nFileCount);
   MapiMessage.lpFiles := Files;
   FilesTmp := Files;
   for FilesCount := Low(AttachmentFileNames) to High(AttachmentFileNames) do
   begin
     FilesTmp.nPosition := $FFFFFFFF;
     FilesTmp.lpszPathName := PAnsiChar(AnsiString(AttachmentFileNames[FilesCount]));
     Inc(FilesTmp)
   end;

   try
     MAPIError := MapiSendMail(
       0,
       Application.MainForm.Handle,
       MapiMessage,
       MAPI_LOGON_UI {or MAPI_NEW_SESSION},
       0);
   finally
     FreeMem(Files)
   end;

   case MAPIError of
     MAPI_E_AMBIGUOUS_RECIPIENT:
      Showmessage('A recipient matched more than one of the recipient descriptor structures and MAPI_DIALOG was not set. No message was sent.');
     MAPI_E_ATTACHMENT_NOT_FOUND:
      Showmessage('The specified attachment was not found; no message was sent.');
     MAPI_E_ATTACHMENT_OPEN_FAILURE:
      Showmessage('The specified attachment could not be opened; no message was sent.');
     MAPI_E_BAD_RECIPTYPE:
      Showmessage('The type of a recipient was not MAPI_TO, MAPI_CC, or MAPI_BCC. No message was sent.');
     MAPI_E_FAILURE:
      Showmessage('One or more unspecified errors occurred; no message was sent.');
     MAPI_E_INSUFFICIENT_MEMORY:
      Showmessage('There was insufficient memory to proceed. No message was sent.');
     MAPI_E_LOGIN_FAILURE:
      Showmessage('There was no default logon, and the user failed to log on successfully when the logon dialog box was displayed. No message was sent.');
     MAPI_E_TEXT_TOO_LARGE:
      Showmessage('The text in the message was too large to sent; the message was not sent.');
     MAPI_E_TOO_MANY_FILES:
      Showmessage('There were too many file attachments; no message was sent.');
     MAPI_E_TOO_MANY_RECIPIENTS:
      Showmessage('There were too many recipients; no message was sent.');
     MAPI_E_UNKNOWN_RECIPIENT:
       Showmessage('A recipient did not appear in the address list; no message was sent.');
     MAPI_E_USER_ABORT:
       Showmessage('The user canceled the process; no message was sent.');
     SUCCESS_SUCCESS:
       Showmessage('MAPISendMail successfully sent the message.');
   else
     Showmessage('MAPISendMail failed with an unknown error code.');
   end;
end;
征棹 2024-08-22 19:42:28

ShellExecute 中的 mailto 似乎无法发送附件。

MAPI 和 Indy 都有一个不幸的特性,即不一定选择用户的电子邮件客户端。

因此,另一种可能性是继续使用 ShellExecute,但找到另一种方法将附件获取到电子邮件客户端。

我决定做的是在创建电子邮件的对话框上,我现在有一个 FileListBox 列出用户可能想要附加到电子邮件的文件。当电子邮件弹出时,他们只需将它们拖放到电子邮件中即可。

就我而言,这实际上是一个很好的解决方案,因为这允许用户选择他们想要包含的文件。另一种方法(自动附加它们)将要求他们删除不想包含的内容。 (即,已经选中“添加 Google 工具栏”选项并不好)

目前此解决方案将起作用。

感谢所有提供答案并帮助我解决这个问题的人(全部+1)。

It appears that mailto in a ShellExecute is not capable of sending attachments.

MAPI and Indy have the unfortunate characteristic of not necessarily selecting the user's email client.

So the other possibility is to continue using ShellExecute, but find another way to get the attachments into the email client.

What I decided to do was on my Dialog that creates the email, I now have a FileListBox listing the files the user may want to attach to the email. When the email pops up, they can simply drag and drop them to the email.

In my case, this is actually a good solution, since this allows the users to select the files they want to include. The other method (automatically attaching them) will require they delete the ones they don't want included. (i.e. Having the "Add Google toolbar" option already checked for you is NOT good)

For the time being this solution will work.

Thank you to all those who contributed answers and helped me see my way through this (all +1).

辞取 2024-08-22 19:42:28

本文 展示了 Delphi 如何模拟“发送到...”shell 上下文菜单命令并以编程方式打开带有附件的默认邮件客户端。

此解决方案不需要 MAPI,并且可以与默认邮件客户端一起使用,但并不完整,因为不会自动填写邮件收件人、正文和主题。 (可以使用剪贴板复制消息正文)。

This article shows how Delphi can simulate the "Send to..." shell context menu command and open the default mail client with attachments programatically.

This solution does not need MAPI and works with the default mail client, but is not complete, because the message recipients, body and subject will not be filled in automatically. (The message body could be copied using the clipboard).

不气馁 2024-08-22 19:42:28

以下是有关所有这些电子邮件设置及其用途的摘要:
http://thesunrinkle.blogspot。 de/2017/03/how-to-configure-eurekalog-to-send-bugs.html

在此处输入图像描述

所以,远离 Shell (mailto)。
Mapi 也是一个坏主意,因为它仅适用于 MS 电子邮件客户端。
我默认设置为Simple MAPI,但我很少收到此渠道发送的电子邮件。大多数电子邮件是通过 SMTP 服务器接收的。

大警告!!!!!!!!!
我发现当您激活 EurekaLog 时,防病毒扫描仪发出的误报警报数量要高得多。因此,仅在绝对必要时才使用 EurekaLog。
此外,Eureka 本身也充满了错误(只需查看发布历史记录,就会发现对于他们发布的每个新功能(甚至更改),他们随后修复了一些错误!因此,如果您正在跟踪错误,请注意 EurekaLog 本身可能会在你的EXE中引入几个!

Here is a summary about all those email settings and what they do:
http://thesunstroke.blogspot.de/2017/03/how-to-configure-eurekalog-to-send-bugs.html

enter image description here

So, stay away from Shell (mailto).
Mapi is also a bad idea since it only works with MS email clients.
I set by default Simple MAPI but I rarely receive emails sent by this channel. Most emails are received via SMTP Server.

BIG WARNING!!!!!!!!!
I have seen that the number of false positive alarms from antivirus scanners is much higher when you activate EurekaLog. So, only use EurekaLog when absolutely necessary.
Also, Eureka itself is peppered with bugs (just look at the release history and see that for each new feature (or even change) they release, they later fix a few bugs! So, if you are tracking for bugs, notice that EurekaLog itself may introduce few in your EXE!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文