Delphiプログラムは、デフォルトの電子メールクライアントを介して添付ファイルを含むメールをどのように送信できますか?
-
21-09-2019 - |
質問
私のプログラム内で、ユーザーのマシンにインストールされているデフォルトの電子メールクライアントソフトウェアを使用して送信する電子メールを作成しています。
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);
Windows VistaマシンでDelphi 2009を使用すると、Microsoftメール「Mailの作成」ウィンドウを開き、件名とボディが正しく入力されます。ただし、ファイルは添付されません。
これを調査したとき、この手法はすべてのメールクライアントでは機能しないと述べているいくつかの解説に気付きました。しかし、これは非常に古いテクニックであることがわかっているので、解説のほとんどはかなり古いものです。
それから私はそれを見つけました Zarko Gajicは言った その「このアプローチは大丈夫ですが、この方法で添付ファイルを送信することはできません」。
Windows Simple Mail API(MAPI)も見たことがありますが、Zarkoは、エンドユーザーにMAPI準拠の電子メールソフトウェアがある場合にのみ機能すると言います。 DelphiでMapiを使用することに関する十分な文書化された手法があります(例: Mapiを使用して電子メールを送信します)、しかし、それらはすべて、Mapiが常にWindowsでインストールされているとは限らないという免責事項を持っています。
その上、私は本当にユーザーのデフォルトの電子メールプログラムで最初にメッセージを掲載したいので、彼らはそれを電子メールレコードの一部として持っていて、それを編集して、いつ送信したいかを決定することができます。 Mapiがどのように機能するか、それがそれを行うかどうかはわかりません。
だから私の要件は次のとおりです。
ユーザーのメールプログラムにメールを送信するため。
1つ以上の添付ファイルを許可します。
XP Up(IE XP、Vista、または7)のWindowsマシン上のすべての電子メールクライアントと(できれば)クライアントをすべて扱うこと。
そのような動物はありますか?それとも、誰かがMailTo/ShellexeCuteテクニックで作業するために添付ファイルを取得する方法を知っていますか?
ほとんどの人は何をしますか?
編集:
Mapiソリューションとインディソリューションでさえ、いくつかの答えがありました。
私が彼らに抱えている問題は、彼らが必ずしもデフォルトのメールクライアントを使用しないことです。たとえば、Vistaマシンでは、デフォルトのクライアントとしてWindowsメールを設定しました。 Mapi Sendを行うと、Windowsメールが表示されませんが、代わりにOutlookのメールを作成してセットアップします。私はそれを望んでいません。
私のプログラムの2人のユーザーが不平を言った:
デバッグルーチンは、デフォルトのメールクライアント(私の場合はThunderbirdで)を使用するのではなく、何らかの理由でWindowsメールを起動しようとするため、ファイルの送信に失敗します。
私は例外レポートに記入しようとしましたが、このサーバーであるこのサーバーを求めたときにあきらめました!それは、それがOutlookを立ち上げたので、本当にイライラしました - 私はそれを決して、それを使用したり、使用したりしたいとは決して言いません。
MapiやIndyのコードは必要ありません。それらはすぐに利用できます。しかし、MapiまたはIndyを提案する場合、私が本当に必要とするのは、デフォルトのクライアントを見つけて、それが送信される電子メールに渡されたものであることを確認する方法です。
また、Mapiが現在普遍的であるかどうかを知る必要があります。 5年前、オペレーティングシステムの一部としてインストールされていなかったため、すべてのマシンで作業することは保証されていませんでした。それはまだ真実ですか、それともMapiにはデフォルトでWindows XP、Vista、7が付属していますか?
Indyまたはその他の提案されたソリューションについても同じ質問があります。デフォルトのクライアントで動作することができ、ほぼすべてのWindows XPおよび後のマシンで動作しますか?
「MailTo」ソリューションが非常に優れている理由は、すべてのマシンがWebページで見つかったHTML Mailtoステートメントを処理する目的でサポートする必要があるためです。今、私がそれを使用して添付ファイルを追加できるなら...
発見される可能性のある解決策:Mjustinは、オペレーティングシステムのSendtoコマンドを利用する代替案を指摘しました。それはおそらく行く方法です。
Mailtoは、HTML Mailto ISのような256文字に限定されませんでしたが、2048文字に制限されていることがわかりました。幸いなことに、数時間後、Mjustinは彼の答えを与えました。
その実装が大丈夫なら、彼の答えは私のためにそれをしたでしょう。そうでない場合は、ここにコメントを追加します。
いいえ。結局のところ、Sendtoソリューションは必ずしもデフォルトの電子メールプログラムを開くとは限りません。私のマシンでは、デフォルトのメーラーがWindowsメールであるときにOutlookを開きます。残念な。 2048年の文字制限にもかかわらず、Mailtoメソッドに戻らなければなりませんでした。
しかし、私は記事で見つけました: メール受信者を送信します それ:
この時点で、:: shellexecuteをよく考えている:: winexec呼び出しに置き換えることができます。レジストリで宣言された実際のmailtoコマンドラインを使用して、現在の電子メールクライアントをターゲットにします(たとえば、 "%programfiles% outlook express msimn .exe " /mailurl:%1)。しかし、制限は32 kbです。結論として、MailToプロトコルを使用して32kbを超える電子メールを送信する方法はありません。
しかし、その後、私はそれぞれの場合にメールクライアントが誰であるかを判断する必要があります。それがさらなる合併症につながると思います。
私が見つけたもう1つのことは、Mailtoが「to」、「cc」、「bcc」、「subject」、「body」の設定を許可することです。一方、sendtoは添付ファイルのみを許可し、デフォルトのメッセージを含むデフォルトの電子メールを設定し、さまざまなフィールドとボディを設定する方法がありません。
解決 3
ShellexeCuteのMailtoは添付ファイルを送信できないようです。
MapiとIndyには、ユーザーのメールクライアントを必ずしも選択しないという不幸な特徴があります。
そのため、他の可能性はShellexeCuteの使用を継続することですが、添付ファイルを電子メールクライアントに入れる別の方法を見つけることです。
私がやろうと決めたのは、電子メールを作成するダイアログで、ユーザーが電子メールに添付したいファイルをリストするFileListBoxが表示されました。電子メールが表示されると、電子メールにドラッグアンドドロップするだけです。
私の場合、これは実際には優れたソリューションです。これにより、ユーザーが含めるファイルを選択できるためです。他の方法(自動的にそれらを添付する)では、含まれていないものを削除する必要があります。 (つまり、「Google Toolbarの追加」オプションを既にチェックしていることは良くありません)
とりあえず、このソリューションは機能します。
答えを寄付し、これを通して自分の道を見るのを手伝ってくれたすべての人に感謝します(すべて+1)。
他のヒント
複雑にならないでください、ただ使用してください JCL MAPIコード。ユニットJclmapi.pasにあります。彼らにもその例があると思います。コードは非常に強力であり、Mapiが許すものは何でもできます。
shellexecuteを使用すると、添付ファイルを送信することはできません。また、メールボディの255文字に制限されています。
Mapiが進む限り、古いWindowsを使用して常にインストールされます(2000、XP)。 Outlook ExpressとOutlook Expressは、ほとんど常にインストールされています。新しいWindows(Vista、7)には、Outlook Expressがなく、Mapiもありません。ただし、MS OutlookまたはMozzila Thunderbirdをインストールすると、Mapiは自動的にインストールされます。だからあなたはかなり安全です。これは基本的なMapiであり、拡張されたMapiではありません。しかし、それはあなたが必要とするすべてをカバーします。
Mapiがインストールされている場合は、コード(JCL)をチェックインすることもできます。私はそれほど前に同じようなことをしましたが、それはうまくいきます。単純なMapiをサポートしていない人気のあるWindowsメールクライアントを見つけていません。これは、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.
Attatchmentが必要かどうかに応じて、MAPIメールを送信するために2つの方法を使用します。添付ファイルのない単純なケースについては、次のことを使用します。
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;
これにより、Simple Shell Executeメソッドが使用されるため、最近のアラート以外の実際の問題はありません。ユーザーに、プログラムがメールを送信しても問題ないことを確認してもらいます。
攻撃には、ブライアン・ロングが元々Delphi Magazineから撮影した次のコードを使用します。 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;
この記事 Delphiが「送信...」シェルコンテキストメニューコマンドをシミュレートし、プログラムで添付ファイルを使用してデフォルトのメールクライアントを開く方法を示します。
このソリューションはMAPIを必要とせず、デフォルトのメールクライアントで動作しますが、メッセージ受信者、本体、および被験者が自動的に入力されないため、完全ではありません。 (メッセージ本文は、クリップボードを使用してコピーできます)。
これらすべての電子メール設定とそれらが何をするかについての要約を次に示します。
http://thesunstroke.blogspot.de/2017/03/how-to-configure-eurekalog-to-send-bugs.html
したがって、シェル(Mailto)から離れてください。
MAPIは、MS Emailクライアントのみで機能するため、悪い考えです。
デフォルトでSimple Mapiを設定しましたが、このチャンネルから送信された電子メールを受け取ることはめったにありません。ほとんどの電子メールはSMTPサーバーを介して受信されます。
大きな警告!!!!!!!!!
Eurekalogを活性化すると、アンチウイルススキャナーからの偽陽性アラームの数がはるかに高くなることがわかりました。したがって、絶対に必要な場合にのみEurekalogを使用してください。
また、ユーレカ自体にはバグが貼られています(リリース履歴を見て、リリースする新しい機能(または変更)について、後でいくつかのバグを修正することを確認してください!あなたのexeに少数を紹介するかもしれません!