Come può un Programma Delphi inviare una e-Mail con Allegati tramite il Client di posta PREDEFINITO?

StackOverflow https://stackoverflow.com/questions/1962765

Domanda

All'interno del mio programma, io sono la composizione di una e-mail per l'invio tramite il client di posta elettronica predefinito software installato sulla macchina dell'utente.

Ho composto il mailto indirizzo, l'oggetto, il multilined corpo, e ho diversi allegati inclusi.

Ho quasi avuto questo lavoro utilizzando mailto e ShellExecute come segue:

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

Utilizzando Delphi 2009 su una macchina Windows Vista, si aprirà una centrale di Microsoft Mail "Crea messaggio" finestra, con A, Oggetto e Corpo riempito correttamente.Tuttavia, il file non essere attaccati.

Come ho ricercato questo, ho notato qualche commento affermando che questa tecnica non funziona con tutti i client di posta.Tuttavia, la maggior parte dei commenti è piuttosto vecchio, come mi rendo conto che questa è una tecnica molto antica.

Poi ho scoperto che Zarko Gajic detto "questo approccio è ok, ma non è possibile inviare allegati in questo modo".

Ho visto c'è anche il Windows Simple Mail API (MAPI), ma Zarko dice che funziona solo se l'utente finale è compatibile MAPI software di posta elettronica.Ci sono ben documentate le tecniche che utilizza MAPI con Delphi (ad es. L'invio di e-mail utilizzando mapi), ma tutti hanno il disclaimer che MAPI non è sempre installato con Windows.

Inoltre, ho davvero bisogno di trasmettere il messaggio prima che l'utente del programma di posta elettronica predefinito, in modo da poter avere come parte del loro email record e si può modificare e decidere se e quando si desidera inviare.Non sono sicuro di come MAPI funziona e se lo farà.

Così i miei requisiti sono:

  1. Per portare l'e-mail dell'utente, il programma di posta.

  2. Per consentire a uno o più allegati.

  3. Per lavorare con (si spera) tutti i client di posta elettronica su qualsiasi macchina Windows da XP in su (cioèXP, Vista o 7).

C'è un animale?O forse qualcuno sa come ottenere allegati a lavorare con il mailto/ShellExecute tecnica?

Che cosa la maggior parte della gente?


Edit:

Ci sono stati un paio di risposte con MAPI soluzioni e anche un Indy soluzione.

Il problema che ho con loro è che essi non necessariamente utilizzare il client di posta elettronica predefinito.Sulla mia macchina windows Vista, per esempio, ho impostato Windows Mail come il mio client predefinito.Quando faccio una MAPI inviare, non portare Windows Mail, ma porta in primo piano e imposta l'e-mail in Outlook.Non voglio che.

Due dei miei utenti del mio programma si lamentava:

Il debug di routine non riesce a inviare il file, come si tenta di avviare windows mail per qualche motivo noto che è di per sé, piuttosto che utilizzando il client di posta elettronica predefinito (nel mio caso thunderbird)

Ho provato a riempire l'eccezione report, ma ha dato quando ha chiesto per la questo server, il server!Poi ho ricevuto veramente fastidio perché ha lanciato Outlook - Non ho mai, mai usare o desidera utilizzarlo.

Non ho bisogno del codice per MAPI o Indy.Essi sono facilmente disponibili.Ma se si suggeriscono MAPI o Indy, cosa di cui avrei bisogno è un modo per trovare il client predefinito e assicurarsi che sia quello che è passato il messaggio è stato inviato.

Inoltre, ho bisogno di sapere se MAPI è ormai universale.5 anni fa, non era garantito su tutte le macchine perché non è stato installato come parte del sistema operativo.È ancora vero, o MAPI ora con Windows XP, Vista e 7 per impostazione predefinita?

Stesse domande per Indy o eventuali altre soluzioni suggerite.Può funzionare con il client predefinito e funziona su quasi tutti i Windows XP e versioni successive macchine?

Il motivo per cui il "mailto" la soluzione è così bello, è che tutte le macchine hanno il supporto per le finalità del trattamento HTML mailto dichiarazione trovato su pagine web.Ora, se solo potessi usarlo per aggiungere allegati.


Probabile soluzione:mjustin sottolineato un'alternativa che fa uso del Sistema Operativo sendto comando.Che probabilmente è la strada da percorrere.

Mailto non si è limitato a 256 caratteri come HTML mailto, ma mi è stato devastato per scoprire che ha finito per essere limitato a 2048 caratteri.Fortunatamente un paio di ore più tardi, mjustin ha dato la sua risposta.

Se l'attuazione di che va bene, la sua risposta ha fatto per me.Se non, vorrei aggiungere i miei commenti qui.


No.Come si scopre, sendto soluzione non sempre aprire il programma di posta elettronica predefinito.Sulla mia macchina, si apre Outlook quando il mio mailer predefinito è Windows Mail.Troppo male.Ho avuto la mailto metodo, nonostante il 2048 caratteri.

Ho fatto, però, trovare nell'articolo: Invia a destinatario di posta elettronica che:

A questo punto, si potrebbe sostituire ::ShellExecute con un ben pensato ::WinExec chiamata, utilizzando il mailto riga di comando dichiarati nel di registro e di destinazione corrente di posta elettronica client (per esempio, "%ProgramFiles%\Outlook Express\msimn.exe" /mailurl:%1).Ma quindi la limitazione è di 32 KB.Come conclusione, non c'è modo di inviare e-mail di dimensioni superiori a 32 KB utilizzando il protocollo mailto.

ma poi avrei dovuto determinare chi è il client di posta è in ogni caso.Mi aspetto che vorresti portare a ulteriori complicazioni.

Un altra cosa che ho scoperto è che mailto permette l'impostazione di "a", "cc", "ccn", "soggetto" e "corpo", ma non gli allegati.Considerando che sendto consente SOLO gli allegati e quindi imposta il valore predefinito e-mail con un messaggio di default e nessun modo per voi di impostare i vari campi e per il corpo.

È stato utile?

Soluzione 3

Sembra che mailto in un ShellExecute non è in grado di inviare allegati.

MAPI e Indy hanno la spiacevole caratteristica di non necessariamente la selezione dell'utente client di posta elettronica.

Così l'altra possibilità è quella di continuare a utilizzare ShellExecute, ma trovare un altro modo per ottenere gli allegati nei client di posta elettronica.

Quello che ho deciso di fare la mia finestra di Dialogo che crea l'email, ora ho un FileListBox che elenca i file che l'utente potrebbe voler allegare alla mail.Quando l'e-mail si apre, si può semplicemente trascinare e rilasciare loro l'e-mail.

Nel mio caso, questo è in realtà una buona soluzione, in quanto questo permette agli utenti di selezionare i file che si desidera includere.L'altro metodo (collegamento automatico di essi) si richiedono loro di eliminare quelli che non si desidera includere.(cioèAvendo il "Aggiungi Google toolbar" opzione già selezionata per voi NON è un bene)

Per il momento questa soluzione funziona.

Grazie a tutti coloro che hanno contribuito risposte e mi ha aiutato a vedere la mia strada attraverso questo (tutti +1).

Altri suggerimenti

Non è complicato, basta usare il JCL Codice MAPI.È nell'unità JclMapi.pas.Penso che anche l'esempio per esso.Il codice è molto potente e si può fare nulla, che MAPI che permette di.

Con ShellExecute non è possibile inviare l'allegato e si è limitato a 255 caratteri per il corpo della mail.

Come lungo come MAPI va, con il vecchio windows è sempre installato (2000, XP).Viene fornito insieme con Outlook Express e Outlook Express è quasi sempre installato.Con le versioni di windows (Vista, 7) non c'è Outlook Express e quindi non MAPI.Ma MAPI viene installato automaticamente se si installa MS Outlook o Mozzila Thunderbird.Quindi sono abbastanza sicuro.Questa è la base MAPI e non MAPI estesa.Ma copre tutto il necessario.

È inoltre possibile controllare il vostro codice (JCL) se MAPI è installato e agire in linea.Ho fatto una cosa simile non molto tempo fa e funziona ok.Non ho trovato un popolare client di posta di windows che non supporta simple MAPI.Questo è un semplice wrapper JCL codice e l'utilizzo di esempio qui sotto:

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.

Un esempio di utilizzo:

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.

Io uso due metodi per l'invio di posta elettronica MAPI, a seconda se un attatchment è necessario.Per il caso semplice con allegato io uso il seguente:

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;

Questo utilizza una semplice shell metodo execute, quindi non dovreste avere alcun problema reale che le più recenti avvisi di ottenere la conferma dell'Utente che sono ok con il vostro programma l'invio di una e-mail.

Per attatchments io uso il seguente codice originariamente presi Delphi Rivista da Brian Lungo.È anche possibile inviare una e-mail SENZA utilizzare il client MAPI, ma utilizzando una nomination server SMTP, ma penso che esplicitamente non si vuole questo.Posso fornire il codice per questo se si fa.

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;

Questo articolo mostra come Delphi in grado di simulare il "Invia a..." shell di comando del menu contestuale e aprire il client di posta elettronica predefinito con gli allegati a livello di programmazione.

Questa soluzione non ha bisogno di MAPI e funziona con il client di posta elettronica predefinito, ma non è completa, perché i destinatari del messaggio, il corpo e l'oggetto non verranno compilati automaticamente.(Il corpo del messaggio può essere copiati utilizzando gli appunti).

Ecco un riepilogo di tutte quelle impostazioni e-mail e cosa fanno:
http://thesunstroke.blogspot.de/2017/03/how-to-configure-eurekalog-to-send-bugs.html

enter image description here

Quindi, stare lontano da Shell (mailto).
Mapi è anche una cattiva idea, dato che funziona solo con MS client di posta elettronica.
Mi Simple MAPI predefinito, ma mi capita raramente di ricevere messaggi di posta elettronica inviati da questo canale.La maggior parte delle email ricevute tramite un Server SMTP.

GRANDE ATTENZIONE!!!!!!!!!
Ho visto che il numero di falsi positivi allarmi da scanner antivirus è molto più alto quando si attiva EurekaLog.Quindi, utilizzare solo EurekaLog quando è assolutamente necessario.
Inoltre, Eureka stesso è ricco di bug (basta guardare il rilascio di storia e vedere che per ogni nuova funzione (o anche cambiare) hanno comunicato, hanno poi correggere alcuni bug!Quindi, se si verifica per i bug noti che EurekaLog stesso può introdurre qualche tuo EXE!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top