Pergunta

No meu programa, estou compondo um email para enviar usando o software cliente de e-mail padrão instalado na máquina de um usuário.

Compusei o endereço do correio, o assunto, o corpo multilinado, e tenho vários anexos a serem incluídos.

Eu quase consegui isso funcionando usando o Mailto e o Shellexecute da seguinte maneira:

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

Usando o Delphi 2009 em uma máquina Windows Vista, isso abrirá uma janela "Criar Mail Mail" da Microsoft, com o sujeito e o corpo preenchidos corretamente. No entanto, o arquivo não é anexado.

Enquanto pesquisei isso, notei alguns comentários afirmando que essa técnica não funciona com todos os clientes de email. No entanto, a maior parte do comentário é bastante antiga, pois percebo que essa é uma técnica muito antiga.

Então eu encontrei isso Zarko Gajic disse Essa "essa abordagem está bem, mas você não pode enviar anexos dessa maneira".

Vi também a API do Windows Simple Mail (MAPI), mas Zarko diz que só funciona se o usuário final tiver software de email compatível com MAPI. Existem técnicas bem documentadas no uso de mapi com delphi (por exemplo Enviando e-mail usando MAPI), mas todos eles têm o aviso de que o MAPI nem sempre está instalado com o Windows.

Além disso, eu realmente quero que a mensagem surja primeiro no programa de email padrão do usuário, para que eles a tenham como parte de seus registros de e -mail e possam editá -lo e decidir se e quando desejam enviá -lo. Não tenho certeza de como o MAPI funciona e se isso fará isso.

Portanto, meus requisitos são:

  1. Para trazer o e -mail no programa de email do usuário.

  2. Para permitir um ou mais anexos.

  3. Para trabalhar com (espero) todos os clientes de email em qualquer máquina Windows do XP UP (ou seja, XP, Vista ou 7).

Existe um animal assim? Ou talvez alguém saiba como fazer com que os anexos funcionem com a técnica Mailto/Shellexecute?

O que a maioria das pessoas faz?


Editar:

Houve algumas respostas com soluções MAPI e até uma solução Indy.

O problema que tenho com eles é que eles não usam necessariamente o cliente de email padrão. Na minha máquina Vista, por exemplo, configurei o Windows Mail como meu cliente padrão. Quando eu faço um MAPI, ele não traz à tona o Windows Mail, mas ele traz e configura o email no Outlook. Eu não quero isso.

Dois dos meus usuários do meu programa reclamaram:

Sua rotina de depuração não envia o arquivo, pois tenta iniciar o Windows Mail por algum motivo conhecido por si mesmo, em vez de usar o cliente de email padrão (no meu caso Thunderbird)

Tentei preencher o relatório de exceção, mas desisti quando ele solicitou esse servidor, esse servidor! Fiquei muito irritado porque lançou o Outlook - nunca o uso ou quero usá -lo.

Não preciso de código para MAPI ou Indy. Eles estão prontamente disponíveis. Mas se você sugerir Mapi ou Indy, o que eu realmente preciso é uma maneira de encontrar o cliente padrão e garantir que seja o que é passado o email a ser enviado.

Além disso, preciso saber se o MAPI agora é universal. Há cinco anos, não era garantido que não trabalhasse em todas as máquinas porque não foi instalado como parte do sistema operacional. Isso ainda é verdadeiro, ou o MAPI agora vem com o Windows XP, Vista e 7 por padrão?

As mesmas perguntas vão para a Indy ou qualquer outra soluções sugeridas. Ele pode funcionar com o cliente padrão e funcionará em quase todas as máquinas do Windows XP e posterior?

A razão pela qual a solução "Mailto" é tão boa, é que todas as máquinas precisam apoiá -la com o objetivo de lidar com a instrução HTML Mailto encontrada nas páginas da web. Agora, se eu pudesse usá -lo para adicionar anexos ...


Solução provável encontrada: MJUSTIN apontou uma alternativa que utiliza o comando sendto do sistema operacional. Provavelmente é o caminho a percorrer.

O Mailto não se limitou a 256 caracteres como o HTML Mailto, mas fiquei arrasado ao descobrir que acabou sendo limitado a 2048 caracteres. Felizmente, algumas horas depois, Mjustin deu sua resposta.

Se a implementação disso corre bem, sua resposta terá feito isso por mim. Caso contrário, adicionarei meus comentários aqui.


Não. Como se vê, a solução SendTo nem sempre abrirá o programa de email padrão. Na minha máquina, ele abre o Outlook quando meu Mailer padrão é o Windows Mail. Que pena. Eu tive que voltar ao método Mailto, apesar do limite de 2048 caracteres.

No entanto, encontrei no artigo: Enviar o destinatário do correio este:

Neste ponto, você pode substituir :: shellexecute por uma chamada bem pensada :: Winexec, usando a linha de comando correio e correio declarada no registro e direcionada ao atual cliente de e-mail (por exemplo, "%programsfiles% Outlook Express msimn .exe " /Mailurl:%1). Mas então a limitação é de 32 kb. Como conclusão, não há como enviar e-mails maiores que 32kb usando o protocolo MailTo.

Mas então eu teria que determinar quem está o cliente de email em cada caso. Espero que isso leve a mais complicações.

A outra coisa que descobri é que o Mailto permite a definição de "para", "CC", "BCC", "Subject" e "Body", mas sem acessórios. Enquanto o envio apenas permite anexos e, em seguida, configura um email padrão com uma mensagem padrão e de maneira alguma para você definir os vários campos e corpo.

Foi útil?

Solução 3

Parece que o Mailto em um Shellexecute não é capaz de enviar anexos.

Mapi e Indy têm a característica infeliz de não necessariamente selecionar o cliente de email do usuário.

Portanto, a outra possibilidade é continuar usando o Shellexecute, mas encontre outra maneira de colocar os anexos no cliente de email.

O que eu decidi fazer foi na minha caixa de diálogo que cria o email, agora tenho um FilElistBox listando os arquivos que o usuário pode querer anexar ao email. Quando o email aparece, eles podem simplesmente arrastá -los e soltá -los para o email.

No meu caso, essa é realmente uma boa solução, pois isso permite que os usuários selecionem os arquivos que desejam incluir. O outro método (anexá -los automaticamente) exigirá que eles excluam os que não desejam incluídos. (ou seja, ter a opção "Adicionar barra de ferramentas do Google" já verificada para você não é boa)

Por enquanto, essa solução funcionará.

Obrigado a todos aqueles que contribuíram com respostas e me ajudaram a ver o meu caminho (tudo +1).

Outras dicas

Não complique, basta usar o Jcl Código MAPI. Está na unidade jclmapi.pas. Eu acho que eles também têm o exemplo disso. O código é muito poderoso e você pode fazer qualquer coisa que o MAPI permita.

Com o Shellexecute, você não pode enviar o anexo e também está limitado a 255 caracteres para o corpo de correio.

Enquanto o MAPI avança, com o Windows antigo, sempre está instalado (2000, XP). Ele se reúne com o Outlook Express e o Outlook Express está quase sempre instalado. Com o Windows mais recente (Vista, 7), não há o Outlook Express e, portanto, nenhum MAPI. Mas o MAPI é instalado automaticamente se você instalar o MS Outlook ou o Mozzila Thunderbird. Então você está bem seguro. Este é o MAPI básico e não o MAPI estendido. Mas ele cobre tudo o que você precisa.

Você também pode verificar seu código (JCL) se o MAPI estiver instalado e agir de forma acordada. Eu fiz uma coisa semelhante há pouco tempo e funciona bem. Não encontrei um cliente popular do Windows Mail que não suporta MAPI simples. Este é um invólucro simples em torno do código JCL e o uso de amostra abaixo:

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.

Uso de amostra:

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.

Eu uso dois métodos para enviar um Mail MAIL, dependendo de ser necessária uma tarefa. Para o caso simples, sem anexo, uso o seguinte:

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;

Isso usa um método simples de execução de shell; portanto, você não deve ter nenhum problema real além dos alertas mais recentes para que o usuário confirme que eles estão bem com o seu programa enviando um email.

Para os empreendimentos, uso o seguinte código originalmente retirado da revista Delphi por Brian Long. Também é possível enviar um email sem usar o cliente MAPI, mas usando um servidor SMTP nomeado, mas acho que você explicitamente não deseja isso. Eu posso fornecer código para isso, se você o fizer.

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;

Este artigo Mostra como o Delphi pode simular o comando "Enviar para ..." do menu de contexto do shell e abrir o cliente de email padrão com anexos programaticamente.

Esta solução não precisa de MAPI e funciona com o cliente de email padrão, mas não está completo, porque os destinatários da mensagem, o corpo e o sujeito não serão preenchidos automaticamente. (O corpo da mensagem pode ser copiado usando a área de transferência).

Aqui está um resumo sobre todas essas configurações de e -mail e o que elas fazem:
http://thesunstroke.blogspot.de/2017/03/how-to-configure-eurekalog-sto-send-bugs.html

enter image description here

Então, fique longe do shell (Mailto).
O MAPI também é uma má idéia, pois funciona apenas com clientes de email MS.
Defina por padrão MAPI simples, mas raramente recebo e -mails enviados por este canal. A maioria dos e -mails é recebida via servidor SMTP.

Grande aviso !!!!!!!!!
Vi que o número de alarmes falso -positivos dos scanners antivírus é muito maior quando você ativa o Eurekalog. Portanto, use apenas Eurekalog quando absolutamente necessário.
Além disso, o próprio Eureka está repleto de insetos (basta olhar para o histórico de lançamento e ver que, para cada novo recurso (ou até mesmo mudar) que eles liberam, eles mais tarde corrigem alguns bugs! Pode apresentar poucos em seu exe!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top