/// <summary>
        /// シェル情報一括読み込み
        /// </summary>
        protected virtual void LoadShells()
        {
            var appDirPath = Util.GetAppDirPath();

            // 既存情報クリア
            lstShell.Clear();
            imgListFace.Images.Clear();

            // FMO情報更新
            UpdateFMOInfo();

            // コマンドライン引数を解釈
            var args = Environment.GetCommandLineArgs().ToList();

            args.RemoveAt(0);                              // 先頭はexe名のため削除

            var caller = args.First();                     // 呼び出し元ゴースト (id or "unspecified")

            args.RemoveAt(0);                              // 先頭削除
            var ghostDirPath = args.First().TrimEnd('\\'); // ゴーストフォルダパス

            SakuraFMOData target  = null;
            var           matched = Regex.Match(caller, @"^id:(.+?)\z");

            if (matched.Success)
            {
                // idが指定された場合、そのidと一致するゴースト情報を探す
                var id = matched.Groups[1].Value.Trim();
                target = FMOGhostList.FirstOrDefault(g => g.Id == id);
            }

            if (caller == "unspecified")
            {
                // "unspecified" が指定された場合、現在起動しているゴーストのうち、ランダムに1体を呼び出し元とする (デバッグ用)
                var rand = new Random();
                target = FMOGhostList[rand.Next(0, FMOGhostList.Count - 1)];
            }

            if (target == null)
            {
                throw new Exception(string.Format("指定されたゴーストが見つかりませんでした。 ({0})", caller));
            }

            // 呼び出し元の情報をプロパティにセット
            CallerId         = target.Id;
            CallerSakuraName = target.Name;
            CallerKeroName   = target.KeroName;
            CallerHWnd       = (IntPtr)target.HWnd;

            // チェックボックスの位置移動
            ChkCloseAfterChange.Left = BtnChange.Left + 1;

            // ゴースト情報読み込み
            var ghost = ExplorerGhost.Load(ghostDirPath);

            // シェル情報読み込み
            ShellManager = ShellManager.Load(ghostDirPath, ghost.SakuraDefaultSurfaceId, ghost.KeroDefaultSurfaceId);

            // シェルが1件も取得できなかった場合はエラー表示
            if (!ShellManager.ListItems.Any())
            {
                MessageBox.Show("有効なシェルフォルダが1件も見つかりませんでした。"
                                , "エラー"
                                , MessageBoxButtons.OK
                                , MessageBoxIcon.Error);
                Application.Exit();
                return;
            }

            // 最終起動時の記録があり、かつ最終起動時とバージョンが異なる場合は、キャッシュをすべて破棄
            if (CurrentProfile.LastBootVersion != null && Util.GetVersion() != CurrentProfile.LastBootVersion)
            {
                Directory.Delete(Util.GetCacheBaseDirPath(), recursive: true);
            }

            // 最終起動情報をセットして、Profileを保存
            CurrentProfile.LastBootVersion = Util.GetVersion();
            Util.SaveProfile(CurrentProfile);

            // ゴーストの顔画像を変換・取得
            var faceImages = ShellManager.GetFaceImages(imgListFace.ImageSize);

            // リストビュー構築処理
            foreach (var shellItem in ShellManager.ListItems)
            {
                // 顔画像を正常に読み込めていれば、イメージリストに追加
                if (faceImages.ContainsKey(shellItem.DirPath))
                {
                    imgListFace.Images.Add(shellItem.DirPath, faceImages[shellItem.DirPath]);
                }

                // リスト項目追加
                var item = lstShell.Items.Add(key: shellItem.DirPath, text: shellItem.Name ?? "", imageKey: shellItem.DirPath);
                item.Tag = shellItem.DirPath;
            }

            // 読み込み中表示を消す
            lblLoading.Hide();

            // ゴースト情報リストの更新に伴う画面表示更新
            UpdateUIOnFMOChanged();

            // ボタン等表示状態を更新
            UpdateUIState();

            // 現在の使用シェルを選択
            var currentShellFolderName = Path.GetFileName(ghost.CurrentShellRelDirPath);

            foreach (ListViewItem item in lstShell.Items)
            {
                var shellFolderName = Path.GetFileName((string)item.Tag);
                if (shellFolderName == currentShellFolderName)
                {
                    item.Focused  = true;
                    item.Selected = true;
                    break;
                }
            }

            // 選択対象のシェルを判別できなかった場合は、1件目を選択
            if (SelectedShellListItem == null)
            {
                lstShell.Items[0].Focused  = true;
                lstShell.Items[0].Selected = true;
            }

            // スクロール
            var selectedIndex = lstShell.SelectedItems[0].Index;

            lstShell.EnsureVisible(selectedIndex);
        }
        /// <summary>
        /// ゴースト情報の一括読み込み (シェルはまだ読み込まない)
        /// </summary>
        public virtual void Load()
        {
            // 既存の値はクリア
            Ghosts.Clear();

            // ゴーストフォルダのサブフォルダを列挙
            foreach (var subDir in Directory.GetDirectories(GhostDirPath))
            {
                // 隠しフォルダの場合はスキップ
                var dirInfo = new DirectoryInfo(subDir);
                if ((dirInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
                {
                    continue;
                }

                // ゴーストフォルダでなければスキップ
                if (!ExplorerGhost.IsGhostDir(subDir))
                {
                    continue;
                }

                // ゴーストの基本情報を読み込み
                var ghost = ExplorerGhost.Load(subDir);

                // キーワードが指定されており、かつキーワードに合致しなければスキップ
                if (!string.IsNullOrWhiteSpace(FilterWord))
                {
                    if (!(ghost.Name.Contains(FilterWord) ||
                          ghost.SakuraName.Contains(FilterWord) ||
                          ghost.KeroName.Contains(FilterWord)))
                    {
                        continue;
                    }
                }

                // リストに追加
                Ghosts.Add(ghost);
            }

            // ゴースト別の使用頻度情報を抽出
            var totalBootTimes  = new Dictionary <string, long>();
            var lastBootSeconds = new Dictionary <string, long>();

            foreach (var ghost in Ghosts)
            {
                Realize2Text.Record rec = null;
                if (Realize2Text != null)
                {
                    rec = Realize2Text.GhostRecords.FirstOrDefault(r => r.Name == ghost.Name);
                }

                if (rec != null)
                {
                    totalBootTimes[ghost.Name]  = rec.TotalBootByMinute;
                    lastBootSeconds[ghost.Name] = rec.LastBootSecond;
                }
                else
                {
                    // 情報が見つからなければ0扱い
                    totalBootTimes[ghost.Name]  = 0;
                    lastBootSeconds[ghost.Name] = 0;
                }
            }

            // ゴースト別のインストール日付情報を取得 (インストール日時順でソートされた場合のみ)
            var installSeconds = new Dictionary <string, long>();

            if (SortType == Const.SortType.ByRecentInstall)
            {
                foreach (var ghost in Ghosts)
                {
                    // 初期値は0
                    installSeconds[ghost.DirPath] = 0;

                    // profile\var.txt が存在すれば、その中からインストール時刻を取得
                    var varPath = Path.Combine(ghost.DirPath, @"ghost\master\profile\var.txt");
                    if (File.Exists(varPath))
                    {
                        try
                        {
                            var lines = File.ReadAllLines(varPath);
                            foreach (var line in lines)
                            {
                                if (line.StartsWith("install,"))
                                {
                                    var tokens = line.TrimEnd().Split(',');
                                    installSeconds[ghost.DirPath] = long.Parse(tokens[1]);
                                    break;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex);
                        }
                    }
                }
            }

            // 最後にソート
            switch (SortType)
            {
            case Const.SortType.ByBootTime:
                Ghosts = Ghosts.OrderByDescending(g => totalBootTimes[g.Name]).ToList();
                break;

            case Const.SortType.ByRecentBoot:
                Ghosts = Ghosts.OrderByDescending(g => lastBootSeconds[g.Name]).ToList();
                break;

            case Const.SortType.ByRecentInstall:
                Ghosts = Ghosts.OrderByDescending(g => installSeconds[g.DirPath]).ToList();
                break;

            default:
                // ゴースト名+フォルダパス順
                Ghosts = Ghosts.OrderBy(g => Tuple.Create(g.Name, g.DirPath)).ToList();
                break;
            }
        }