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

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

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

Я почти получил эту работу, используя 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);

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

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

Затем я обнаружил, что Zarko Gajic сказал, что "этот подход в порядке, но вы не можете отправлять вложения таким образом".

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

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

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

  • Чтобы отправить электронное письмо в почтовую программу пользователя.

  • Чтобы разрешить одно или несколько вложений.

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

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

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


Изменить:

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

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

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

Ваша процедура отладки не может отправить файл, поскольку он пытается запустить почтовую почту по какой-либо причине, известную себе, а не использовать почтовый клиент по умолчанию (в моем случае 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 символами. К счастью, несколько часов спустя, mjustin дал свой ответ.

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


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

Однако я нашел в статье: SendTo mail recipient, который:

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

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

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

Ответы

Ответ 1

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

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

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

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

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

Пока это решение будет работать.

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

Ответ 2

Я использую два метода для отправки почты 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;

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

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

Ответ 3

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

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

Пока MAPI идет, старые окна всегда устанавливаются (2000, XP). Он поставляется вместе с Outlook Express, и Outlook Express почти всегда установлен. С более новыми окнами (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.

Ответ 4

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

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

Ответ 5

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

введите описание изображения здесь

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

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