SQLの窓

2020年09月21日


nuget.exe CLI を使用してパッケージをダウンロードし、C# のソースコードで利用して PowerShell でビルドする

 ps.bat ( PowerShell をそのまま使えない場合は以下のバッチファイルを作成して使用します )
@powershell -NoProfile -ExecutionPolicy Unrestricted "./%1.ps1"
nuget.exe - recommended latest のダウンロードページ nuget.exe CLI を使用してパッケージを管理する( Microsoft のドキュメント )
nuget.exe を最新にするには
nuget update -Self

ここで使用するパッケージの最新バージョンの確認は こちらから 行って以下のコマンドライン
nuget install Newtonsoft.Json -Version バージョン
json_sample_01.cs
using System;
using System.IO;
using System.Text;
using System.Net;
using System.Web;
using System.Windows.Forms;
using Newtonsoft.Json;

public class Program
{
	public static void Main()
	{
		ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;	

		string url = "https://lightbox.sakura.ne.jp/demo/json/syain_json.php";
		
		string json = "";

		using( WebClient wc = new WebClient() ) {
			wc.Encoding = Encoding.UTF8;
			json = wc.DownloadString( url );
		}

		Syain[] syain = JsonConvert.DeserializeObject<Syain[]>(json);

		foreach (Syain data in syain) {
			Console.WriteLine( data.社員コード );
			Console.WriteLine( data.氏名 );
			Console.WriteLine( data.フリガナ );
		}

		MessageBox.Show("処理が終了しました");

	}

	private class Syain
	{
		public string 社員コード  { get; set; }
		public string 氏名  { get; set; }
		public string フリガナ  { get; set; }
	}

}


build.ps1

Newtonsoft.Json.dll は、ソースコードと同じ場所に置きます
Add-Type -path "json_sample_01.cs" `
	-ReferencedAssemblies "Newtonsoft.Json.dll", System.Web, System.Windows.Forms `
	-OutputAssembly json_sample_01.exe `
	-OutputType ConsoleApplication

Read-Host "何かキーを押してください"


run.ps1

Newtonsoft.Json.dll は、ソースコードと同じ場所に置きます
Add-Type -path "Newtonsoft.Json.dll"
Add-Type -path "json_sample_01.cs" `
	-ReferencedAssemblies "Newtonsoft.Json.dll", System.Web, System.Windows.Forms

[Program]::Main()

Read-Host "何かキーを押してください"




posted by lightbox at 2020-09-21 18:32 | PowerShell + C# | このブログの読者になる | 更新情報をチェックする

2020年08月30日


PowerShell を使用して、C# のコンソールアプリ用のソースコードから exe を作成する( WebClient で wget.exe ) / ビルドせずに PowerShell で実行

▼ ps.bat ( PowerShell をそのまま使えない場合は以下のバッチファイルを作成して使用します )
@powershell -NoProfile -ExecutionPolicy Unrestricted "./%1.ps1"
▼ build.ps1
Add-Type -path "wget.cs" `
	-ReferencedAssemblies System.Web, System.Windows.Forms `
	-OutputAssembly my_wget.exe `
	-OutputType ConsoleApplication

Read-Host "何かキーを押してください"


wget.cs は、wget.ps1 と同じフォルダにあります
 ` で継続行指定です。
System.Web, System.Windows.Forms という感じで複数の指定を行っています

▼ wget.cs

第一引数に渡した URL をダウンロードします。
Environment.GetCommandLineArgs() の結果には、自分自身が含まれています。
よって、param[1] と args[0] が同じ内容になります。( 後述の PowerShell から実行した場合は、param には PowerShell への引数がセットされます )
Visual Studio で追加参照が必要なクラスが -ReferencedAssemblies の対象です
using System;
using System.IO;
using System.Net;
using System.Web;
using System.Windows.Forms;

public class Program
{
	public static void Main(string[] args)
	{
		ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;	

		string[] param = Environment.GetCommandLineArgs();
		if (param.Length > 1)
		{
			Console.WriteLine( string.Format("第一引数 : {0}", param[1]) );
			Console.WriteLine(string.Format("第一引数 : {0}", args[0]));
		}
		else
		{
			MessageBox.Show("ダウンロードする URL を引数に指定して下さい");
			Environment.Exit(0);
		}

		string localFileName = Path.GetFileName(param[1]);
		Console.WriteLine(string.Format("ファイル名 : {0}", localFileName));

		using( WebClient wc = new WebClient() ) {
			wc.DownloadFile( param[1], localFileName );
		}

		// *******************************************
		// -ReferencedAssemblies の複数テスト用
		// *******************************************
		string percent_encoding = HttpUtility.UrlEncode(param[1]);
		Console.WriteLine( percent_encoding );

		MessageBox.Show("処理が終了しました");

	}
}


MessageBox.Show
HttpUtility.UrlEncode

▼ 実行用バッチファイルのサンプル
my_wget.exe https://winofsql.jp/image/planet.jpg
▼ 実行結果の表示
C:\user\ps\cs>my_wget.exe http://winofsql.jp/image/planet.jpg
第一引数 : http://winofsql.jp/image/planet.jpg
第一引数 : http://winofsql.jp/image/planet.jpg
ファイル名 : planet.jpg
http%3a%2f%2fwinofsql.jp%2fimage%2fplanet.jpg
この後、メッセージボックスが表示されます PowerShell として exe なしで実行する場合 ▼ run.ps1
Param($url)
Write-Host $url

Add-Type -path "wget.cs" `
	-ReferencedAssemblies System.Web, System.Windows.Forms

[Program]::Main($url)

Read-Host "何かキーを押してください"

実行
powershell -NoProfile -ExecutionPolicy Unrestricted run.ps https://winofsql.jp/image/planet.jpg

スクリプトセットのダウンロード




関連するドキュメント


Read-Host コマンドレットの使用
Add-Type




posted by lightbox at 2020-08-30 14:33 | PowerShell + C# | このブログの読者になる | 更新情報をチェックする

2020年08月14日


PowerShell で VisualStudio で作成した Form アプリケーションをビルドする( DataGridView に select 文の結果を表示する / MySQL )

▼ ps.bat ( PowerShell をそのまま使えない場合は以下のバッチファイルを作成して使用します )
@powershell -NoProfile -ExecutionPolicy Unrestricted "./%1.ps1"
build.ps1 exe 作成用 PowerShell スクリプト ✅ Program.cs アプリケーション開始 ✅ Form1.cs Form コントロール ✅ Form1.Designer.cs 画面定義 ▼ build.ps1
Add-Type -path "Program.cs", "Form1.cs", "Form1.Designer.cs" `
	-ReferencedAssemblies System.Windows.Forms, System.Drawing, System.Data, System.Xml `
	-OutputAssembly form-02.exe `
	-OutputType WindowsApplication

Read-Host "何かキーを押してください"


ソース内の固定の select 文 を使用して、MySQL にあるデータを DataGridView に表示するテンプレートです


▼ Program.cs
using System;
using System.Windows.Forms;

namespace form_02
{
	static class Program
	{
		[STAThread]
		static void Main()
		{
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.Run(new Form1());
		}
	}
}


▼ Form1.cs
using System;
using System.Data;
using System.Data.Odbc;
using System.Windows.Forms;

namespace form_02
{
	public partial class Form1 : Form
	{

		// *****************************
		// SQL文字列格納用
		// *****************************
		private string query = "select * from 社員マスタ";

		// *****************************
		// 接続文字列作成用
		// *****************************
		private OdbcConnectionStringBuilder builder = new OdbcConnectionStringBuilder();

		public Form1()
		{
			InitializeComponent();

			setBuilderData();
		}

		// *****************************
		// 接続文字列の準備
		// *****************************
		private void setBuilderData()
		{
			// ドライバ文字列をセット ( 波型括弧{} は必要ありません ) 
			// builder.Driver = "MySQL ODBC 8.0 Unicode Driver";
			builder.Driver = "MySQL ODBC 5.3 Unicode Driver";

			// 接続用のパラメータを追加
			builder.Add("server", "localhost");
			builder.Add("database", "lightbox");
			builder.Add("uid", "root");
			builder.Add("pwd", "");
		}

		// *****************************
		// SELECT 文よりデータ表示
		// *****************************
		private void loadMySQL()
		{

			// 接続と実行用のクラス
			using (OdbcConnection connection = new OdbcConnection())
			using (OdbcCommand command = new OdbcCommand())
			{
				// 接続文字列
				connection.ConnectionString = builder.ConnectionString;

				try
				{
					// 接続文字列を使用して接続
					connection.Open();
				}
				catch (Exception ex)
				{
					MessageBox.Show(ex.Message);
					return;
				}

				// コマンドオブジェクトに接続をセット
				command.Connection = connection;
				// コマンドを通常 SQL用に変更
				command.CommandType = CommandType.Text;

				// *****************************
				// 実行 SQL
				// *****************************
				command.CommandText = query;

				try
				{
					// レコードセット取得
					using (OdbcDataReader reader = command.ExecuteReader())
					{
						// データを格納するテーブルクラス
						DataTable dataTable = new DataTable();

						// DataReader よりデータを格納
						dataTable.Load(reader);

						// 画面の一覧表示用コントロールにセット
						dataGridView1.DataSource = dataTable;

						// リーダを使い終わったので閉じる
						reader.Close();
					}

				}
				catch (Exception ex)
				{
					// 接続解除
					connection.Close();
					MessageBox.Show(ex.Message);
					return;
				}

				// 接続解除
				connection.Close();
			}

			// カラム幅の自動調整
			dataGridView1.AutoResizeColumns();
		}


		private void button1_Click(object sender, EventArgs e)
		{
			loadMySQL();
		}
	}
}


▼ Form1.Designer.cs
namespace form_02
{
	partial class Form1
	{
		private System.ComponentModel.IContainer components = null;

		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		private void InitializeComponent()
		{
			this.dataGridView1 = new System.Windows.Forms.DataGridView();
			this.button1 = new System.Windows.Forms.Button();
			((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
			this.SuspendLayout();
			// 
			// dataGridView1
			// 
			this.dataGridView1.AllowUserToAddRows = false;
			this.dataGridView1.AllowUserToDeleteRows = false;
			this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
			this.dataGridView1.Location = new System.Drawing.Point(18, 70);
			this.dataGridView1.Name = "dataGridView1";
			this.dataGridView1.ReadOnly = true;
			this.dataGridView1.RowTemplate.Height = 21;
			this.dataGridView1.Size = new System.Drawing.Size(760, 351);
			this.dataGridView1.TabIndex = 0;
			// 
			// button1
			// 
			this.button1.Location = new System.Drawing.Point(18, 21);
			this.button1.Name = "button1";
			this.button1.Size = new System.Drawing.Size(190, 31);
			this.button1.TabIndex = 1;
			this.button1.Text = "実行";
			this.button1.UseVisualStyleBackColor = true;
			this.button1.Click += new System.EventHandler(this.button1_Click);
			// 
			// Form1
			// 
			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
			this.ClientSize = new System.Drawing.Size(800, 450);
			this.Controls.Add(this.button1);
			this.Controls.Add(this.dataGridView1);
			this.Name = "Form1";
			this.Text = "SELECT 実行結果の表示";
			((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
			this.ResumeLayout(false);

		}

		private System.Windows.Forms.DataGridView dataGridView1;
		private System.Windows.Forms.Button button1;
	}
}






posted by lightbox at 2020-08-14 22:39 | PowerShell + C# | このブログの読者になる | 更新情報をチェックする

2020年08月11日


PowerShell で VisualStudio で作成した Form アプリケーションをビルドする( Form アプリケーションを テキストエディタのみで作成するテンプレート )



▼ ps.bat ( PowerShell をそのまま使えない場合は以下のバッチファイルを作成して使用します )
@powershell -NoProfile -ExecutionPolicy Unrestricted "./%1.ps1"
build.ps1 exe 作成用 PowerShell スクリプト ✅ Program.cs アプリケーション開始 ✅ Form1.cs Form コントロール ✅ Form1.Designer.cs 画面定義 ▼ build.ps1
Add-Type -path "Program.cs", "Form1.cs", "Form1.Designer.cs" `
	-ReferencedAssemblies System.Windows.Forms, System.Drawing `
	-OutputAssembly form-01.exe `
	-OutputType WindowsApplication

Read-Host "何かキーを押してください"


プログラムは、入力した内容をボタンをクリックした後 MessageBox で表示するだけのものです。つまりは、Form アプリケーションを テキストエディタのみで作成するテンプレートでもあります。

▼ Program.cs
using System;
using System.Windows.Forms;

namespace form_01
{
	static class Program
	{
		[STAThread]
		static void Main()
		{
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.Run(new Form1());
		}
	}
}


▼ Form1.cs
using System;
using System.Windows.Forms;

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

		private void button1_Click(object sender, EventArgs e)
		{
			MessageBox.Show(textBox1.Text);
		}
	}
}


▼ Form1.Designer.cs
namespace form_01
{
	partial class Form1
	{
		private System.ComponentModel.IContainer components = null;

		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		private void InitializeComponent()
		{
			this.textBox1 = new System.Windows.Forms.TextBox();
			this.button1 = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// textBox1
			// 
			this.textBox1.Location = new System.Drawing.Point(65, 48);
			this.textBox1.Name = "textBox1";
			this.textBox1.Size = new System.Drawing.Size(243, 19);
			this.textBox1.TabIndex = 0;
			// 
			// button1
			// 
			this.button1.Location = new System.Drawing.Point(64, 94);
			this.button1.Name = "button1";
			this.button1.Size = new System.Drawing.Size(243, 28);
			this.button1.TabIndex = 1;
			this.button1.Text = "実行";
			this.button1.UseVisualStyleBackColor = true;
			this.button1.Click += new System.EventHandler(this.button1_Click);
			// 
			// Form1
			// 
			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
			this.ClientSize = new System.Drawing.Size(534, 290);
			this.Controls.Add(this.button1);
			this.Controls.Add(this.textBox1);
			this.Name = "Form1";
			this.Text = "タイトル";
			this.ResumeLayout(false);
			this.PerformLayout();

		}

		private System.Windows.Forms.TextBox textBox1;
		private System.Windows.Forms.Button button1;
	}
}






posted by lightbox at 2020-08-11 18:56 | PowerShell + C# | このブログの読者になる | 更新情報をチェックする

2018年10月21日


TKMP + imap + C# + PowerShell : メールボックス(階層)の一覧表示

.NET用メール送受信クラスライブラリ (TKMP.DLL)

TKMP : ImapClient Members

TKMP : Mailbox Members

▼ バッチファイル
powershell -NoProfile -ExecutionPolicy Unrestricted .\imap_sub_mailbox.ps1 ユーザ パスワード
imap_sub_mailbox.ps1
$user = $Args[0]
$pass = $Args[1]

$code = @"
using System;
using TKMP.Net;
using TKMP.Reader;
public class MyClass {
	public static void mail_Subbox() {

		ImapClient client = null;

		// 接続準備
		BasicImapLogon logon = new BasicImapLogon("${user}@さくらユーザ.sakura.ne.jp", "${pass}");
		client = new ImapClient(logon, "さくらユーザ.sakura.ne.jp", 993);
		client.AuthenticationProtocol = AuthenticationProtocols.SSL;

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

		// 階層のセパレータ文字列の取得
		Console.WriteLine("Separator : " + client.Separator);

		// デフォルトメールボックスの表示
		Console.WriteLine("FullName : " + client.DefaultMailbox.FullName);

		// Mailbox 一覧 : 実質 INBOX
		Mailbox[] mailbox_list = client.GetMailBox();

		foreach (Mailbox mb in mailbox_list) {

			Console.WriteLine( "ルート : " + mb.FullName);

			// 全て表示
			show_Subbox( mb );

		}

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

	}

	// ***************************************
	// メールボックス(階層)の一覧表示
	// ***************************************
	public static void show_Subbox( Mailbox mb ) {

		Console.WriteLine( "mailbox : " + mb.FullName );

		if ( (mb.Flags & MailBoxFlags.HasChildren) == MailBoxFlags.HasChildren ) {

			Mailbox[] mailbox_list = mb.SubMailbox;
			foreach (Mailbox mailbox in mailbox_list) {

				show_Subbox( mailbox );

			}
		}

	}
}
"@

$path = "C:\user\ps\TKMPDLL_3.1.8"
Add-Type -Path ("${path}\TKMP.dll")
Add-Type -Language CSharp -TypeDefinition $code -ReferencedAssemblies ("${path}\TKMP.dll")

[MyClass]::mail_Subbox()


実行結果
Separator : .
FullName : INBOX
ルート : INBOX
mailbox : INBOX
mailbox : INBOX.個人用
mailbox : INBOX.AAA
mailbox : INBOX.spam
mailbox : INBOX.Sent
mailbox : INBOX.TEST
mailbox : INBOX.Trash
mailbox : INBOX.Draft
mailbox : INBOX.Drafts
mailbox : INBOX.最上位のフォルダ
mailbox : INBOX.メールアイテム格納用
mailbox : INBOX.メールアイテム格納用.階層テスト
posted by lightbox at 2018-10-21 11:06 | PowerShell + C# | このブログの読者になる | 更新情報をチェックする

nuget.exe + SMO + PowerShell + C# : テーブルの CREATE TABLE 文 を取得

nuget.exe Windows x86 Commandline のダウンロードページ

ダウンロードする SMO のバージョン確認

▼ SMO のダウンロード
nuget install Microsoft.SqlServer.SqlManagementObjects -Version 140.17283.0
▼ PowerShell 実行用のバッチファイル
powershell -NoProfile -ExecutionPolicy Unrestricted .\create_sql.ps1 sa ""
create_sql.ps1
# バッチファイルからの引数
$user = $Args[0]
$pass = $Args[1]

$code = @"
using System;
using System.Text;
using System.IO;
using System.Collections.Generic;
using System.Collections.Specialized;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

public class MyClass {

	public static void create_sql() {

		// BOM なしのテキスト用
		// https://docs.microsoft.com/ja-jp/dotnet/api/system.text.utf8encoding.-ctor?view=netframework-4.7.2
		UTF8Encoding UTF8N_Enc = new UTF8Encoding(false);

		// false は上書き
		// https://docs.microsoft.com/ja-jp/dotnet/api/system.io.streamwriter.-ctor?view=netframework-4.7.2
		StreamWriter WriteFile = new StreamWriter("sqlexpress.sql", false, UTF8N_Enc);

		// サーバー : Microsoft.SqlServer
		Server srv;
		// インスタンス
		srv = new Server();

		// サーバーインスタンスの情報
		srv.ConnectionContext.AutoDisconnectMode = AutoDisconnectMode.NoAutoDisconnect;
		srv.ConnectionContext.LoginSecure = false;

		// PowerShell の文字列の埋め込み
		srv.ConnectionContext.Login = "${user}";
		srv.ConnectionContext.Password = "${pass}";

		// 接続
		srv.ConnectionContext.Connect();

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

		// データベース選択
		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();

	}
}
"@

# 共通のパスを作成
$path = "C:\user\nuget\Microsoft.SqlServer.SqlManagementObjects.140.17283.0\lib\net40"

# 配列定義
$dll = @(1..5)

# 配列にライブラリファイル名を代入
$dll[0] = "Microsoft.SqlServer.ConnectionInfo.dll"
$dll[1] = "Microsoft.SqlServer.Smo.dll"
$dll[2] = "Microsoft.SqlServer.Management.Sdk.Sfc.dll"
$dll[3] = "Microsoft.SqlServer.SmoExtended.dll"
$dll[4] = "Microsoft.SqlServer.SqlEnum.dll"

# PowerShell の単純変数の埋め込みと配列変数の埋め込み
Add-Type -Path `
	( `
	"${path}\$($dll[0])", `
	"${path}\$($dll[1])", `
	"${path}\$($dll[2])", `
	"${path}\$($dll[3])", `
	"${path}\$($dll[4])" `
	)

Add-Type -Language CSharp -TypeDefinition $code -ReferencedAssemblies `
	( `
	"${path}\$($dll[0])", `
	"${path}\$($dll[1])", `
	"${path}\$($dll[2])", `
	"${path}\$($dll[3])", `
	"${path}\$($dll[4])" `
	)

# 実行
[MyClass]::create_sql()

関連する記事

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

C# : VB.net : SQLExpress(SQLServer) : SQL-DMO と同等の SMO によるバックアップ
上のリンク先のコードを使用すれば、PowerShell で SQLServer のバックアップが出来ます
posted by lightbox at 2018-10-21 10:12 | PowerShell + 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 終わり