これまでに、パスワード管理ツールの自作について8回に分けてソースコードを解説しました。
今回は、バージョンアップということで、パスワード管理ツールに乱数を使ったパスワード生成機能を追加したいと思います。
今回は最後にサンプル動画を用意しましたので、細かい部分はそちらも参考にして頂ければと思います。
機能追加の概要
パスワード管理ツールの追加する機能は次の通りです。
- 編集ボタンをクリックすることで、指定した長さのランダムなパスワードを生成する
- 英大文字、小文字、数字、記号を含むランダムなパスワードを生成する。
編集ボタンをクリックすることで、下記のレイアウトになります。

別のウィンドウを表示して、パスワードを構成する文字の種類が指定できるなどの機能も考えましたが、分かりやすさを優先して、今回はシンプルな仕様にしました。
パスワード生成機能の有効/無効の制御は、編集モードと参照モードの切り替えと考え方は同じで、DataGridViewのReadOnlyプロパティの値を見て有効/無効を設定します。
あと、パスワード生成のクラスを使うため、System.Webの参照設定を行っている点がポイントです。
それでは、ソースコードの説明を始めたいと思います。
ソースコード
まず、全体のソースコードを掲載しておきます。
尚、今回のVisual Studio 2019 プロジェクトファイルは、下記からダウンロード可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.IO; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Web.Security; namespace PasswordManager { public partial class MainForm : Form { /// <summary> /// IPパスワードファイル名 /// </summary> private string _idPassFile = "IdPass.xml"; /// <summary> /// 画面に配置するコントロールの登録と初期化 /// </summary> public MainForm() { InitializeComponent(); //プログラムを画面の真ん中に表示 this.StartPosition = FormStartPosition.CenterScreen; //パスワード文字列長の初期値をセット uxPasswordLength.Text = "8"; //パスワード文字列長を右寄せに設定 uxPasswordLength.TextAlign = HorizontalAlignment.Right; //パスワード生成パネルの表示位置を編集ボタンに合わせる uxGenaratePasswordPanel.Location = uxPwdToClip.Location; //パスワード生成パネルの非表示 uxGenaratePasswordPanel.Visible = false; } /// <summary> /// 画面表示時の初期化処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void MainForm_Load(object sender, EventArgs e) { //ウィンドウのサイズを固定にする this.FormBorderStyle = FormBorderStyle.FixedSingle; //IDパスワード一覧に表示するデータの作成 DataTable dt = new DataTable(); dt.Columns.Add("カテゴリ"); dt.Columns.Add("アプリ/サイト名"); dt.Columns.Add("ID"); dt.Columns.Add("パスワード"); //テーブル名を設定 dt.TableName = "IdPass"; //既に保存済みの/IDパスワードファイルがあれば、読み込む if (File.Exists(_idPassFile)) { dt.ReadXml(_idPassFile); } ///IDパスワード一覧に表示 uxIdPasswordGrid.DataSource = dt; //IDパスワード一覧の初期設定 uxIdPasswordGrid.SelectionMode = DataGridViewSelectionMode.CellSelect; //行選択モードの設定 uxIdPasswordGrid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; //列を左右一杯に広げる uxIdPasswordGrid.AlternatingRowsDefaultCellStyle.BackColor = Color.Linen; //1行置きの背景色を設定 uxIdPasswordGrid.ReadOnly = true; // 書き込み禁止の設定 uxIdPasswordGrid.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText; //コピーした値にヘッダを含まない uxIdPasswordGrid.AllowUserToAddRows = false; //ユーザーの行追加を禁止にする } /// <summary> /// 保存ボタンクリック処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UxSave_Click(object sender, EventArgs e) { //IDパスワード一覧をファイルに保存 DataTable dt = (DataTable)uxIdPasswordGrid.DataSource; dt.WriteXml(_idPassFile); //メッセージの表示 MessageBox.Show("保存しました"); } /// <summary> /// ID取得ボタンクリック処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UxIdToClip_Click(object sender, EventArgs e) { //カレント行に登録されているIDを取得 var row = uxIdPasswordGrid.CurrentRow; var id = row.Cells["ID"].Value.ToString(); if (id != "") { //クリップボードへコピー Clipboard.SetText(id); } } /// <summary> /// パスワード取得ボタンクリック処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UxPwdToClip_Click(object sender, EventArgs e) { //カレント行に登録されているパスワードを取得 var row = uxIdPasswordGrid.CurrentRow; var pwd = row.Cells["パスワード"].Value.ToString(); if (pwd != "") { //クリップボードへコピー Clipboard.SetText(pwd); } } /// <summary> /// 編集ボタンクリック処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UxEditMode_Click(object sender, EventArgs e) { //ReadOnlyプロパティの状態を反転 uxIdPasswordGrid.ReadOnly = !uxIdPasswordGrid.ReadOnly; //IDパスワード一覧の書き込み禁止モードを有効にする if(uxIdPasswordGrid.ReadOnly == true) { //パスワード生成パネルの非表示 uxGenaratePasswordPanel.Visible = false; //編集ボタンのテキストと背景色を設定(最初の状態に戻す) uxEditMode.Text = "編集"; uxEditMode.BackColor = SystemColors.Control; //ユーザーによる行追加を禁止にする uxIdPasswordGrid.AllowUserToAddRows = false; //カテゴリドロップダウンリストを作り直す RefreshDropDownList(); } else { //パスワード生成パネルの表示 uxGenaratePasswordPanel.Visible = true; //編集ボタンのテキストと背景色を編集中に設定 uxEditMode.Text = "編集中"; uxEditMode.BackColor = Color.Yellow; //ユーザーによる行追加を有効にする uxIdPasswordGrid.AllowUserToAddRows = true; } } /// <summary> /// カテゴリドロップダウンリスト表示時の処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UxCategory_DropDown(object sender, EventArgs e) { //カテゴリドロップダウンリストの中身が0なら、作り直す if (uxCategory.Items.Count == 0) { RefreshDropDownList(); } } /// <summary> /// カテゴリドロップダウンの再作成 /// </summary> private void RefreshDropDownList() { uxCategory.Items.Clear(); uxCategory.Items.Add(""); for (int i = 0; i < uxIdPasswordGrid.Rows.Count; i++) { var category = uxIdPasswordGrid["カテゴリ", i].Value.ToString(); if (! uxCategory.Items.Contains(category)) { uxCategory.Items.Add(category); } } } /// <summary> /// カテゴリドロップダウンリスト選択時の処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UxCategory_SelectedIndexChanged(object sender, EventArgs e) { var dt = (DataTable)uxIdPasswordGrid.DataSource; if (uxCategory.Text == "") { dt.DefaultView.RowFilter = ""; } else { dt.DefaultView.RowFilter = "カテゴリ = '" + uxCategory.Text + "'"; } } /// <summary> /// キー入力の処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void UxIdPasswordGrid_KeyDown(object sender, KeyEventArgs e) { //書き込み禁止か? if (uxIdPasswordGrid.ReadOnly) { return; } //最下段(空白行)か? if (uxIdPasswordGrid.CurrentRow.IsNewRow == true) { return; } //DELキーが押されたか? if (e.KeyCode == Keys.Delete) { //カレント行を削除 uxIdPasswordGrid.Rows.Remove(uxIdPasswordGrid.CurrentRow); } } private void UxGenaratePassword_Click(object sender, EventArgs e) { int val; var len = (int.TryParse(uxPasswordLength.Text, out val) == true) ? val : 8; if (len <= 128) { var password = Membership.GeneratePassword(len, 0); uxIdPasswordGrid["パスワード", uxIdPasswordGrid.CurrentRow.Index].Value = password; } } } } |
参照設定
最初は乱数を使ってパスワードを生成しようと思っていたのですが、パスワード生成が簡単に出来るクラスがあったので、これを使うことにしました。
その為、参照設定という作業が必要になります。
- Visual Studio 2019 の右端にある「ソリューションエクスプローラー」から「参照」を右クリックします。
- 表示されるメニューから「参照の追加」をクリックすると、「参照マネージャ」のウィンドウが表示されます。
- 左端の「アセンブリ」をクリックすると表示される名前一覧から「System.Web」を探し、クリックします。
- 左端のチェックボックスにチェックが入ったことを確認し、最後にOKボタンをクリックしてください。
以上で、参照設定が完了します。

参照設定が正しく行われたか否かは、「参照」の▹マークをクリックし、表示される一覧の中に「System.Web」が表示されているか否かで確認できます。

参考動画の2分あたりに参照設定の操作がありますので、必要に応じてご覧ください。
追加内容
では、具体的な追加内容について解説していきます。
usingの指定
まず冒頭にある一連のusing の最後に、下記の1行を追加しています。
1 |
using System.Web.Security; |
これで、後述するパスワード生成クラスが利用できるようになります。
デザイン変更
パスワード生成を行うために、以下のコントロールを張り付けています。
用途 | コントロール名 |
---|---|
パスワード文字数の入力欄 | uxPasswordLength |
パスワード生成ボタン | uxGenaratePassword |
上記2つのコントロールの 表示/非表示切り替え用 | uxGenaratePasswordPanel |
パスワード文字数の入力欄とパスワード生成ボタンは、直接MainFormに張り付けても良かったのですが、Panelコントロールを張り付けてから、その上にこれら2つのコントロールを張り付ける方法を採用しました。
パスワード文字数の入力欄とパスワード生成ボタンは、編集モードの時に表示され、それ以外は非表示にするという制御を行います。
Panelに対して表示/非表示設定を行うと、Panelの上に張り付けられたコントロールはそれに連動して表示/非表示となるため、複数個のコントロールを一括制御する場合はPanelを使う方が楽なのです。

画面レイアウトが終わったら、パスワード生成ボタン「乱数」をダブルクリックして、イベントハンドラを作っておいてください。
今回追加するイベントハンドラは、これだけです。
パスワード生成ボタンについて
パスワード生成ボタンの表記が「乱数」となっていますが、これはちょっとしたミスです。
すみません。
もともと、乱数を使ってパスワードを生成するつもりだったのですが、パスワード生成に便利なクラスがあったので、これを使う事にしました。
その際、ボタンの表記を「生成」にすべきだったのですが、修正するのを忘れて動画を作成したので、このままにしています。
ボタンの表記、レイアウトを含めて好きなように変更して頂ければと思います。
MainForm コンストラクタへの追加
コンストラクタには、次の4行を追加しました。
1 2 3 4 5 6 7 8 9 10 11 |
//パスワード文字列長の初期値をセット uxPasswordLength.Text = "8"; //パスワード文字列長を右寄せに設定 uxPasswordLength.TextAlign = HorizontalAlignment.Right; //パスワード生成パネルの表示位置を編集ボタンに合わせる uxGenaratePasswordPanel.Location = uxPwdToClip.Location; //パスワード生成パネルの非表示 uxGenaratePasswordPanel.Visible = false; |
uxPasswordLength.Text は、単純にパスワード文字列長の初期値に8を設定しています。
Textプロパティは string 型なので、数字の8ではなく、文字列の8を代入するため、”8″ としています。
また、TextAlignプロパティにHorizontalAlignment.Right を設定しているのは、右寄せしたかったからです。
次に、uxGenaratePasswordPanelの位置が入っているLocationプロパティに対して、「パスワード取得」ボタンの位置を 代入しています。
1 |
uxGenaratePasswordPanel.Location = uxPwdToClip.Location; |
こうすることによって、Panelコントロールをレイアウトエディタのどこに置いても、実行時には「パスワード取得」ボタンの位置に移動するようになります。

もちろん、レイアウトエディタ上で、最初から「パスワード取得ボタン」の上にPanelコントロールを置いてもOKです。
ただ、将来プログラム変更をする際に「パスワード取得ボタン」が隠れていると変に悩んでしまうかもしれません。
そこで、あえて位置をずらしてレイアウトし、実行時に位置合わせするようにしました。
追加した4行目は、パスワード生成パネル(uxGenaratePasswordPanel)を非表示にするコードです。
全てのコントロールには、 visibleというプロパティがあって、ここに false を代入すると非表示になります。
1 |
uxGenaratePasswordPanel.Visible = false; |
これが無いと、「パスワード取得ボタン」が隠れてしまい、押せなくなってしまいます。
尚、これらの初期値設定は、コードを書かずにVisual Studio のプロパティウィンドウからの設定も可能です。

編集ボタンクリック時のイベントハンドラへの追加
UxEditMode_Clickイベントハンドラは、DataGridViewのReadOnlyプロパティの値に応じて、画面のレイアウト(編集ボタンの表記、背景色の設定)を変更するコードを書いています。
今回、ReadOnlyの状態に応じて、パスワード生成パネルの表示/非表示を切り替えるコードを書きました。
uxIdPasswordGrid.ReadOnly が true の時は、uxGenaratePasswordPanelを非表示にしたいので、visibleプロパティにfalse を代入しています。
1 |
uxGenaratePasswordPanel.Visible = false; |
uxIdPasswordGrid.ReadOnly が false の時は、uxGenaratePasswordPanelを表示したいので、visibleプロパティに true を代入しています。
1 |
uxGenaratePasswordPanel.Visible = true; |
このイベントハンドラへの変更は、この2行の追加だけとなります。
パスワード生成ボタンのイベントハンドラ追加
パスワード生成ボタンは今回追加したものなので、クリックされた時のイベントハンドラも新しく追加しています。
やりたい事は、画面から入力されたパスワード長を読み込んで、その文字数だけパスワードを生成するという内容です。
1 2 3 4 5 6 7 8 9 10 11 12 |
private void UxGenaratePassword_Click(object sender, EventArgs e) { int val; var len = (int.TryParse(uxPasswordLength.Text, out val) == true) ? val : 8; if (len <= 128) { var password = Membership.GeneratePassword(len, 0); uxIdPasswordGrid["パスワード", uxIdPasswordGrid.CurrentRow.Index].Value = password; } } |
ここで新しいことが2点登場します。
1つは画面から入力された文字を数値として取得する方法と、パスワードを生成する方法です。
パスワードの文字列長は uxPasswordLength のTextプロパティに入っていますが、これはあくまで文字列、つまりstring 型です。
パスワード生成クラスは MembershipクラスのGeneratePasswordメソッドを使う事で取得できますが、このメソッドには数値、つまり int 型の文字列長を指定しなければなりません。
そこで、文字列から数値への変換が必要になります。
文字列を数値に変換するには
単に変換するだけなら、それぞれの型にはParseメソッドが搭載されているので、これを使えば済みます。
例えば、文字列としての数値を int型の数値にに変換するなら、 int.Parse()を、実数型の数値に変換するなら、double.Parse()を使うことで完了します。
1 2 3 4 5 |
//文字列を整数型の数値に変換 int.Parse(uxPasswordLength.Text) //文字列を実数型の数値に変換 double.Parse(uxPasswordLength.Text) |
C#に用意されている全ての型は、実はクラスなので、様々なメソッドが用意されており、Tryメソッドもその中の1つです。
ただし、Tryメソッドには問題もあります。
それは、数値以外の文字が混ざっている場合、例えば “12ab” なんかだとエラーになるのです。
その対応方法として、変換できるか否かを最初にチェックし、変換できるなら値を返すという TryParse メソッドが用意されています。
そこで、今回はこれを使う事にしました。
1 2 |
int val; var len = (int.TryParse(uxPasswordLength.Text, out val) == true) ? val : 8; |
メソッドの戻り値は1つしか返せませんので、TryParseでは変換に成功したか否かを示す true/false が返されます。
変換した結果は、 第2引数に指定した val 変数に返されます。
ここで、 out val という記述をしていますが、これはC#のルールです。
通常、引数として渡された値は、メソッドの中で変更しても、呼び出し側には反映されません。
しかし、out を付けることで、メソッドの中で変更した結果を、引数に反映することが可能です。
勿論、何でもかんでもそうなるのではなく、out を付加された引数を受け取れるよう、メソッド側で特別な記述をしなければなりません。
TryPhaseメソッドは、それに対応しているメソッドなのです。
3項演算子とは
ここでもう1つ説明すべきことがあります。
それは、3項演算子です。
(条件) ? 式1 : 式2
という風な書き方をすると、条件が true の場合は 式1を、false の場合は 式2を実行して、結果を返してくれます。
1 |
var len = (int.TryParse(uxPasswordLength.Text, out val) == true) ? val : 8; |
と書いていますが、これはTryParseの結果が true なら、TryParseの第2引数に返された val の値を len に代入し、false なら 8を代入するという意味になります。
通常の if 文を使うのであれば、
1 2 3 4 5 6 7 8 9 |
int val; if(int.TryParse(uxPasswordLength.Text,out val) == true) { len = val; } else { len = 8; } |
の様に記述することになりますが、3項演算子だと1行で書けてしまうので、C#では良く使われます。
ちなみに、TryPhaseを使うには、あらかじめ結果を受け取る変数を定義する必要がありますが、TryPahseの中で受け取る変数を定義してしまう方法もあります。
1 2 3 4 5 6 |
//①変数を定義して、tryParseに渡す方法 int val; int.TryParse(uxPasswordLength.Text, out val) //②TryParseの中で一緒に変数を定義してしまう方法 int.TryParse(uxPasswordLength.Text, out int val) |
②の方法だと、事前記述した int val を無くすことが可能です。
パスワード生成
パスワードを生成する部分は以下のクラスとメソッドを使います。
System.Web.Security.Membership.GeneratePassword(引数1、引数2)
ソースコードの冒頭に
1 |
using System.Web.Security; |
を記述しましたが、これをすることで System.Web.Securityを省略できるようになるので、
Membership.GeneratePassword(引数1、引数2)
という記述で済みます。
1か所しか記述しないのであれば using は不要かもしれませんが、複数個所記述するのであれば、 using をしておく方が楽になります。
さて、この GeneratePassword メソッドには、第1引数として「生成するパスワードの長さ」を、第2引数として「最低限、区切り記号を含ませる個数」を指定します。
1 |
Membership.GeneratePassword(len,0); |
今回は 第1引数に画面から入力された値、第2引数に0を指定しています。
第2引数に0を指定すると、「パスワードに区切り記号が全く含まれない可能性がある」という意味だけなので、区切り記号が含まれることも十分あり得ます。
3を指定すると、「パスワードに区切り記号が最低3個含まれる」という意味になります。
1 2 3 4 5 6 |
if (len <= 128) { var password = Membership.GeneratePassword(len, 0); uxIdPasswordGrid["パスワード", uxIdPasswordGrid.CurrentRow.Index].Value = password; } |
パスワード生成をする前に if文で len が 123以下であることを確認していますが、これはMembership.GeneratePasswordの第1引数が128を超えるとエラーになるためです。
そして、最後に生成したパスワードを DataGridView のカレント行の「パスワード」列に代入しています。
DataGridViewへの代入の仕方が、今までと少し変わっていますが、実は複数の代入方法があります。
どちらの方法を使っても同じなのですが、今回は新しい方法を紹介しました。
1 2 3 4 5 |
//カレント行の指定列への代入方法1(今までの記述方法) uxIdPasswordGrid.Rows[0].Cells["パスワード"].Value = password; //カレント行の指定列への代入方法2(今回の記述方法) uxIdPasswordGrid.CurrentRow.Index].Value = password; |
レイアウト変更と参照設定の動画
最後に、レイアウト変更と参照設定についての参考動画を掲載しておきます。
参照設定の方法は2分後あたりから登場します。
まとめ
如何でしたでしょうか。
前回作ったパスワード生成ツールに、パスワード生成機能を追加してみました。
このように、使いながら必要な機能を追加していけるのが、自分で作ることのメリットであり、DIYの醍醐味です。
最初は試行錯誤するかもしれませんが、是非自分で作ったプログラムをバージョンアップしていってください。