SQLの窓

2019年03月19日


C# : SQL 文を外部テキストにして、String.Format でデータ部分を置き換えて利用する

基本的には、String.Format メソッドのお話ですが、文字列の配列の扱いとの関係での注意事項です。
-- ******************************
-- 社員マスタ更新
-- ******************************
 
UPDATE 社員マスタ
set
氏名 = '{1}',
給与 = {2},
-- 行コメント
生年月日 = {3}

--
 
where 社員コード = '{0}'
 
-- 行コメント
行コメントは、正規表現で削除します。{} 部分の個数より、配列が大きい必要があり、Nothing が指定されると、{} ごとなくなります。
※ 配列のリサイズが必要な場合は、Array.Resize メソッドで行います。


C# による記述
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace text_input
{
	class Program
	{
		static void Main(string[] args)
		{
			// 入力ファイルのパス
			string[] arguments = Environment.GetCommandLineArgs();
			// 引数は一つのみ許可
			if (arguments.Length != 2)
			{
				Console.WriteLine("引数を指定して下さい");
				return;
			}

			// 引数から取得
			string filePath = arguments[1];

			// パスを表示
			Console.WriteLine(filePath);

			// *********************************
			// ▼ 関数呼び出しを記述して、ALT + Enter で自動作成
			// *********************************
			string textData = loadTextData(filePath);

			// *********************************
			// 行コメントの削除
			// ▼ 正規表現のオプション
			// https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/regular-expression-options
			// *********************************
			string pattern = "(?m)^--.*";   // 複数行モード
			textData = System.Text.RegularExpressions.Regex.Replace(textData, pattern, "");

			Console.Write(textData);

			// *********************************
			// 配列の準備(1)
			// *********************************
			string[] data1 = { "0001", "山田太郎", "100000", "'1980/01/01'" };
			Console.WriteLine("配列の数は {0} です", data1.Length);

			// *********************************
			// 配列を文字列内に埋め込む
			// *********************************
			string sqlResult = String.Format(textData, data1);
			Console.Write(sqlResult);

			// *********************************
			// 配列の準備(2)
			// *********************************
			string[] data2 = new string[4];
			data2[0] = "0002";
			data2[1] = "山田花子";
			data2[2] = "20000";
			data2[3] = "NULL";

			// *********************************
			// 配列を文字列内に埋め込む
			// *********************************
			sqlResult = String.Format(textData, data2);
			Console.Write(sqlResult);

			// *********************************
			// 配列の準備(3)
			// *********************************
			string[] data3 = new string[4];
			data3.SetValue("0003", 0);
			data3.SetValue("山田美子", 1);
			data3.SetValue("30000", 2);
			data3.SetValue("'1980/01/01'", 3);

			// *********************************
			// 配列を文字列内に埋め込む
			// *********************************
			sqlResult = String.Format(textData, data3);
			Console.Write(sqlResult);

			// *********************************
			// 配列の準備(4)
			// *********************************
			List<string> data4 = new List<string>();
			data4.Add("0004");
			data4.Add("山田史郎");
			data4.Add("40000");
			data4.Add("'1990/01/01'");

			// *********************************
			// 配列を文字列内に埋め込む
			// *********************************
			sqlResult = String.Format(textData, data4.ToArray());
			Console.Write(sqlResult);

			// *********************************
			// 配列の準備(5)
			// ※ 追加するデータ型自由
			// *********************************
			ArrayList data5 = new ArrayList();
			data5.Add("0005");
			data5.Add("山田吾郎");
			data5.Add(50000);
			data5.Add(new DateTime(1995, 1, 1));

			// *********************************
			// 配列を文字列内に埋め込む
			// 混在なので object 配列
			// *********************************
			object[] strWork = data5.ToArray();
			sqlResult = String.Format(textData, strWork[0], strWork[1], strWork[2], $"'{strWork[3]}'");
			Console.Write(sqlResult);

			Console.ReadLine();

		}

		private static string loadTextData(string filePath)
		{

			string text = "";

			// *********************************
			// 主なエンコード
			// *********************************
			// SHIFT_JIS
			// Encoding Enc = Encoding.GetEncoding(932);
			// EUC-JP
			//Encoding Enc = Encoding.GetEncoding(51932);
			// UNICODE 用
			//Encoding Enc = Encoding.GetEncoding(1200);
			// UTF-8N
			Encoding Enc = new UTF8Encoding();
			// UTF-8
			//Encoding Enc = new UTF8Encoding(true);

			// プロック終了時に開放
			try
			{
				using (StreamReader ReadFile = new StreamReader(filePath, Enc))
				{
					// 読込み
					text = ReadFile.ReadToEnd();

					// 全て読み込んでいるので閉じる
					ReadFile.Close();

					Console.Write(text);
				}

			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
			}

			return text;

		}
	}
}

引数は、プロジェクトのプロパティからデバッグタブを開けて設定します。





posted by lightbox at 2019-03-19 09:48 | VS(C#) | このブログの読者になる | 更新情報をチェックする

2019年03月13日


C# コンソールアプリを AN HTTPD で実行

テストにはまだまだ使える(重宝する) AN HTTP Server の正しい使用方法

単純に、WEB アプリケーション初心者にブラウザとサーバーのやり取りを知ってもらう為に( 今は、開発者ツールがあるので途中の解説がしやすいですし )、C# で作った EXE をそのままブラウザで実行させます。

基本設定

EXE の列の『一般パスでも実行する』にチェックします



次に、一般パスに C# で作成された Debug フォルダを登録します。



後は、loclhost から実行するだけです。

ソースコード
using System;
using System.Collections;
using System.Text;
using System.Web;

namespace cgi_test
{
	class Program
	{
		static void Main(string[] args)
		{
			string formtype = "POST";

			string[] param = { };

			// ******************************
			// Console.WriteLine を UTF8 で
			// ******************************
			Console.OutputEncoding = Encoding.UTF8;

			// ******************************
			// メソッド
			// ******************************
			string method = Environment.GetEnvironmentVariable("REQUEST_METHOD");

			// ******************************
			// GET
			// ******************************
			if (method == "GET")
			{
				// ******************************
				// QUERY_STRING
				// ******************************
				string query_string = System.Environment.GetEnvironmentVariable("QUERY_STRING");
				// URL のみの場合はデータ無しの QUERY_STRING を用意する
				if (query_string == "" )
				{
					query_string = "field1=&field2=";
				}

				// & で分割して key=value の文字列の配列を作成する
				param = query_string.Split('&');
			}

			// ******************************
			// POST
			// ******************************
			if (method == "POST")
			{
				string line;

				// POST 時は必ず key=value の文字列が存在する
				line = Console.ReadLine();
				param = line.Split('&');
			}

			// = で区切って key と value が配列の 0 と 1 にセットされる
			string[] key_value = { };

			// ******************************
			// key と value の格納
			// ******************************
			Hashtable field = new Hashtable();

			foreach (string key_value_set in param)
			{
				key_value = key_value_set.Split('=');
				// key がある場合は、Hashtable に格納する
				if (key_value[0] != "") {
					// System.Web を参照して using System.Web; で HttpUtility.UrlDecode
					// %エンコードを元に戻す
					field.Add(key_value[0], HttpUtility.UrlDecode(key_value[1]));
				}
			}

			// ******************************
			// HTTP ヘッダ
			// PHP の session_cache_limiter
			// ******************************
			Console.WriteLine("Content-Type: text/html; charset=utf-8");
			Console.WriteLine("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
			Console.WriteLine("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
			Console.WriteLine("Pragma: no-cache");
			Console.WriteLine();

			string message = "<div>こんにちは世界</div>";

			// ******************************
			// HTML
			// $ で変数埋め込みのヒアドキュメント
			// ******************************
			string html = $@"<!DOCTYPE html>
<html>
<head>
</head>
<body>
{message}
<form method='{formtype}'>
<p>氏名 : <input type='text' name='field1' value='{field["field1"]}'></p>
<p>フリガナ : <input type='text' name='field2' value='{field["field2"]}'></p>
<p>送信 : <input type='submit' name='send' value='送信'></p>
</form>
</body>
</html>";

			// 作成した HTML を出力する
			Console.WriteLine(html);
		}
	}
}





posted by lightbox at 2019-03-13 22:02 | VS(C#) | このブログの読者になる | 更新情報をチェックする

2018年10月19日


C# : SQLServer( SQLExpress ) の SMO を使用してテーブルの CREATE TABLE 文 を取得する

SMO のダウンロード方法は、C# : VB.net : SQLExpress(SQLServer) : SQL-DMO と同等の SMO によるバックアップ を参照して下さい。

こちらは、同じコードを PowerShell で実行します

参考ページ : Generate Scripts for database objects with SMO for SQL Server
※ 参考ページでは、いろいろなオプションが紹介されています。

using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SQLExpress_backup
{
	class Program
	{
		static void Main(string[] args)
		{

			{

				UTF8Encoding UTF8N_Enc = new UTF8Encoding();
				// false は上書き
				StreamWriter WriteFile = new StreamWriter("sqlexpress.sql", false, UTF8N_Enc);

				// サーバー
				Server srv;
				// インスタンス
				srv = new Server();

				// サーバーインスタンスの情報
				srv.ConnectionContext.AutoDisconnectMode = AutoDisconnectMode.NoAutoDisconnect;
				srv.ConnectionContext.LoginSecure = false;
				srv.ConnectionContext.Login = "sa";
				srv.ConnectionContext.Password = "";
				// 接続
				srv.ConnectionContext.Connect();

				// バージョンの表示
				Console.WriteLine(srv.Information.Version);

				// 参考
				// https://www.mssqltips.com/sqlservertip/1833/generate-scripts-for-database-objects-with-smo-for-sql-server/
				// Scripter scripter = new Scripter(srv);

				Database myDb = srv.Databases["lightbox"];
				foreach (Table myTable in myDb.Tables)
				{

					StringCollection tableScripts = myTable.Script();
					foreach (string script in tableScripts)
					{
						WriteFile.WriteLine(script);
					}
				}

				WriteFile.Close();
				WriteFile.Dispose();

				srv.ConnectionContext.Disconnect();

			}


		}
	}
}


結果サンプル

※ sqlexpress.sql に出力された内容です
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
CREATE TABLE [dbo].[コード名称マスタ](
	[区分] [int] NOT NULL,
	[コード] [nvarchar](10) COLLATE Japanese_CI_AS NOT NULL,
	[名称] [nvarchar](50) COLLATE Japanese_CI_AS NULL,
	[数値1] [int] NULL,
	[数値2] [int] NULL,
	[作成日] [datetime] NULL,
	[更新日] [datetime] NULL
) ON [PRIMARY]

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
CREATE TABLE [dbo].[社員マスタ](
	[社員コード] [nvarchar](4) COLLATE Japanese_CI_AS NOT NULL,
	[氏名] [nvarchar](50) COLLATE Japanese_CI_AS NULL,
	[フリガナ] [nvarchar](50) COLLATE Japanese_CI_AS NULL,
	[所属] [nvarchar](4) COLLATE Japanese_CI_AS NULL,
	[性別] [int] NULL,
	[作成日] [datetime] NULL,
	[更新日] [datetime] NULL,
	[給与] [int] NULL,
	[手当] [int] NULL,
	[管理者] [nvarchar](4) COLLATE Japanese_CI_AS NULL,
	[生年月日] [datetime] NULL
) ON [PRIMARY]

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
CREATE TABLE [dbo].[取引データ](
	[取引区分] [nvarchar](2) COLLATE Japanese_CI_AS NOT NULL,
	[伝票番号] [int] NOT NULL,
	[行] [int] NOT NULL,
	[取引日付] [datetime] NULL,
	[取引先コード] [nvarchar](4) COLLATE Japanese_CI_AS NULL,
	[商品コード] [nvarchar](4) COLLATE Japanese_CI_AS NULL,
	[数量] [int] NULL,
	[単価] [int] NULL,
	[金額] [int] NULL,
	[更新済] [nvarchar](1) COLLATE Japanese_CI_AS NULL
) ON [PRIMARY]

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
CREATE TABLE [dbo].[商品マスタ](
	[商品コード] [nvarchar](4) COLLATE Japanese_CI_AS NOT NULL,
	[商品名] [nvarchar](50) COLLATE Japanese_CI_AS NULL,
	[在庫評価単価] [int] NULL,
	[販売単価] [int] NULL,
	[商品分類] [nvarchar](3) COLLATE Japanese_CI_AS NULL,
	[商品区分] [nvarchar](1) COLLATE Japanese_CI_AS NULL,
	[作成日] [datetime] NULL,
	[更新日] [datetime] NULL,
	[備考] [ntext] COLLATE Japanese_CI_AS NULL,
	[削除フラグ] [nvarchar](1) COLLATE Japanese_CI_AS NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
CREATE TABLE [dbo].[得意先マスタ](
	[得意先コード] [nvarchar](4) COLLATE Japanese_CI_AS NOT NULL,
	[得意先名] [nvarchar](50) COLLATE Japanese_CI_AS NULL,
	[得意先区分] [nvarchar](1) COLLATE Japanese_CI_AS NULL,
	[担当者] [nvarchar](4) COLLATE Japanese_CI_AS NULL,
	[郵便番号] [nvarchar](7) COLLATE Japanese_CI_AS NULL,
	[住所1] [nvarchar](100) COLLATE Japanese_CI_AS NULL,
	[住所2] [nvarchar](100) COLLATE Japanese_CI_AS NULL,
	[作成日] [datetime] NULL,
	[更新日] [datetime] NULL,
	[締日] [int] NULL,
	[締日区分] [int] NULL,
	[支払日] [int] NULL,
	[備考] [nvarchar](100) COLLATE Japanese_CI_AS NULL
) ON [PRIMARY]

SET ANSI_NULLS (Transact-SQL)
SET ANSI_NULLS が ON の場合、WHERE column_name = NULL を使用する SELECT ステートメントを実行すると、column_name に NULL 値が入っていた場合は条件は成り立たず、行が返されません。逆に、SET ANSI_NULLS が OFF の場合は、ISO 標準が適用されません。 WHERE column_name = NULL を使用する SELECT ステートメントでは、column_name に NULL 値を持つ行が返されます。

SET ANSI_NULLS OFF;
select * from [社員マスタ] where 手当 = null;

この SQL では、手当が NULL の行が戻されます


SET QUOTED_IDENTIFIER ON にすると、テーブル名等の識別市子を "(ダブルクォート)で囲む事ができます

SET QUOTED_IDENTIFIER OFF; 
select * from "社員マスタ"

この SQL はエラーとなります
posted by lightbox at 2018-10-19 14:10 | VS(C#) | このブログの読者になる | 更新情報をチェックする

2018年09月04日


C# : DataGridView に TKMP.DLL の IMAP(POP3) で受信したメールを非同期に表示する( 添付ファイルも取得 )

※ BasicImapLogon => BasicPopLogon, ImapClient => PopClient で POP3 も同じです

DataGridView の作成方法の注意点は、『C# でDataTable と DataSource を使用して、DataGridView にデータを表示するテンプレート( 行をダブルクリックしてダイアログを表示して行データを処理 )』を参照して下さい

画面のテンプレートのダウンロード


行をダブルクリックすると、本文を表示します。ここでは、サンプルして行のカラムに本文を保存していますが、本来は外部に保存します。



▼ スプリットコンテナを使用していますが、配置方法は基本的に直感的なものと逆です。


※ 重要
Gmail では安全性の低いアプリの許可を『有効』にする必要があります
※ サーバーは imap.gmail.com
※ Yahoo! メールでは、接続はできるのですが、メールが取得できません ( imap.mail.yahoo.co.jp )。
※ BasicImapLogon => BasicPopLogon, ImapClient => PopClient に変更すると、Yahoo! で接続できました
( POP3 : pop.mail.yahoo.co.jp : 995 )

.NET用メール送受信クラスライブラリ (TKMP.DLL) 
※ 2018-06-20 時点で 3.1.8( 2017/02/15 作成 )



using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using TKMP.Net;
using TKMP.Reader;

namespace MailRecTest
{
	public partial class Form1 : Form
	{
		// ******************************************************
		// 画面レイアウト作成方法は以下を参照
		// http://logicalerror.seesaa.net/article/459948736.html
		// ******************************************************

		private DataTable table;
		private DataColumn column;
		private DataRow row;

		private SynchronizationContext sc = null;
		private ImapClient client = null;
		private int mailCounter = 0;
		private string[] bodyText = new string[20];

		// 接続解除用
		private int endCounter = 0;
		private int maxCount = 20;

		public Form1()
		{
			InitializeComponent();
		}

		private void Form1_Load(object sender, EventArgs e)
		{
			// 非同期内からの UI スレッドへのアクセス用
			sc = SynchronizationContext.Current;

			this.toolStripStatusLabel1.Text = "ツールバーのボタンで受信してください";
		}

		private void toolStripButton1_Click(object sender, EventArgs e)
		{
			if (MessageBox.Show("メールを受信しますか?", "確認", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
			{
				return;
			}

			// IMAP 用基本認証
			BasicImapLogon logon = new BasicImapLogon("アカウント", "パスワード");
			// IMAP 用ログイン( 993 は、SSL 用 )
			ImapClient client = new ImapClient(logon, "サーバードメイン", 993);

			// SSL で接続する
			client.AuthenticationProtocol = AuthenticationProtocols.SSL;

			try
			{
				if (!client.Connect())
				{
					MessageBox.Show("接続できませんでした");
					return;
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show("接続エラーが発生しました");
				return;
			}

			// メールデータ一覧を格納するオブジェクト
			IMailData[] mailData = client.GetMailList();

			// データがありません
			if (mailData == null)
			{
				MessageBox.Show("データがありません");
				return;
			}

			// メールデータの数
			toolStripStatusLabel1.Text = mailData.Length.ToString();
			mailCounter = mailData.Length;
			if (mailCounter < maxCount)
			{
				maxCount = mailCounter;
			}

			// 読込み制限
			int idx = 0;


			// DataTable の作成
			table = new DataTable("TKMP");

			// 列情報 の作成
			column = new DataColumn();
			column.DataType = Type.GetType("System.String");
			column.ColumnName = "差出人";
			table.Columns.Add(column);

			// 列情報 の作成
			column = new DataColumn();
			column.DataType = Type.GetType("System.String");
			column.ColumnName = "件名";
			table.Columns.Add(column);

			// 列情報 の作成
			column = new DataColumn();
			column.DataType = Type.GetType("System.String");
			column.ColumnName = "受信日時";
			table.Columns.Add(column);

			// 列情報 の作成
			column = new DataColumn();
			column.DataType = Type.GetType("System.String");
			column.ColumnName = "本文";
			table.Columns.Add(column);

			// 非同期でメールデータを表示するループ
			foreach (var data in mailData)
			{

				idx++;
				if (idx > maxCount)
				{

					break;
				}

				// 個別にイベント登録
				data.BodyLoaded += new EventHandler(MailData_BodyLoaded);
				// 非同期処理の受信を一件づつ開始
				data.ReadBodyAnsync();

			}

		}

		private void MailData_BodyLoaded(object sender, EventArgs e) {


			IMailData MailData = (IMailData)sender;

			// 本文無し( 本文が必要な場合は、false で、reader.MainText )
			MailReader reader = new MailReader(MailData.DataStream, false);

			if (reader.FileCount == 0)
			{
				Console.WriteLine("添付ファイルはありません");
			}

			//添付ファイルのコレクションを検査します
			foreach (TKMP.Reader.File file in reader.FileCollection)
			{
				// 保存場所は事前に作成する必要があります
				// (メールとの関連は、アプリケーション側で工夫する必要があります)
				file.FileSave(@"c:\temp\data\");
			}
			//Console.WriteLine(reader.MainText);

			// UI スレッドへの処理( この場合、post_state は null )
			sc.Post((object post_state) =>
			{


				// 新規行
				row = table.NewRow();

				// 本文をメモリ内保存( 本来はファイルかデータベースに書きだす )
				row["本文"] = reader.MainText;

				// ヘッダの一覧より、目的のヘッダを探す
				foreach (TKMP.Reader.Header.HeaderString headerdata in reader.HeaderCollection)
				{

					if (headerdata.Name == "From")
					{
						row["差出人"] = headerdata.Data;
					}
					if (headerdata.Name == "Subject")
					{
						row["件名"] = headerdata.Data;
					}
					if (headerdata.Name == "Date")
					{
						Console.WriteLine(headerdata.Data);

						string target = headerdata.Data;
						target = target.Replace(" (JST)", "");
						target = target.Replace(" (PDT)", "");
						try
						{
							DateTime dt = System.DateTime.ParseExact(target, "ddd, d MMM yyyy HH':'mm':'ss zzz", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None);
							row["受信日時"] = string.Format("{0}", dt);
						}
						catch (Exception ex)
						{
							row["受信日時"] = headerdata.Data;
							Console.WriteLine("フォーマット変換できませんでした");
						}

					}

				}

				// 行を追加
				table.Rows.Add(row);


				// 接続解除用
				endCounter++;
				if (endCounter == maxCount)
				{

					endCounter = 0;

					// 受信終了
					client.Close();

					// データーソース経由で DataGridView を表示
					dataGridView1.DataSource = table;

					this.dataGridView1.Columns[3].Visible = false;

					// 第3カラム(受信日)で逆ソート
					dataGridView1.Sort(dataGridView1.Columns[2], ListSortDirection.Descending);
					// 自動整列
					dataGridView1.AutoResizeColumns();

				}


			}, null);

			// イベント削除
			MailData.BodyLoaded -= new EventHandler(MailData_BodyLoaded);


		}

		private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
		{
			int rowNumber = e.RowIndex;
			if (rowNumber < 0 ){
				return;
			}

			this.textBox1.Text = this.dataGridView1["本文",rowNumber].Value.ToString();
		}
	}
}


関連する記事

C# と VB.net : TKMP.DLL を使って IMAP でメール本文の一覧を取得する( コンソール )

C# : TKMP.DLLを使った、メール送信テンプレート



posted by lightbox at 2018-09-04 21:46 | VS(C#) | このブログの読者になる | 更新情報をチェックする

C# : TKMP.DLLを使った、メール送信テンプレート

C:\Users\ユーザ\Documents\Visual Studio 20XX\Templates\ProjectTemplates\Visual C# に保存して下さい


( TKMP.DLL 同梱 )

※ TKMP.DLL は 32ビット用です。





テンプレートが古いので、Microsoft の SMTP サーバーは pop-mail.outlook.com に変更して下さい

久しぶりにメールの処理の検証をしてみようと、最新(周辺)の Microsoft の Framework をチェックしてみたのですが、相変わらず標準的なものはなさそうなので、最新の TKMP.DLL で検証してみました。TKMP.DLL は運用はした事がありませんが、メール送信に非同期処理が無い事を除いて、たいていは間に合いそうな気がします。

しかし、いかに簡単に使えるように設計されていても、メール処理はそれなりに煩雑なので、単に『メール送信』に特化してクラスを作成しました。

メールは Hotmail でなくても使えますが、送信用のクラスを作成する為のテストに Hotmail を使って行い、Hotmail の特性に従ってエラー処理もしています。

Hotmail を使用する場合は、ひとつ注意が必要で、エラーになる原因としてメールサービスがスパム対策として確認用の画像を読ませてログインさせようとする場合があるようです。
MailClass.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TKMP.Writer;
using TKMP.Net;

namespace TKMP_SendMail_Sakura1 {
	class MailClass {
		private MailWriter mw = null;

		public string SmtpServer { get; set; }
		public int Port { get; set; }
		public string User { get; set; }
		public string Pass { get; set; }
		public AuthenticationProtocols Protocol { get; set; }

		private string err_message = "";

		public class MailClassErrorArg {
			public string Message { get; set; }
		}

		public delegate void MailClassError(MailClassErrorArg e);

		public bool SendMail(string To, string From, string Subject, string Body, string To_J, string From_J, MailClassError mce) {
			bool bResult = true;

			mw = new MailWriter();

			try {
				mw.ToAddressList.Add(To);
			}
			catch (Exception ex) {
				bResult = false;
			}
			if (!bResult) {
				if (mce != null) {
					MailClassErrorArg e = new MailClassErrorArg() { Message = "宛先が正しくありません" };
					mce(e);
				}
				return bResult;
			}

			// From が未指定や正しくない文字列の場合
			try {
				mw.FromAddress = From;
			}
			catch (Exception ex) {
				// ユーザが正しければ、以下のように設定しても『ユーザ名 <メールアドレス>』に変換される
				mw.FromAddress = "______@hotmail.co.jp";
			}

			TextPart tp = new TextPart(Body);
			mw.MainPart = tp;

			if (To_J != null) {
				mw.Headers.Add("To", To_J + " <" + To + ">");
			}
			else {
				mw.Headers.Add("To", To);
			}
			if (From_J != null) {
				mw.Headers.Add("From", From_J + " <" + From + ">");
			}
			else {
				// Hotmail では、自動的に 『ユーザ名 <メールアドレス>』に変換される
				mw.Headers.Add("From", From);
			}

			mw.Headers.Add("Subject", Subject);
			mw.Headers.Add("X-Mailer", "TKMP Version 3.1.2");

			var logon = new TKMP.Net.AuthLogin(User, Pass);
			SmtpClient sc = new SmtpClient(SmtpServer, Port, logon);
			sc.AuthenticationProtocol = Protocol;

			try {
				if (!sc.Connect()) {
					err_message = "接続に失敗しました";
					bResult = false;
				}
				else {
					sc.SendMail(mw);
					sc.Close();
				}
			}
			catch (Exception ex) {
				err_message = ex.Message;
				bResult = false;
			}
			if (!bResult) {
				if (mce != null) {
					MailClassErrorArg e = new MailClassErrorArg() { Message = err_message };
					// このメソッドの引数である、ErrorHandler デリゲートを呼び出す
					mce(e);
				}
			}

			return bResult;
		}

		public bool SendMail(string To, string From, string Subject, string Body, string To_J, string From_J) {
			return SendMail(To, From, Subject, Body, To_J, From_J, null);
		}

		public bool SendMail(string To, string From, string Subject, string Body) {
			return SendMail(To, From, Subject, Body, null, null, null);
		}

	}

}

エラー処理は、引数にデリゲートを渡す仕様にしているので、Java の 引数へのインターフェイス渡しのような感じで『ラムダ式』を使ってその場で記述する事を想定しています。

これによって、細かいエラー情報が必要な場合は記述し、そうでない場合はオーバーロードされた引数の少ないメソッドを使って成功が失敗かだけをチェックすればいいようになっています。
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Mail;
using System.Net;
using TKMP.Writer;
using TKMP.Net;

namespace TKMP_SendMail3
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private void button1_Click(object sender, EventArgs e)
		{

			MailClass mc = new MailClass()
			{
				SmtpServer = "smtp.live.com",
				Port = 587,
				User = "ユーザ名@hotmail.co.jp",
				Pass = "パスワード",
				Protocol = AuthenticationProtocols.TLS
			};

			var result = mc.SendMail(
				"宛先メールアドレス",
				"ユーザ名@hotmail.co.jp",
				this.subject.Text,
				this.body.Text,
				null,   // 必要な場合、宛先を日本語で
				null,   // 必要な場合、差出人を日本語で
				(MailClass.MailClassErrorArg _e) =>
				{
					this.error.Text = _e.Message;
				}
			);

			if (result)
			{
				MessageBox.Show("メールを送信しました");
			}

		}

	}
}

Hotmail の仕様に関しては、正式には 『Outlook.com の POP、IMAP、および SMTP の設定』というページにあります

IMAP サーバー名: imap-mail.outlook.com

IMAP ポート: 993

IMAP 暗号化方法: TLS

POP サーバー名: pop-mail.outlook.com

POP ポート: 995

POP 暗号化方法: TLS

SMTP サーバー名: smtp-mail.outlook.com

SMTP ポート: 587

SMTP 暗号化方法: STARTTLS
一般的にどこのメールサービスでも、TLS または SSL と書かれていますが、TLS で 587、SSL で 465 です。( このへんはかなり仕様がわかり難いです / Microsoft は、TLS のみ。Google は、両方。他は SSL のみだったり ) メッセージのソースの様子 ( ソースは GMail で確認するのが一番良さそうです )
From: =?iso-2022-jp?B?GyRCOjk9UD9NGyhCQg==?= <______@hotmail.co.jp>
To: =?iso-2022-jp?B?GyRCMDhAaBsoQkI=?= <______@gmail.com>
Date: Tue, 23 Jul 2013 20:10:15 +0900
MIME-Version: 1.0
Subject: =?iso-2022-jp?B?GyRCN29MPhsoQg==?=
X-Mailer: TKMP Version 3.1.2
Content-Type: text/plain; charset="iso-2022-jp"
Content-Transfer-Encoding: 7bit

$BK\J8$N(B
$BFbMF(B

Gmail 用テンプレート

コードは同じですが、smtp サーバーが、『smtp.gmail.com(TLS/587)』になります。ユーザは、Hotmail ではメールアドレスでしたが、Gmail では、ユーザ名部分だけで認証します(メールアドレスでもOKなはずです)。

安全性の低いアプリの許可を『有効』にします

Nifty 用テンプレート

同様に、コードは同じです。smtp サーバーは、『smtp.nifty.com(SSL/465)』になります。ユーザは、NiftyID を使用したメールアドレスを使いますが、From では通常の別名を使用します。

Yahoo 用テンプレート

同様です。smtp サーバーは、『smtp.mail.yahoo.co.jp(SSL/465)』になります。アカウントは、「@yahoo.co.jpより前の部分」となります。

関連する記事

(C#) / VS2010 または VS2012 : TKMP.DLL(3.1.2 または 3.1.8)を使った、『さくらインターネット』用メール送信テンプレート

さくらインターネット用



C# : DataGridView に TKMP.DLL の IMAP(POP3) で受信したメールを非同期に表示する( 添付ファイルも取得 )



posted by lightbox at 2018-09-04 21:21 | VS(C#) | このブログの読者になる | 更新情報をチェックする

2018年06月20日


C# と VB.net : TKMP.DLL を使って IMAP でメール本文の一覧を取得する( コンソール )

※ 重要
Gmail では安全性の低いアプリの許可を『有効』にする必要があります

.NET用メール送受信クラスライブラリ (TKMP.DLL) 
※ 2011-10-01 時点で 3.0.1
※ 2018-06-20 時点で 3.1.8( 2017/02/15 作成 )

TKMP は、VB.net や C# から日本語環境を気にしないでメールの送受信が行える貴重なライブラリですが、IMAP に対応しておられるのに添付されているドキュメントや、WEB 上のサンプルコードにその記述がありませんでしたので作成しました。

※ IMAP は 2011/08/11 に対応されています。
オンラインドキュメント

C#
using System;
using TKMP.Net;
using TKMP.Reader;

namespace MailRecTestCs
{
    class Program
    {
        static void Main(string[] args)
        {
            // IMAP 用基本認証
            BasicImapLogon logon = new BasicImapLogon("アカウント", "パスワード");
            // IMAP 用ログイン( 993 は、SSL 用 )
            ImapClient client = new ImapClient(logon, "サーバードメイン", 993);

            // SSL で接続する
            client.AuthenticationProtocol = AuthenticationProtocols.SSL;

            // 接続
            client.Connect();

            // メールデータ一覧の取得
            IMailData[] md_i = client.GetMailList();

            // メールデータの数
            Console.WriteLine(md_i.Length);

            // メールデータの本文を取得
            MailReader reader = null/* TODO Change to default(_) if this is not a reference type */;
            System.IO.Stream Body_data = null;

            // 全て表示
            for (int i = 0; i <= md_i.Length - 1; i++)
            {

                // メッセージを読み込む( 同期処理 )
                md_i[i].ReadBody();

                // 読み出しの為にストリームを取得
                Body_data = md_i[i].DataStream;

                // メールリーダで本文を解析
                reader = new TKMP.Reader.MailReader(Body_data, false);

                // マルチパートの時は最初に見つかったテキストセクションの本文
                Console.WriteLine(reader.MainText);

                // ヘッダ情報の取得
                foreach (TKMP.Reader.Header.HeaderString headerdata in reader.HeaderCollection)
                {
                    if (headerdata.Name == "From")
                        Console.WriteLine(string.Format("From : {0}", headerdata.Data));

                    if (headerdata.Name == "Subject")
                        Console.WriteLine(string.Format("Subject : {0}", headerdata.Data));

                    if (headerdata.Name == "Date")
                    {
                        Console.WriteLine(string.Format("Date オリジナル : {0}", headerdata.Data));
                        string target = headerdata.Data;
                        // 日付データの最後に(おそらく)改行が含まれていたので (JST) + 1 で6バイト除去しています
                        target = target.Substring(0, target.Length - 6);
                        try
                        {
                            DateTime dt = System.DateTime.ParseExact(target, "ddd, d MMM yyyy HH':'mm':'ss zzz", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None);
                            Console.WriteLine(string.Format("Date : {0}", dt));
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("フォーマット変換できませんでした");
                        }
                    }
                }
            }

            // 接続解除
            client.Close();

            Console.ReadLine();
        }
    }
}

VB.net
Imports TKMP.Net
Imports TKMP.Reader

Module Module1

    Sub Main()

        ' IMAP 用基本認証
        Dim logon As BasicImapLogon = New BasicImapLogon("アカウント", "パスワード")
        ' IMAP 用ログイン( 993 は、SSL 用 )
        Dim client As ImapClient = New ImapClient(logon, "サーバードメイン", 993)

        ' SSL で接続する
        client.AuthenticationProtocol = AuthenticationProtocols.SSL

        ' 接続
        client.Connect()

        ' メールデータ一覧の取得
        Dim md_i As IMailData() = client.GetMailList()

        ' メールデータの数
        Console.WriteLine(md_i.Length)

        ' メールデータの本文を取得
        Dim reader As MailReader = Nothing
        Dim Body_data As System.IO.Stream = Nothing

        ' 全て表示
        For i As Integer = 0 To md_i.Length - 1

            ' メッセージを読み込む( 同期処理 )
            md_i(i).ReadBody()

            ' 読み出しの為にストリームを取得
            Body_data = md_i(i).DataStream

            ' メールリーダで本文を解析
            reader = New TKMP.Reader.MailReader(Body_data, False)

            ' マルチパートの時は最初に見つかったテキストセクションの本文
            Console.WriteLine(reader.MainText)

            ' ヘッダ情報の取得
            For Each headerdata As TKMP.Reader.Header.HeaderString In reader.HeaderCollection

                If headerdata.Name = "From" Then
                    Console.WriteLine(String.Format("From : {0}", headerdata.Data))
                End If

                If headerdata.Name = "Subject" Then
                    Console.WriteLine(String.Format("Subject : {0}", headerdata.Data))
                End If

                If headerdata.Name = "Date" Then
                    Console.WriteLine(String.Format("Date オリジナル : {0}", headerdata.Data))
                    Dim target As String = headerdata.Data
                    ' 日付データの最後に(おそらく)改行が含まれていたので (JST) + 1 で6バイト除去しています
                    target = target.Substring(0, target.Length - 6)
                    Try
                        Dim dt As DateTime = System.DateTime.ParseExact(target, "ddd, d MMM yyyy HH':'mm':'ss zzz", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None)
                        Console.WriteLine(String.Format("Date : {0}", dt))
                    Catch ex As Exception
                        Console.WriteLine("フォーマット変換できませんでした")
                    End Try
                End If
            Next

        Next

        ' 接続解除
        client.Close()

        Console.ReadLine()

    End Sub


End Module

関連する Microsfot のドキュメント

POP3 アクセス用および IMAP4 アクセス用の TLS と SSL の構成: Exchange 2010 SP1 のヘルプ




posted by lightbox at 2018-06-20 11:05 | VS(C#) | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり