How can a Delphi Program send an Email with Attachments via the DEFAULT E-mail Client?

前端 未结 5 869
醉梦人生
醉梦人生 2020-12-24 15:31

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

相关标签:
5条回答
  • 2020-12-24 15:44

    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).

    0 讨论(0)
  • 2020-12-24 15:46

    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).

    0 讨论(0)
  • 2020-12-24 15:48

    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.
    
    0 讨论(0)
  • 2020-12-24 15:51

    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

    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!

    0 讨论(0)
  • 2020-12-24 16:00

    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;
    
    0 讨论(0)
提交回复
热议问题