コンテンツにスキップ

【移植#35】メール本文の改行を送信前に整える — SendMail用CRLFプリフライトをMQL5で作る

EURUSDの1時間足でLF単独のメール本文をCRLFへ正規化し、5ケースと8バッファの検証成功を表示したMT5画面

この記事の3行まとめ

  • LF・CR・CRLFが混在したメール本文を、送信前にCRLFへ統一します
  • 元記事の「失敗メールが残って後続も失敗したように見える」事例から、件名とログを対応づける大切さを読み直します
  • 専用MT5で5種類の入力と8個の診断値を照合し、メールを実送信せずに前処理を検証しました

今回の原典は「改行を直したのに、まだ失敗する」

投稿日順では、2010年1月22日の相場観に関する記事と、1月23日のセキュリティ製品に関する記事が続きます。どちらもMQL移植の技術題材ではないため、次の1月24日の記事へ進みます。

元記事「Yahoo Mail にメールを送る時は、改行に注意する。」を読む

元記事では、MQL4のSendMail()へ渡す本文を\nだけで改行すると送信エラーになり、\r\nへ直す必要があると説明されていました。

ところが、本文を直してもう一度実行しても同じエラーが出ます。ログの件名を見比べると、失敗していたのは修正版ではなく、最初に投入したメールでした。当時の端末では失敗したメールを再送しようとしていたため、端末を再起動してから修正版を送ると成功した、という流れです。

ここには、改行コード以外にも大切な教訓があります。

  • 修正後の実行結果だと思い込まず、件名や識別子でログと入力を対応づける
  • 送信要求を受け付けたことと、相手へ届いたことを分ける
  • 古い失敗状態が残り得る処理では、再試行前にキューや端末状態を確認する

なお、「失敗メールが残り、端末再起動で解消した」という部分は2010年当時のMT4で観察された挙動です。現在のMT5でも同じ再送動作になるとは限らないため、今回は再現条件に含めていません。


\n\r\nは何が違うのか

文字列内の改行は、画面上では同じように見えても内部の文字コードが異なります。

表記 コード 意味
\r 13 CR(キャリッジリターン)
\n 10 LF(ラインフィード)
\r\n 13, 10 CRとLFの組み合わせ

元記事の最初の本文は、次のようにLFだけを使っていました。

MQL
string body = "Hello\nThis is test\n";

今回のMQL5版は、送信直前に本文を1文字ずつ調べます。既にCRLFになっている箇所はそのまま保持し、LF単独とCR単独だけをCRLFへ置き換えます。

Text Only
CRLF   → CRLFのまま
LF単独 → CRLF
CR単独 → CRLF

単純な一括置換でLFを先に置き換えると、既存のCRLFがCR + CRLFになりかねません。1文字ずつ読み、CRの次がLFなら2文字を1組として消費することで、既に正しい本文へ何度適用しても結果が変わらないようにしています。


MQL5版は送信せずに「送信前」を見える化する

配布版はチャートへ適用するインジケーターです。メールを勝手に送ることはありません。入力したサンプル本文について、次の診断をパネルと8個の計算バッファへ出します。

診断 既定サンプルの結果
変換前の孤立LF 2
変換前の孤立CR 0
変換後のCRLF 2組
変換後の孤立LF 0
変換後の孤立CR 0
本文が変わったか YES
プリフライト判定 READY
変換後のUTF-8バイト数 21

パネルでは制御文字をそのまま改行せず、<LF><CRLF>へ置き換えて表示します。これなら、見た目だけでは区別できない改行の違いをチャート上で確認できます。

MQL5のStringGetCharacter()は、指定位置の文字コードをushortで返します。今回の実装では13と10を直接確認し、それ以外の文字はShortToString()で戻しています。文字列の扱いは公式のStringGetCharacterリファレンスでも確認できます。

UTF-8のバイト数はStringToCharArray()で別に数えています。MQL5の文字列長は文字数であり、UTF-8へ変換した後のバイト数とは一致しない場合があるためです。詳細は公式のStringToCharArrayリファレンスを参照してください。


SendMail()の戻り値を「配信成功」と読まない

現在のMQL5公式リファレンスでは、SendMail(subject, text)trueを返すのは、メールが送信キューへ入ったときです。相手の受信箱へ届いたことまで保証する戻り値ではありません。また、端末のメール設定で送信が無効、宛先が未設定などの場合は失敗し、詳しいエラーはGetLastError()で確認します。ストラテジーテスター内では動作しません。

詳しい条件は公式のSendMailリファレンスにまとまっています。

実際のコードへ組み込む場合は、次の順番に分けると追跡しやすくなります。

  1. 本文をCRLFへ正規化する
  2. 孤立LFと孤立CRが0か確認する
  3. 端末のメール送信設定が有効か確認する
  4. ResetLastError()後にSendMail()を呼ぶ
  5. 件名、戻り値、GetLastError()を同じログ行へ残す
  6. 到着確認は端末のキュー投入とは別に行う

今回の配布版は1と2だけを担当します。外部メールサーバーや実在の宛先を使わずに前処理を検査できるので、認証、通信、本文生成の問題を切り分けやすくなります。


専用MT5での照合結果

検証EAは配布インジケーターの関数を呼び出さず、改行の正規化を独立に実装しました。次の5ケースについて、変換後の文字列が期待値と一致し、孤立したCRまたはLFが残らないことを確認しています。

入力 期待する結果 判定
LF単独 CRLF 一致
既存のCRLF 変更なし 一致
CR単独 CRLF 一致
CRLF・LF・CRの混在 すべてCRLF 一致
改行なし 変更なし 一致
確認項目 結果
対象 EURUSD・H1
独立比較ケース 5種類
診断バッファ 8本
変換後のCRLF 2組
変換後の孤立LF/CR 0/0
変換後のUTF-8バイト数 21
プリフライト判定 READY
コンパイル 0エラー・0警告
外部メール送信 なし
売買注文 なし
実画面キャプチャー 成功

この検証で確認できるのは、本文の正規化と診断値です。メールサーバーへの接続、認証、送信キューの現在の再試行仕様、実際の到着までは確認していません。


ダウンロードと使い方

35_SendMail_CRLF_Preflight_v1_00.mq5 をダウンロード

MT5のIndicatorsへ保存してコンパイルし、任意のチャートへ適用してください。既定のInpSampleBodyにはLF単独の2改行が入っています。入力を変えると、パネルの変換前後と診断値が更新されます。

実際のメール本文へ転用するときは、配布ファイル内のNormalizeCrLf()AnalyzeLineEndings()を送信処理の直前で利用できます。ただし、配布インジケーター自体はSendMail()を呼びません。


使えないこと・注意点

  • CRLFへ整えただけでは、メールの送信や到着は保証できません。
  • SMTP設定、認証、宛先、通信状態、サーバー側の拒否、送信頻度は別に確認が必要です。
  • 2010年当時のMT4で見られた失敗メールの再送挙動を、現在のMT5仕様として断定するものではありません。
  • UTF-8バイト数は本文をCP_UTF8へ変換した値です。実際のメール転送時の符号化やヘッダー全体のサイズを表すものではありません。
  • 本文内の改行だけを対象にしており、件名や添付ファイルは検査しません。
  • SendMail()はストラテジーテスターでは動作しないため、実送信を確認する場合は設定済み端末で別途行う必要があります。

ロジック評価の結論

今回の原典で一番印象的なのは、正しい修正を入れても、見ているログが古ければ「直っていない」と判断してしまう点です。

メール、通知、注文、ファイル出力のように処理がキューをまたぐ機能では、入力と結果の対応が曖昧になりやすくなります。件名や識別子を変え、投入時刻を残し、前処理と外部送信を分けて検査する。それだけで、文字列の問題なのか、端末設定なのか、過去の失敗なのかをかなり絞れます。

古いMQL4の小さな改行トラブルは、MQL5では「送る前に正規化し、送った後はキュー投入と到着を分けて観測する」という、より一般的なデバッグ手順へ移植できました。


前後の記事

前: 【移植#34】7行のCTSLをほどいて再構成する — Gann Swing OscillatorをMQL5へ

— SPONSORED —