Как программа Delphi может отправить электронное письмо с вложениями через почтовый клиент по умолчанию?

в моей программе я составляю электронное письмо для отправки с помощью программного обеспечения почтового клиента по умолчанию, установленного на компьютере пользователя.

Я составил адрес mailto, тему, многолинейное тело, и у меня есть несколько вложений, которые должны быть включены.

я почти получил эту работу, используя mailto и ShellExecute следующим образом:

  Message := 'mailto:someone@somewhere.com'
    + '?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);

используя Delphi 2009 на компьютере с Windows Vista, откроется окно Microsoft Mail "создать почту" с помощью, Предмет и тело заполнены правильно. Однако файл не прикрепляется.

когда я исследовал это, я заметил комментарий, в котором говорится, что этот метод не работает со всеми почтовыми клиентами. Однако большая часть комментариев довольно стара, поскольку я понимаю, что это очень старая техника.

тогда я нашел это что "этот подход в порядке, но вы не можете отправлять вложения таким образом".

Я видел theres также окна Простой почтовый API (MAPI), но Зарко говорит, что работает только в том случае, если конечный пользователь имеет MAPI-совместимое программное обеспечение электронной почты. Существуют хорошо документированные методы использования MAPI с Delphi (например,отправка электронной почты с помощью MAPI), но все они имеют отказ от ответственности, что MAPI не всегда устанавливается с Windows.

кроме того, я действительно хочу, чтобы сообщение появилось первым в почтовой программе пользователя по умолчанию, поэтому они будут иметь его как часть своих записей электронной почты, и они могут редактировать его и решать, если и когда они захотят его отправить. Я не уверен, как работает MAPI и будет ли это делать.

Итак, мои требования:

  1. для вызова электронной почты в почтовой программе пользователя.

  2. разрешить одно или несколько вложений.

  3. для работы (надеюсь) со всеми почтовыми клиентами на любой машине Windows с XP (т. е. XP, Vista или 7).

есть ли такое животное? Или, может быть, кто-нибудь знает как получить вложения для работы с техникой mailto/ShellExecute?

что делает большинство людей?


изменить:

было несколько ответов с решениями MAPI и даже решением Indy.

проблема с ними заключается в том, что они не обязательно используют почтовый клиент по умолчанию. Например, на моей машине Vista я настроил Windows Mail в качестве клиента по умолчанию. Когда я отправляю MAPI, он не вызывает почту Windows, но он вместо этого выводит и настраивает электронную почту в Outlook. Я не хочу этого.

два моих пользователя моей программы жаловались:

ваша процедура отладки не может отправить файл, так как он пытается запустить Windows mail по какой-то причине, известной ему самому, а не с помощью почтового клиента по умолчанию (в моем случае thunderbird)

Я попытался заполнить исключение доклад, но сдался, когда его попросили этот сервер, этот сервер! Потом я очень раздражен, потому что он начал Прогноз - Я никогда не использую его и не хочу использовать.

мне не нужен код для MAPI или Indy. Они легко доступны. Но если вы предлагаете MAPI или Indy, то мне действительно нужен способ найти клиент по умолчанию и убедиться, что это тот, который передается по электронной почте.

кроме того, мне нужно знать, является ли MAPI теперь универсальным. 5 лет назад, это не гарантированно работать на всех машинах, потому что он не был установлен как часть операционной системы. Есть это все еще верно, или MAPI теперь поставляется с Windows XP, Vista и 7 по умолчанию?

те же вопросы идут для Indy или любых других предлагаемых решений. Может ли он работать с клиентом по умолчанию и будет ли он работать почти на всех Windows XP и более поздних компьютерах?

причина, по которой решение "mailto" настолько приятно, заключается в том, что все машины должны поддерживать его с целью обработки оператора HTML mailto, найденного на веб-страницах. Теперь, если бы я мог использовать его для добавления вложений ...


найдено вероятное решение: mjustin указал альтернативу, которая использует команду sendto операционной системы. Это, скорее всего, путь.

mailto не был ограничен 256 символами, такими как HTML mailto, но я был опустошен, чтобы узнать, что он в конечном итоге ограничен 2048 символами. К счастью, через несколько часов мьюстин ответил:

Если реализация этого пойдет хорошо, его ответ сделает это за меня. Если нет, я добавлю свои комментарии здесь.


нет. Как оказалось, решение sendto не всегда будет открывать программу электронной почты по умолчанию. На моей машине он открывает Outlook, когда моим почтовым средством по умолчанию является Почта Windows. Очень плохо. Мне пришлось вернуться к методу mailto, несмотря на ограничение 2048 символов.

Я, однако, нашел в статье:Отправить сообщение что:

в этот момент Вы можете заменить :: ShellExecute с колодцем мысль :: Вызов WinExec, используя фактический командная строка mailto объявлена в реестр и адрес текущей электронной почты клиент (например, "%ProgramFiles%Outlook Экспресскоманду msimn.exe " / mailurl:%1). Но тогда ограничение в 32 КБ. Как заключение, нет способа отправить электронная почта больше чем 32KB используя протокол mailto.

но тогда я должен был бы определить, кто почтовый клиент в каждом случае. Я ожидаю, что это приведет к дальнейшему осложнения.

еще одна вещь, которую я узнал, - это то, что mailto позволяет устанавливать "to", "cc", "bcc", "subject" и "body", но без вложений. В то время как sendto разрешает только вложения, а затем настраивает электронную почту по умолчанию с сообщением по умолчанию и не может установить различные поля и тело.

5 ответов


Я использую два метода для отправки почты MAPI, в зависимости от того, требуется ли attatchment. Для простого случая без вложения я использую следующее:

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;

Это использует простой метод выполнения оболочки, поэтому у вас не должно быть никаких реальных проблем, кроме более поздних предупреждений, чтобы заставить пользователя подтвердить, что они в порядке с вашей программой, отправляющей электронное письмо.

для attatchments я использую следующий код, первоначально взятый из журнала 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;

Не усложняйте, просто использовать JCL код MAPI. Это в блоке JclMapi.первенство. Думаю, у них тоже есть для этого пример. Код очень мощный, и вы можете делать все, что позволяет MAPI.

С ShellExecute вы не можете отправить вложение, и вы также ограничены 255 символами для тела почты.

пока MAPI идет, со старыми окнами он всегда установлен (2000, XP). Он поставляется вместе с Outlook Express и Outlook Экспресс почти всегда установлен. С более новой windows (Vista, 7) нет Outlook Express и поэтому нет MAPI. Но MAPI автоматически устанавливается при установке MS Outlook или Mozzila Thunderbird. Так что ты в безопасности. Это базовый MAPI, а не расширенный MAPI. Но это покрывает все, что вам нужно.

вы также можете проверить свой код (JCL), если MAPI установлен и действует согласованно. Я не так давно сделал аналогичную вещь, и она работает нормально. Я не нашел популярного почтового клиента windows, который не поддерживает простой MAPI. Это простая обертка вокруг кода 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.

похоже, что mailto в ShellExecute не способен отправлять вложения.

MAPI и Indy имеют неудачную характеристику не обязательно выбирать почтовый клиент пользователя.

таким образом, другая возможность-продолжить использование ShellExecute, но найти другой способ получить вложения в почтовый клиент.

то, что я решил сделать, было в моем диалоговом окне, которое создает электронную почту, теперь у меня есть FileListBox список файлов, которые пользователь может захотеть прикрепить к электронной почте. Когда сообщение всплывает, они могут просто перетащить их на электронную почту.

в моем случае, это действительно хорошее решение, поскольку это позволяет пользователям выбирать файлы, которые хотите включить. Другой метод (автоматически прикрепляя их) потребует, чтобы они удалили те, которые они не хотят включать. (т. е. наличие опции "добавить панель инструментов Google" уже проверено для вас не хорошо)

на данный момент это решение работа.

спасибо всем тем, кто дал ответы и помог мне увидеть свой путь через это (все +1).


в этой статье показывает, как Delphi может имитировать " отправить..."команда контекстного меню оболочки и откройте почтовый клиент по умолчанию с вложениями программно.

Это решение не нуждается в MAPI и работает с почтовым клиентом по умолчанию, но не является полным, потому что получатели сообщений, тело и тема не будут заполнены автоматически. (Тело сообщения можно скопировать с помощью буфера обмена).


вот краткое описание всех этих настроек электронной почты и что они do:
http://thesunstroke.blogspot.de/2017/03/how-to-configure-eurekalog-to-send-bugs.html

enter image description here

Итак, держитесь подальше от оболочки (mailto).
MAPI-это также плохая идея, так как он работает только с MS клиентам по электронной почте.
Я установил по умолчанию простой MAPI, но я редко получаю письма, отправленные этим каналом. Большинство писем поступают через SMTP-сервер.

БОЛЬШОЕ ПРЕДУПРЕЖДЕНИЕ!!!!!!!!!
Я видел, что количество ложноположительных сигналов тревоги от антивирусных сканеров намного выше, когда вы активируете EurekaLog. Итак, используйте EurekaLog только тогда, когда это абсолютно необходимо.
Кроме того, сама Эврика усеяна ошибками (просто посмотрите на историю выпуска и посмотрите, что для каждой новой функции (или даже изменения) они выпускают, они позже исправляют несколько ошибок! Итак, если вы отслеживаете ошибки, обратите внимание, что EurekaLog сам может ввести несколько в вашем EXE!