В рамках моей программы я составляю электронное письмо для отправки использования программного обеспечения почтового клиента по умолчанию, установленного на машине пользователя.
Я составил адрес 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 работает и если он сделает это.
Таким образом, мои требования:
Поднять электронную почту в почтовой программе пользователя.
Позволить одно или несколько вложений.
Работать с (надо надеяться), всеми почтовыми клиентами на любой машине 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 ТОЛЬКО позволяет вложения и затем настраивает электронное письмо по умолчанию с сообщением по умолчанию и никаким способом для Вас установить различные поля и тело.
Похоже, что mailto в ShellExecute не способен отправлять вложения.
MAPI и Indy имеют неудачную характеристику - необязательно выбирать почтовый клиент пользователя.
Другая возможность - это продолжить использовать ShellExecute, но найти другой способ получить вложенные файлы в почтовом клиенте.
Я решил сделать это в моем Dialog, который создает письмо, теперь у меня есть FileListBox со списком файлов, которые пользователь может захотеть прикрепить к письму. Когда письмо всплывает, они могут просто перетащить его на электронную почту.
В моем случае это действительно хорошее решение, так как это позволяет пользователям выбирать файлы, которые они хотят включить. Другой метод (автоматическое прикрепление) потребует удаления тех файлов, которые они не хотят включать. (т.е. наличие уже установленной для вас опции "Добавить панель инструментов Google" НЕ является хорошим решением)
Пока это решение будет работать.
Спасибо всем тем, кто предоставил ответы и помог мне увидеть свой путь (все +1).
Не усложняйте, просто используйте код 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.
Эта статья показывает, как Delphi может моделировать команду контекстного меню оболочки "Send to..." и программно открывать почтовый клиент по умолчанию с вложениями.
Это решение не нуждается в MAPI и работает с почтовым клиентом по умолчанию, но не является полным, потому что получатели, тело и тема сообщения не будут заполнены автоматически. (Тело сообщения можно скопировать с помощью буфера обмена)
.Я использую два метода для отправки почты 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;