14

私のプログラムでは、ユーザーのマシンにインストールされているデフォルトの電子メール クライアント ソフトウェアを使用して送信する電子メールを作成しています。

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. ユーザーのメール プログラムで電子メールを表示します。

  2. 1 つ以上の添付ファイルを許可します。

  3. XP (つまり、XP、Vista、または 7) 以降の任意の Windows マシン上のすべての電子メール クライアントで (うまくいけば) 動作します。

そんな動物いる?または、添付ファイルを mailto/ShellExecute テクニックで動作させる方法を知っている人はいますか?

ほとんどの人は何をしますか?


編集:

MAPI ソリューションと Indy ソリューションでさえ、いくつかの回答がありました。

私が抱えている問題は、必ずしもデフォルトのメール クライアントを使用しているとは限らないことです。たとえば、私の Vista マシンでは、Windows メールを既定のクライアントとしてセットアップしました。MAPI 送信を行うと、Windows メールは表示されませんが、代わりに Outlook で電子メールが表示および設定されます。私はそれをしたくありません。

私のプログラムの 2 人のユーザーが不満を漏らしました。

デフォルトのメールクライアント(私の場合はサンダーバード)を使用するのではなく、何らかの理由で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 のように 256 文字に制限されていませんでしたが、最終的に 2048 文字に制限されていることを知り、打ちのめされました。幸いなことに、数時間後、mjustin が答えをくれました。

その実装がうまくいけば、彼の答えは私のためにそれを行ったでしょう. そうでない場合は、ここにコメントを追加します。


いいえ。結局のところ、sendto ソリューションは常にデフォルトの電子メール プログラムを開くわけではありません。私のマシンでは、既定のメーラーが Windows メールの場合に Outlook が開きます。残念な。2048 文字の制限にもかかわらず、mailto メソッドに戻らなければなりませんでした。

ただし、次の記事で見つけました: SendTo メール受信者:

この時点で、レジストリで宣言されている実際の mailto コマンド ラインを使用して、よく考えられた ::WinExec 呼び出しで ::ShellExecute を置き換え、現在の電子メール クライアントをターゲットにすることができます (たとえば、"%ProgramFiles%\Outlook Express\msimn .exe" /mailurl:%1)。ただし、制限は 32 KB です。結論として、mailto プロトコルを使用して 32KB を超える電子メールを送信する方法はありません。

しかし、その場合、メール クライアントが誰であるかをそれぞれの場合に判断する必要があります。それがさらなる複雑化につながると思います。

もう 1 つ分かったのは、mailto では "to"、"cc"、"bcc"、"subject"、"body" を設定できますが、添付ファイルは設定できないということです。sendto は添付ファイルのみを許可し、デフォルトのメッセージを含むデフォルトの電子メールを設定しますが、さまざまなフィールドや本文を設定する方法はありません。

4

5 に答える 5

5

複雑にしないでください。JCLMAPIコードを使用してください。ユニットJclMapi.pasにあります。彼らにもその例があると思います。コードは非常に強力であり、MAPIで許可されていることなら何でも実行できます。

ShellExecuteを使用すると、添付ファイルを送信できません。また、メール本文の文字数は255文字に制限されています。

MAPIが使用されている限り、古いウィンドウでは常にインストールされます(2000、XP)。Outlook Expressと一緒に付属しており、ほとんどの場合OutlookExpressがインストールされています。新しいウィンドウ(Vista、7)では、Outlook Expressがないため、MAPIはありません。ただし、MSOutlookまたはMozzilaThunderbirdをインストールすると、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.
于 2009-12-26T14:35:25.120 に答える
4

ShellExecute の mailto は添付ファイルを送信できないようです。

MAPI と Indy には、ユーザーの電子メール クライアントを必ずしも選択しないという残念な特性があります。

そのため、ShellExecute を引き続き使用することもできますが、添付ファイルを電子メール クライアントに取り込む別の方法を見つけることもできます。

私がやろうと決めたのは、電子メールを作成するダイアログで、ユーザーが電子メールに添付したいファイルをリストする FileListBox ができました。メールがポップアップしたら、メールにドラッグ アンド ドロップするだけです。

私の場合、これは実際には良い解決策です。これにより、ユーザーは含めるファイルを選択できるからです。もう 1 つの方法 (自動的にアタッチする) では、含めたくないものを削除する必要があります。(つまり、「Google ツールバーを追加」オプションを既にオンにしておくのは良くありません)

当分の間、このソリューションは機能します。

回答を提供してくれたすべての人に感謝し、これを理解するのを助けてくれました (すべて +1)。

于 2009-12-27T01:40:10.583 に答える
4

添付ファイルが必要かどうかに応じて、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;

これは単純なシェル実行メソッドを使用するため、電子メールを送信するプログラムに問題がないことをユーザーに確認させるための最新のアラート以外に、実際の問題はありません。

添付ファイルには、Brian Long が 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;
于 2009-12-26T07:46:40.487 に答える
2

この記事では、Delphi でシェル コンテキスト メニューの「Send to...」コマンドをシミュレートし、添付ファイル付きのデフォルトのメール クライアントをプログラムで開く方法を示します。

このソリューションは MAPI を必要とせず、デフォルトのメール クライアントで動作しますが、メッセージの受信者、本文、および件名が自動的に入力されないため、完全ではありません。(メッセージ本文は、クリップボードを使用してコピーできます)。

于 2009-12-27T11:43:34.050 に答える