メール受信一覧について


由香  2010-01-19 20:48:05  No: 37074

メールの受信一覧をクリックすると,メール本文が表示されるようにしているのですが,いつも実行時にエラーが出ます。「List index out of bounds」や「Access violation at adress・・・ 」など出ます。一番古い2通のメールは表示されるのですが、3通目から本文が表示されません。
これは何なんでしょうか?
コードはこちらです。

procedure TForm1.btnReceptionClick(Sender: TObject);
var
  i: integer;
  a,b,s: string;
begin
begin

  IdPOP31.Connect;

  a := '  送信者:';
  b := '  件名:';

  //メールの送信者,送信日,件名,本文を表示する
  for i :=  1 to  IdPOP31.CheckMessages do  begin
  SetLength(Main,IdPOP31.CheckMessages);
    Msg :=  TIdMessage.Create(Self);
    IdPOP31.Retrieve(i, Msg);
    
    ListBox1.Items.Add(datetostr(msg.Date)+  a  + Msg.From.Text +  b  + Msg.Subject);
    if(Msg.MessageParts.Items[0] is TIdText)then
    s := TIdText(Msg.MessageParts.Items[0]).Body.Text;
        Main[i] := jconvert.ConvertJCode(s, SJIS_OUT);
        end;
  end;
 IdPOP31.Disconnect;
end;


はて  2010-01-19 21:34:12  No: 37075

>これは何なんでしょうか?
なんでしょうね?
Mainとは?
受信メールは何通?
ループの中でTIdMessageを何度もCreateしない方がいい。破棄もしてないようだし。


tor  2010-01-19 23:48:01  No: 37076

SetLengthしているところから、Mainは何かの動的配列だと推測されますが…
動的配列の場合、長さしか指定できないので、添字は必ず0から始まります。
下限が0で個数がNだったら、添字の範囲は 0 から N-1 までです。

例えばIdPOP31.CheckMessagesの値が3とした場合、長さが3に設定されるので、利用できる配列の要素は

Main[0], Main[1], Main[2]

の3つです。それに対してforループは1から始めていますから

         Main[1], Main[2], Main[3]

となって、用意した領域をはみ出してしまいますね。

範囲エラーだとメッセージが出ているので、まずはこういった添字の指定が合っているかチェックしてみてはどうでしょうか。
ついでに、SetLengthは1回だけやればいいので、ループの中に入れる必要はないと思います。


Mr.XRAY  2010-01-20 12:36:20  No: 37077

Mr.XRAYです.
酒が入って,途中で寝て,今目が醒めてしまいましたので,ちょっとやってみました.
まず,苦言を.提示してあるコードおかしいです.
そんなに長いコードではないと思いますので,キチンと掲載しましょうね.

そこで,勝手にこんなことがしたいと想像(創造かもしれない?)したものです.

新規フォームにIdPOP3,Button,istbox,RichEditを配置して以下のコードをコピペしてください.
また,環境の記載がないので,動作確認は以下です.
他のバージョンでは,文字コードのコンバートが必要かも知れません.
(TPanelとTSplitterを使用していますが,体裁のためです.無視してOKです)

Windows XP(SP3) + Delphi2010(UP5) Pro + Indey 10.5.5

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdExplicitTLSClientServerBase, IdMessageClient, IdPOP3, StdCtrls, ComCtrls,
  ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    ListBox1: TListBox;
    Splitter1: TSplitter;
    RichEdit1: TRichEdit;
    Button1: TButton;
    IdPOP31: TIdPOP3;
    procedure Button1Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses IdMessage, IdText;

{$R *.dfm}

//=============================================================================
//  使用したオブジェクトは必ず破棄
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  i:integer;
begin
  for i := 0 to ListBox1.Items.Count-1 do
    Dispose(PString(ListBox1.Items.Objects[i]));
end;

//=============================================================================
//  受信
//  受信したらListBoxのItemのCaptionに送信者等の文字列を代入
//  ListBoxのItem番号と対応のあるメール本文を表示させるために,ItemのDataオブ
//  ジェクトに本文を代入しておく
//  配列などを用いて,その配列要素番号とListBoxのItem番号の対応でメール本文を
//  取出してもよい
//  この方が動作は軽くなるが,このサンプルではテキスト部分だけを扱っているので,
//  目に見える差はないだろう
//
//  このサンプルコードでは,添付ファイルの処理は行っていない
//
//  TIdMessaegの使用にはusesにIdMessageが必要
//  TIdTextの使用にはusesにIdTextが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);

  //ListViewのItemのDataプロパティに追加する文字オブジェクト生成
  function NewStr(S: string): PString;
  begin
    New(Result);
    Result^ := S;
  end;
  //---------------------------------------------------------------------------
var
  i   : Integer;
  k   : Integer;
  a   : string;
  b   : string;
  s   : string;
  Msg : TIdMessage;
  ACaption : string;
begin
  //Dataオブジェクトがあったら破棄
  for i := 0 to ListBox1.Items.Count-1 do
    Dispose(PString(ListBox1.Items.Objects[i]));

  //アカウント類の設定
  IdPOP31.Host     := 'pop.nifty.ne.jp';
  IdPOP31.Username := 'HGH03072';
  IdPOP31.Password := '98HGXON';

  //固定値なので最初の方で設定
  a := '  送信者:';
  b := '  件名:';

  IdPOP31.Connect;

  //クリアしないと同じメールを何度でも表示することになる
  ListBox1.Items.Clear;

  Msg :=  TIdMessage.Create(Self);

  //受信開始
  for i := 1 to  IdPOP31.CheckMessages do
  begin
    IdPOP31.Retrieve(i, Msg);

    ACaption := datetostr(msg.Date)+  a  + Msg.From.Text +  b  + Msg.Subject;

    if Msg.MessageParts.Count > 0 then
    begin
      for k := 0 to MSG.MessageParts.Count -1 do
      begin
        //HTMLメールのHTML部分または添付のHTMLメールのHTMLテキスト
        if Msg.MessageParts[k].ContentType = 'text/html' then
        begin
          s := TIdText(Msg.MessageParts[k]).Body.Text;
          ListBox1.AddItem(ACaption, TObject(NewStr(s)));
        end else
        //HTMLメールののplainテキスト部分
        if Msg.MessageParts[k].ContentType = 'text/plain' then
        begin
          s := TIdText(Msg.MessageParts[k]).Body.Text;
          ListBox1.AddItem(ACaption, TObject(NewStr(s)));
        end else
        begin
          //添付ファイル類なので取得しても表示不可
        end;
      end;
    end else
    begin
      //plainテキスト形式のメール本文
      s := Msg.Body.Text;
      ListBox1.AddItem(ACaption, TObject(NewStr(s)));
    end;
  end;
  IdPOP31.Disconnect;
end;

//=============================================================================
//  ListBoxのItemをクリックしたらDataプロパティからテキストを取出して表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
  AIndex : Integer;
  S : string;
begin
  AIndex := ListBox1.ItemIndex;
  if AIndex < 0 then exit;

  RichEdit1.Clear;
  if ListBox1.Items.Objects[AIndex] <> nil then
  begin
    s := PString(ListBox1.Items.Objects[AIndex])^;
    RichEdit1.Text := s;
  end;
end;

end.

また眠くなってきましたので,詳しい説明は省略します.
コード内のコメントでご容赦ください.
メールの知識も必要となるでしょう.

初心者には難解となるかも知れませんが,他のサンプルもあります.
http://mrxray.on.coocan.jp/Delphi/plSamples/776_IdPOP3MailClient.htm


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加