Как Программа 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, это откроет окно "Create Mail" Microsoft Mail, с К, Предмет и Тело, заполненное правильно. Однако файл не становится приложенным.

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

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

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

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

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

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

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

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

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

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


Править:

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

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

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

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

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

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

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

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

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


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

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

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


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

Я действительно, однако, находил в статье: адресат SendTo, что:

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

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

Еще одна вещь, которую я узнал, состоит в том, что mailto позволяет устанавливать "на", "cc", "скрытая копия", "предмет" и "тело", но никакие вложения. Принимая во внимание, что sendto ТОЛЬКО позволяет вложения и затем настраивает электронное письмо по умолчанию с сообщением по умолчанию и никаким способом для Вас установить различные поля и тело.

14
задан lkessler 28 December 2009 в 17:10
поделиться

4 ответа

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

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

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

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

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

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

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

4
ответ дан 1 December 2019 в 14:11
поделиться

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

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

В старых окнах MAPI устанавливается всегда (2000, XP). Он поставляется вместе с Outlook Express, и Outlook Express устанавливается почти всегда. В более новых версиях 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.
5
ответ дан 1 December 2019 в 14:11
поделиться

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

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

.
2
ответ дан 1 December 2019 в 14:11
поделиться

Я использую два метода для отправки почты 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, поэтому у вас не должно быть никаких реальных проблем, отличных от более поздних предупреждений, чтобы получить пользователь, чтобы подтвердить, что они в порядке с вашей программой Отправка электронной почты.

Для приспособлений я использую следующий код, изначально взятый из журнала Delphi Brian Long. Также возможно отправить электронное письмо без использования клиента 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;
4
ответ дан 1 December 2019 в 14:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: