0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CrystalDiskInfoってなんだ?〜ストレージの健康診断ツールを完全理解〜

0
Posted at

この記事の対象読者

  • HDD/SSDの健康状態を確認したいが、方法がわからない方
  • CrystalDiskInfoをインストールしたものの、見方がわからない方
  • S.M.A.R.T.情報の経時変化をログで追いたいエンジニア・システム管理者
  • PCの故障を事前に予測して、データ損失を防ぎたい方

この記事で得られること

  • CrystalDiskInfoの正体: 何ができるツールなのか、技術的背景を理解
  • 画面の見方: 健康状態、温度、S.M.A.R.T.属性の読み解き方
  • ログの確認方法: Smartフォルダの構造、グラフ機能、イベントログの活用
  • 実践的な監視設定: 常駐、アラート、メール通知の設定方法
  • トラブルシューティング: よくある問題と対処法

この記事で扱わないこと

  • S.M.A.R.T.の内部仕様(ATA/NVMe規格の詳細)
  • データ復旧の方法
  • 他のディスク診断ツール(smartmontools等)との比較

1. CrystalDiskInfoとの出会い

「HDDが壊れて、大事なデータが全部消えた...」

エンジニアなら一度は聞いたことがある、あるいは自ら経験したことがあるかもしれない悲劇だ。私も例外ではない。数年前、前触れもなく外付けHDDが認識しなくなり、数年分の写真が消失した。

「なんで事前に気づけなかったんだろう?」

その答えが CrystalDiskInfo だった。

CrystalDiskInfoは、例えるなら 「ストレージの健康診断ツール」 だ。人間ドックで血液検査や心電図を見るように、HDD/SSDの内部状態を数値化して「正常」「注意」「異常」と教えてくれる。しかも無料で、日本製で、日本語対応だ。

実は、すべてのHDD/SSDには S.M.A.R.T.(Self-Monitoring, Analysis and Reporting Technology) という自己診断機能が搭載されている。ストレージは自分の健康状態を常に記録しているのだ。CrystalDiskInfoは、この「カルテ」を人間が読める形で表示してくれる。

ここまでで、CrystalDiskInfoがどんなものか、なんとなくイメージできただろうか。次は、この記事で使う用語を整理しておこう。


2. 前提知識の確認

本題に入る前に、この記事で登場する用語を確認する。

2.1 S.M.A.R.T.とは

S.M.A.R.T.(スマート)は、HDD/SSDに搭載された自己診断機能だ。ストレージが自ら以下のような情報を記録する。

記録される情報 説明
読み取りエラー率 データ読み取り時のエラー発生頻度
代替処理済セクタ数 不良セクタを予備領域で代替した回数
電源投入回数 起動された回数
使用時間 累計稼働時間(通電時間)
温度 現在の動作温度

2.2 属性値の読み方

S.M.A.R.T.情報には、各項目に対して以下の値が存在する。

値の種類 説明
現在値(Current) 今の状態を示す正規化された値(通常100が最良、低いほど悪い)
最悪値(Worst) 過去に記録された最も悪い値
しきい値(Threshold) メーカーが定めた「故障」と判断する境界値
生の値(Raw Value) 実際の測定データ(16進数で表示されることが多い)

重要: 現在値がしきい値を下回ると「異常」と判定される。

2.3 健康状態の4段階

CrystalDiskInfoは、S.M.A.R.T.情報を総合して以下の4段階で判定する。

状態 意味
正常 青/緑 問題なし
注意 劣化の兆候あり(早めのバックアップ推奨)
異常 深刻な問題あり(即時バックアップ・交換推奨)
不明 S.M.A.R.T.情報を取得できない

2.4 Pre-fail属性とOld-age属性

S.M.A.R.T.属性には2種類のタイプがある。

タイプ 説明
Pre-fail(故障予兆) しきい値を下回ると故障が近いことを示す重要な属性
Old-age(経年劣化) 使用に伴う自然な劣化を示す属性

これらの用語が押さえられたら、CrystalDiskInfoの背景を見ていこう。


3. CrystalDiskInfoが生まれた背景

3.1 開発者と歴史

CrystalDiskInfoは、日本の開発者 hiyohiyo氏 によって開発されたオープンソースソフトウェアだ。MITライセンス で公開されており、誰でも無料で利用できる。

最初のバージョンは2008年頃にリリースされ、以来継続的にアップデートが行われている。2025年現在、バージョン9.7.xがリリースされており、NVMe SSDやUSB接続ドライブ、Intel/AMD RAIDにも対応している。

3.2 なぜCrystalDiskInfoが必要か

S.M.A.R.T.情報は、Windowsの標準機能では確認しづらい。コマンドプロンプトで wmic diskdrive get status を実行しても、「OK」か「Pred Fail」しか表示されない。詳細な属性値を見るには、専用ツールが必要だ。

CrystalDiskInfoの強みは以下の通り。

強み 説明
日本語対応 属性名から設定まで完全日本語化
USB対応 一部の外付けHDD/SSDでもS.M.A.R.T.を取得可能
NVMe対応 最新のNVMe SSDにも対応(Windows 10以降)
グラフ機能 S.M.A.R.T.値の経時変化を可視化
常駐監視 バックグラウンドで監視し、異常時にアラート
ポータブル版 インストール不要で持ち運び可能

3.3 公式サイトと信頼性

公式サイトは Crystal Dew World だ。同作者の CrystalDiskMark(ディスクベンチマークツール)も有名で、IT業界では定番のツールとなっている。

背景がわかったところで、基本的な仕組みを見ていこう。


4. 基本概念と仕組み

4.1 画面構成

CrystalDiskInfoを起動すると、以下のような画面が表示される。

┌─────────────────────────────────────────────────────────────┐
│ [メニューバー]  ファイル  編集  機能  ディスク  ヘルプ        │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────┐  ファームウェア: XXXXX   シリアル: YYYYY        │
│ │ 健康状態 │  インターフェース: NVMe   転送モード: PCIe 4.0  │
│ │  正常   │  対応規格: NVMe 1.4                             │
│ │         │  対応機能: S.M.A.R.T., TRIM, VolatileWriteCache │
│ │  37°C  │  総読込量: XXX GB   総書込量: YYY GB            │
│ └─────────┘  電源投入回数: ZZZ回  使用時間: AAAA時間        │
├─────────────────────────────────────────────────────────────┤
│ [S.M.A.R.T.情報一覧]                                        │
│ ID  属性名              現在値  最悪値  しきい値  生の値     │
│ ─────────────────────────────────────────────────────────── │
│ 01  リードエラーレート    100    100      50     00000000   │
│ 05  代替処理済セクタ数    100    100       5     00000000   │
│ 09  使用時間              099    099       0     00001234   │
│ 0C  電源投入回数          100    100       0     00000567   │
│ C2  温度                  063    055       0     00000025   │
│ ...                                                         │
└─────────────────────────────────────────────────────────────┘

4.2 特に注目すべきS.M.A.R.T.属性(HDD)

HDDで故障を予測する上で重要な属性は以下の5つだ(Backblaze社の研究より)。

ID 属性名 説明 危険サイン
05 代替処理済セクタ数 不良セクタを予備領域で代替した数 生の値が1以上
BB 報告済み訂正不能エラー 訂正できなかったエラーの数 生の値が1以上
BC コマンドタイムアウト タイムアウトしたコマンドの数 生の値が増加傾向
C5 代替処理保留中セクタ数 代替処理を待っている不良セクタ数 生の値が1以上
C6 回復不能セクタ数 回復できなかったセクタ数 生の値が1以上

4.3 特に注目すべきS.M.A.R.T.属性(SSD)

SSDでは、以下の属性が重要だ。

ID 属性名 説明 危険サイン
05 代替処理済セクタ数 不良ブロックを代替した数 急激な増加
B1/AD 残り寿命 SSDの残り寿命(%) 10%以下
F1 総書き込み量(ホスト) 書き込まれた総データ量 TBW上限に近い
E9 メディア消耗指標 NANDの消耗度 しきい値に近い

4.4 NVMe SSDの場合

NVMe SSDは従来のS.M.A.R.T.とは異なる形式で情報を提供する。CrystalDiskInfoでは以下の項目が表示される。

項目 説明
Critical Warning 重大な警告フラグ
Temperature 温度(複数センサー)
Available Spare 予備領域の残り(%)
Percentage Used 消耗度(100%以上になることもある)
Data Units Read/Written 読み書きしたデータ量
Power On Hours 稼働時間
Unsafe Shutdowns 不正なシャットダウン回数

基本概念が理解できたところで、実際にインストールして使ってみよう。


5. 実践:CrystalDiskInfoの使い方

5.1 ダウンロードとインストール

CrystalDiskInfoには複数のエディションがある。

エディション 特徴
Standard Edition 通常版
Shizuku Edition キャラクター「水晶雫」版
Kurei Kei Edition カスタマイズ版
Aoi Edition キャラクター「蒼」版

機能は同じなので、好みで選んで良い。

ダウンロード手順:

  1. 公式サイト https://crystalmark.info/ にアクセス
  2. 「CrystalDiskInfo」をクリック
  3. 「Download」から以下を選択:
    • ZIP版: インストール不要(ポータブル)
    • インストーラー版: レジストリに設定を保存

5.2 環境別の設定ファイル

開発環境用(DiskInfo.ini - 開発者向け)

; DiskInfo.ini - 開発環境用
; 頻繁にチェック、詳細ログを有効化

[Setting]
Language=Japanese
AutoRefresh=1
RefreshInterval=10         ; 10分間隔でS.M.A.R.T.を更新
StartupWaitTime=30         ; 起動時30秒待機
AutoDetection=1            ; 新規ディスク自動検出
EventLog=1                 ; Windowsイベントログに記録
AlertSound=1               ; 警報音有効
ResidentMinimize=0         ; 常駐時は非表示

; 温度しきい値(開発環境は低めに設定)
[Temperature]
HDD=45                     ; HDD警告温度
SSD=55                     ; SSD警告温度

; 健康状態判定(厳格)
[HealthStatus]
05=1                       ; 代替処理済セクタ 1以上で注意
C5=1                       ; 代替処理保留中セクタ 1以上で注意
C6=1                       ; 回復不能セクタ 1以上で注意

本番環境用(DiskInfo.ini - サーバー向け)

; DiskInfo.ini - 本番サーバー向け
; メール通知重視、負荷を抑えた設定

[Setting]
Language=Japanese
AutoRefresh=1
RefreshInterval=60         ; 60分間隔(サーバー負荷軽減)
StartupWaitTime=120        ; 起動時120秒待機(RAIDコントローラ対応)
AutoDetection=0            ; 自動検出無効(予期せぬ負荷防止)
EventLog=1                 ; イベントログ有効
AlertSound=0               ; サーバーなので音無効
AlertMail=1                ; メール通知有効
ResidentMinimize=1         ; 最小化で常駐

; 温度しきい値(データセンター想定)
[Temperature]
HDD=50
SSD=65
NVMe=70

; メール設定
[AlertMail]
Host=smtp.example.com
Port=587
SSL=STARTTLS
User=alert@example.com
To=admin@example.com
Subject=[CrystalDiskInfo] ストレージ異常検知

テスト環境用(DiskInfo.ini - 検証用)

; DiskInfo.ini - テスト環境用
; 最小限の設定、ログ無効

[Setting]
Language=Japanese
AutoRefresh=0              ; 自動更新無効(手動でチェック)
StartupWaitTime=0          ; 待機時間なし
AutoDetection=0            ; 自動検出無効
EventLog=0                 ; イベントログ無効
AlertSound=0               ; 警報音無効

; 温度しきい値(緩め)
[Temperature]
HDD=55
SSD=70

5.3 ログファイルの場所と構造

CrystalDiskInfoのログは、インストールフォルダ(またはZIP展開フォルダ)内の Smart フォルダ に保存される。

CrystalDiskInfo/
├── DiskInfo32.exe
├── DiskInfo64.exe
├── DiskInfo.ini           ← 設定ファイル
├── DiskInfo.txt           ← /CopyExit オプションで出力されるテキスト
└── Smart/                 ← S.M.A.R.T.ログフォルダ
    ├── [ディスク名+シリアル番号]/
    │   └── Smart.ini      ← S.M.A.R.T.履歴データ
    ├── [ディスク名+シリアル番号]/
    │   └── Smart.ini
    └── ...

Smart.iniの構造

各ディスクの Smart.ini には、以下の3つのセクションが含まれる。

; Smart.ini の構造例

[SAMSUNG_SSD_980_PRO_1TB_S5XXXXXXXXFIRST]
; 初回記録時のS.M.A.R.T.値
Date=2024/01/15 10:30:00
01=100,100,0,0000000000000000
05=100,100,10,0000000000000000
09=100,100,0,0000000000001234
; ...

[SAMSUNG_SSD_980_PRO_1TB_S5XXXXXXXX]
; 最新のS.M.A.R.T.値(グラフ用履歴データ)
Date=2025/02/01 09:00:00
01=100,100,0,0000000000000000
05=100,100,10,0000000000000000
09=098,098,0,0000000000005678
; ...
Date=2025/01/31 09:00:00
01=100,100,0,0000000000000000
; ...(過去の記録が続く)

[SAMSUNG_SSD_980_PRO_1TB_S5XXXXXXXXTHRESHOLD]
; しきい値(メーカー定義)
01=50
05=10
09=0
; ...

重要: グラフ機能は、このSmart.iniに蓄積されたデータを可視化している。CrystalDiskInfoが起動していない間はデータが記録されないため、継続的な監視には常駐機能を使う必要がある。

5.4 よくあるエラーと対処法

症状 原因 対処法
外付けHDDが認識しない USB-SATAブリッジがS.M.A.R.T.を通さない 機能→上級者向け機能→ATA_PASS_THROUGH を試す
NVMe SSDが表示されない Windows 7/8ではNVMe非対応 Windows 10以降にアップグレード
健康状態が「不明」 RAIDコントローラ経由 Intel RAID/AMD RAIDXpert2対応を有効化
グラフが表示されない Smart.iniがない CrystalDiskInfoを常駐させてデータを蓄積
温度が異常に高い センサー値の誤読 別の温度監視ツールと比較(±3℃の誤差あり)
「注意」だがPCは正常動作 デフォルト判定が厳格 機能→健康状態設定で基準値を調整

5.5 グラフ機能の使い方

グラフ機能は、S.M.A.R.T.値の経時変化を可視化する強力な機能だ。

表示方法:

  1. メニュー → 機能 → グラフ
  2. 左上でディスクを選択
  3. 表示したい属性をチェック

グラフで確認できる項目:

  • 温度の推移
  • 代替処理済セクタ数の増加傾向
  • 使用時間の累積
  • 総書き込み量の推移(SSD)
#!/usr/bin/env python3
"""
CrystalDiskInfo Smart.iniパーサー
ログファイルを解析してCSVに変換する

使い方: python parse_smart_log.py /path/to/Smart.ini
"""

import re
import csv
import sys
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Optional
from dataclasses import dataclass


@dataclass
class SmartRecord:
    """S.M.A.R.T.レコード"""
    timestamp: datetime
    attributes: Dict[str, tuple]  # {ID: (current, worst, threshold, raw)}


def parse_smart_ini(filepath: Path) -> List[SmartRecord]:
    """
    Smart.iniファイルをパースしてレコードリストを返す
    
    Args:
        filepath: Smart.iniのパス
    
    Returns:
        SmartRecordのリスト
    """
    records = []
    current_record = None
    
    with open(filepath, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            
            # セクションヘッダーをスキップ(FIRSTやTHRESHOLDは除外)
            if line.startswith('[') and line.endswith(']'):
                section = line[1:-1]
                if 'FIRST' in section or 'THRESHOLD' in section:
                    current_record = None
                continue
            
            # 日付行
            if line.startswith('Date='):
                if current_record:
                    records.append(current_record)
                
                date_str = line.split('=')[1]
                try:
                    timestamp = datetime.strptime(date_str, '%Y/%m/%d %H:%M:%S')
                    current_record = SmartRecord(
                        timestamp=timestamp,
                        attributes={}
                    )
                except ValueError:
                    current_record = None
                continue
            
            # 属性行(例: 05=100,100,10,0000000000000000)
            if current_record and '=' in line:
                match = re.match(r'^([0-9A-Fa-f]{2})=(\d+),(\d+),(\d+),([0-9A-Fa-f]+)$', line)
                if match:
                    attr_id = match.group(1).upper()
                    current = int(match.group(2))
                    worst = int(match.group(3))
                    threshold = int(match.group(4))
                    raw = match.group(5)
                    current_record.attributes[attr_id] = (current, worst, threshold, raw)
    
    if current_record:
        records.append(current_record)
    
    return records


def export_to_csv(records: List[SmartRecord], output_path: Path, 
                  attr_ids: Optional[List[str]] = None):
    """
    レコードをCSVに出力
    
    Args:
        records: SmartRecordのリスト
        output_path: 出力CSVパス
        attr_ids: 出力する属性ID(Noneならすべて)
    """
    if not records:
        print("レコードがありません")
        return
    
    # すべての属性IDを収集
    all_attrs = set()
    for record in records:
        all_attrs.update(record.attributes.keys())
    
    if attr_ids:
        all_attrs = all_attrs.intersection(set(attr_ids))
    
    all_attrs = sorted(all_attrs)
    
    # CSVヘッダー
    headers = ['Timestamp']
    for attr in all_attrs:
        headers.extend([f'{attr}_Current', f'{attr}_Raw'])
    
    with open(output_path, 'w', newline='', encoding='utf-8') as f:
        writer = csv.writer(f)
        writer.writerow(headers)
        
        for record in sorted(records, key=lambda x: x.timestamp):
            row = [record.timestamp.isoformat()]
            for attr in all_attrs:
                if attr in record.attributes:
                    current, _, _, raw = record.attributes[attr]
                    row.extend([current, int(raw, 16)])
                else:
                    row.extend(['', ''])
            writer.writerow(row)
    
    print(f"CSVを出力しました: {output_path}")
    print(f"レコード数: {len(records)}")
    print(f"属性数: {len(all_attrs)}")


def main():
    if len(sys.argv) < 2:
        print("使い方: python parse_smart_log.py /path/to/Smart.ini [output.csv]")
        print("\n例:")
        print("  python parse_smart_log.py ./Smart/SAMSUNG_SSD_980_PRO_1TB_XXXXX/Smart.ini")
        sys.exit(1)
    
    input_path = Path(sys.argv[1])
    output_path = Path(sys.argv[2]) if len(sys.argv) > 2 else input_path.with_suffix('.csv')
    
    if not input_path.exists():
        print(f"ファイルが見つかりません: {input_path}")
        sys.exit(1)
    
    print(f"パース中: {input_path}")
    records = parse_smart_ini(input_path)
    
    # 重要な属性のみ出力
    important_attrs = ['05', '09', '0C', 'C2', 'C5', 'C6', 'F1', 'B1']
    export_to_csv(records, output_path, important_attrs)


if __name__ == "__main__":
    main()

5.6 イベントログの確認方法

CrystalDiskInfoは、Windowsイベントログに異常を記録できる。

有効化方法:

  1. メニュー → 機能 → 警報機能 → イベントログ

イベントビューアーでの確認:

  1. Win + Reventvwr.msc
  2. Windows ログ → Application
  3. ソース「CrystalDiskInfo」でフィルタ

記録されるイベントID:

イベントID レベル 説明
601 警告 健康状態の悪化
602 警告 代替処理済セクタ数の増加
603 警告 再配置イベント数の増加
604 警告 代替処理保留中セクタ数の増加
605 警告 回復不能セクタ数の増加
606 警告 温度しきい値超過

5.7 環境診断スクリプト

#!/usr/bin/env python3
"""
CrystalDiskInfo環境診断スクリプト
インストール状態とログを確認する

実行方法: python check_cdi_env.py
"""

import os
import sys
from pathlib import Path
from datetime import datetime


def find_crystaldiskinfo() -> list:
    """CrystalDiskInfoのインストール場所を探す"""
    possible_paths = [
        # インストーラー版
        Path(os.environ.get('PROGRAMFILES', '')) / 'CrystalDiskInfo',
        Path(os.environ.get('PROGRAMFILES(X86)', '')) / 'CrystalDiskInfo',
        # ポータブル版(よくある場所)
        Path.home() / 'Downloads' / 'CrystalDiskInfo',
        Path.home() / 'Desktop' / 'CrystalDiskInfo',
        Path('C:/Tools/CrystalDiskInfo'),
    ]
    
    found = []
    for path in possible_paths:
        if path.exists():
            # DiskInfo*.exe が存在するか確認
            exes = list(path.glob('DiskInfo*.exe'))
            if exes:
                found.append(path)
    
    return found


def analyze_smart_logs(cdi_path: Path) -> dict:
    """Smartフォルダ内のログを分析"""
    smart_path = cdi_path / 'Smart'
    
    result = {
        'smart_folder_exists': smart_path.exists(),
        'drives': [],
        'total_records': 0,
        'oldest_record': None,
        'newest_record': None
    }
    
    if not smart_path.exists():
        return result
    
    for drive_folder in smart_path.iterdir():
        if drive_folder.is_dir():
            smart_ini = drive_folder / 'Smart.ini'
            if smart_ini.exists():
                # ファイルサイズとレコード数を概算
                size = smart_ini.stat().st_size
                with open(smart_ini, 'r', encoding='utf-8', errors='ignore') as f:
                    content = f.read()
                    date_count = content.count('Date=')
                
                result['drives'].append({
                    'name': drive_folder.name,
                    'size_kb': size / 1024,
                    'record_count': date_count
                })
                result['total_records'] += date_count
    
    return result


def check_diskinfo_ini(cdi_path: Path) -> dict:
    """DiskInfo.iniの設定を確認"""
    ini_path = cdi_path / 'DiskInfo.ini'
    
    result = {
        'exists': ini_path.exists(),
        'settings': {}
    }
    
    if not ini_path.exists():
        return result
    
    current_section = None
    with open(ini_path, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f:
            line = line.strip()
            if line.startswith('[') and line.endswith(']'):
                current_section = line[1:-1]
            elif '=' in line and current_section:
                key, value = line.split('=', 1)
                result['settings'][f'{current_section}.{key}'] = value
    
    return result


def main():
    print("=" * 60)
    print("CrystalDiskInfo 環境診断ツール")
    print("=" * 60)
    print()
    
    # CrystalDiskInfoを探す
    print("【インストール場所の検索】")
    found_paths = find_crystaldiskinfo()
    
    if not found_paths:
        print("  CrystalDiskInfoが見つかりませんでした")
        print("  公式サイトからダウンロードしてください:")
        print("  https://crystalmark.info/")
        return
    
    for path in found_paths:
        print(f"  発見: {path}")
    
    print()
    
    # 各インストールを分析
    for cdi_path in found_paths:
        print(f"{cdi_path}")
        
        # バージョン確認(ファイル名から推測)
        exes = list(cdi_path.glob('DiskInfo*.exe'))
        print(f"  実行ファイル: {[e.name for e in exes]}")
        
        # 設定ファイル
        ini_result = check_diskinfo_ini(cdi_path)
        if ini_result['exists']:
            print(f"  設定ファイル: あり")
            if 'Setting.Language' in ini_result['settings']:
                print(f"    言語: {ini_result['settings']['Setting.Language']}")
            if 'Setting.AutoRefresh' in ini_result['settings']:
                auto = "有効" if ini_result['settings']['Setting.AutoRefresh'] == '1' else "無効"
                print(f"    自動更新: {auto}")
            if 'Setting.EventLog' in ini_result['settings']:
                evlog = "有効" if ini_result['settings']['Setting.EventLog'] == '1' else "無効"
                print(f"    イベントログ: {evlog}")
        else:
            print(f"  設定ファイル: なし(デフォルト設定で動作)")
        
        # Smartログ
        smart_result = analyze_smart_logs(cdi_path)
        if smart_result['smart_folder_exists']:
            print(f"  Smartログフォルダ: あり")
            print(f"    検出ドライブ数: {len(smart_result['drives'])}")
            print(f"    総レコード数: {smart_result['total_records']}")
            for drive in smart_result['drives']:
                print(f"      - {drive['name']}: {drive['record_count']}レコード ({drive['size_kb']:.1f}KB)")
        else:
            print(f"  Smartログフォルダ: なし(まだログが蓄積されていません)")
        
        print()
    
    print("=" * 60)
    print("【推奨アクション】")
    
    if found_paths:
        # 設定の推奨
        print("  1. 常駐機能を有効にしてログを蓄積する")
        print("     メニュー → 機能 → 常駐")
        print()
        print("  2. イベントログを有効にしてWindowsに記録する")
        print("     メニュー → 機能 → 警報機能 → イベントログ")
        print()
        print("  3. 定期的にグラフでS.M.A.R.T.値の推移を確認する")
        print("     メニュー → 機能 → グラフ")


if __name__ == "__main__":
    main()

5.8 Docker設定(WSL2環境でのテスト用)

注意: CrystalDiskInfoはWindowsネイティブアプリのため、Docker内では直接動作しない。以下はWSL2からWindowsのCrystalDiskInfoログを分析する場合の設定例。

# Dockerfile - CrystalDiskInfo ログ分析環境
FROM python:3.11-slim

WORKDIR /app

# 必要なパッケージをインストール
RUN pip install pandas matplotlib

COPY parse_smart_log.py .
COPY check_cdi_env.py .

# Windows側のCrystalDiskInfoログをマウントして使用
# docker run -v /mnt/c/Program\ Files/CrystalDiskInfo/Smart:/data/smart analyzer
VOLUME /data/smart

CMD ["python", "parse_smart_log.py"]
# docker-compose.yml
version: '3.8'
services:
  cdi-analyzer:
    build: .
    volumes:
      # WSL2からWindowsのパスをマウント
      - /mnt/c/Program Files/CrystalDiskInfo/Smart:/data/smart:ro
    command: >
      python parse_smart_log.py 
      /data/smart/*/Smart.ini

実装方法がわかったので、次は具体的なユースケースを見ていこう。


6. ユースケース別ガイド

6.1 ユースケース1: 個人PCの定期チェック

想定読者: 自分のPCのストレージ状態を定期的に確認したい方

推奨設定:

  • 常駐: 有効
  • 更新間隔: 30分
  • 警報音: 有効

サンプルコード(週次レポート生成):

#!/usr/bin/env python3
"""
個人PC向け: 週次ストレージ健康レポート生成
CrystalDiskInfoのログからレポートを作成

使い方: python weekly_report.py
"""

import os
from pathlib import Path
from datetime import datetime, timedelta
from typing import Dict, List


def generate_weekly_report(cdi_path: Path) -> str:
    """週次レポートを生成"""
    
    report_lines = []
    report_lines.append("=" * 60)
    report_lines.append(f"ストレージ健康レポート - {datetime.now().strftime('%Y/%m/%d')}")
    report_lines.append("=" * 60)
    report_lines.append("")
    
    smart_path = cdi_path / 'Smart'
    if not smart_path.exists():
        report_lines.append("Smartフォルダが見つかりません")
        return "\n".join(report_lines)
    
    for drive_folder in sorted(smart_path.iterdir()):
        if not drive_folder.is_dir():
            continue
        
        smart_ini = drive_folder / 'Smart.ini'
        if not smart_ini.exists():
            continue
        
        report_lines.append(f"{drive_folder.name}")
        
        # 最新のS.M.A.R.T.データを取得
        latest_data = get_latest_smart_data(smart_ini)
        if latest_data:
            report_lines.append(f"  最終更新: {latest_data.get('date', '不明')}")
            
            # 重要な属性をチェック
            critical_attrs = {
                '05': '代替処理済セクタ数',
                'C5': '代替処理保留中セクタ数',
                'C6': '回復不能セクタ数',
                'C2': '温度',
                '09': '使用時間'
            }
            
            for attr_id, attr_name in critical_attrs.items():
                if attr_id in latest_data.get('attributes', {}):
                    current, raw = latest_data['attributes'][attr_id]
                    status = "正常" if current > 50 else "注意"
                    
                    # 特定の属性は生の値で判定
                    if attr_id in ['05', 'C5', 'C6']:
                        raw_int = int(raw, 16) if raw else 0
                        status = "正常" if raw_int == 0 else "注意"
                        report_lines.append(f"  {attr_name}: {raw_int} ({status})")
                    elif attr_id == 'C2':
                        temp = int(raw, 16) if raw else 0
                        status = "正常" if temp < 50 else "注意"
                        report_lines.append(f"  {attr_name}: {temp}°C ({status})")
                    elif attr_id == '09':
                        hours = int(raw, 16) if raw else 0
                        report_lines.append(f"  {attr_name}: {hours}時間")
            
        report_lines.append("")
    
    report_lines.append("=" * 60)
    report_lines.append("※ 「注意」がある場合は、早めのバックアップを推奨します")
    
    return "\n".join(report_lines)


def get_latest_smart_data(smart_ini: Path) -> Dict:
    """最新のS.M.A.R.T.データを取得"""
    result = {'date': None, 'attributes': {}}
    
    with open(smart_ini, 'r', encoding='utf-8', errors='ignore') as f:
        in_latest_section = False
        for line in f:
            line = line.strip()
            
            # FIRSTやTHRESHOLDセクションはスキップ
            if line.startswith('['):
                in_latest_section = 'FIRST' not in line and 'THRESHOLD' not in line
                continue
            
            if not in_latest_section:
                continue
            
            if line.startswith('Date=') and result['date'] is None:
                result['date'] = line.split('=')[1]
            elif '=' in line and ',' in line:
                parts = line.split('=')
                if len(parts) == 2:
                    attr_id = parts[0]
                    values = parts[1].split(',')
                    if len(values) >= 4:
                        result['attributes'][attr_id] = (int(values[0]), values[3])
    
    return result


def main():
    # CrystalDiskInfoの場所を探す
    possible_paths = [
        Path(os.environ.get('PROGRAMFILES', '')) / 'CrystalDiskInfo',
        Path(os.environ.get('PROGRAMFILES(X86)', '')) / 'CrystalDiskInfo',
        Path.home() / 'Downloads' / 'CrystalDiskInfo',
    ]
    
    cdi_path = None
    for path in possible_paths:
        if (path / 'Smart').exists():
            cdi_path = path
            break
    
    if not cdi_path:
        print("CrystalDiskInfoのSmartフォルダが見つかりません")
        print("CrystalDiskInfoを起動してログを蓄積してください")
        return
    
    report = generate_weekly_report(cdi_path)
    print(report)
    
    # ファイルにも保存
    output_path = Path.home() / 'Desktop' / f'storage_report_{datetime.now().strftime("%Y%m%d")}.txt'
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(report)
    print(f"\nレポートを保存しました: {output_path}")


if __name__ == "__main__":
    main()

6.2 ユースケース2: 複数PCの一括監視

想定読者: 社内PCやサーバーを管理するシステム管理者

推奨構成:

  • 各PCにCrystalDiskInfoをインストール
  • コマンドラインオプションで定期出力
  • 中央サーバーでログを収集

サンプルコード(リモート収集スクリプト):

#!/usr/bin/env python3
"""
システム管理者向け: 複数PCからS.M.A.R.T.情報を収集
CrystalDiskInfoの /CopyExit オプションを活用

前提: 各PCにCrystalDiskInfo(ポータブル版)を配置済み
"""

import subprocess
import shutil
from pathlib import Path
from datetime import datetime
from typing import List


def collect_smart_info(pc_list: List[str], cdi_path: str, output_dir: Path):
    """
    複数PCからS.M.A.R.T.情報を収集
    
    Args:
        pc_list: PCのホスト名/IPリスト
        cdi_path: CrystalDiskInfoのUNCパス(例: \\\\PC01\\C$\\Tools\\CrystalDiskInfo)
        output_dir: 収集先ディレクトリ
    """
    
    output_dir.mkdir(parents=True, exist_ok=True)
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    
    results = []
    
    for pc in pc_list:
        print(f"収集中: {pc}")
        
        try:
            # リモートでCrystalDiskInfoを実行(/CopyExitオプション)
            remote_path = f"\\\\{pc}\\{cdi_path.lstrip('\\\\')}"
            exe_path = f"{remote_path}\\DiskInfo64.exe"
            
            # PsExecなどでリモート実行(要管理者権限)
            # subprocess.run(['psexec', f'\\\\{pc}', exe_path, '/CopyExit'], timeout=60)
            
            # DiskInfo.txtをコピー
            src = Path(f"{remote_path}\\DiskInfo.txt")
            dst = output_dir / f"{pc}_{timestamp}.txt"
            
            if src.exists():
                shutil.copy(src, dst)
                results.append({'pc': pc, 'status': 'success', 'file': dst})
            else:
                results.append({'pc': pc, 'status': 'no_output', 'file': None})
                
        except Exception as e:
            results.append({'pc': pc, 'status': 'error', 'error': str(e)})
    
    # 結果サマリー
    print("\n" + "=" * 60)
    print("収集結果サマリー")
    print("=" * 60)
    
    success = [r for r in results if r['status'] == 'success']
    failed = [r for r in results if r['status'] != 'success']
    
    print(f"成功: {len(success)}")
    print(f"失敗: {len(failed)}")
    
    if failed:
        print("\n失敗したPC:")
        for r in failed:
            print(f"  {r['pc']}: {r.get('error', r['status'])}")
    
    return results


def main():
    # 監視対象PCリスト
    pc_list = [
        'PC001',
        'PC002',
        'PC003',
        # ...
    ]
    
    # CrystalDiskInfoの配置パス(各PCで共通)
    cdi_path = "C$\\Tools\\CrystalDiskInfo"
    
    # 収集先
    output_dir = Path("./collected_smart_info")
    
    collect_smart_info(pc_list, cdi_path, output_dir)


if __name__ == "__main__":
    main()

6.3 ユースケース3: NASの定期健康チェック

想定読者: 自宅NASを運用している方、小規模オフィスのNAS管理者

注意: NASにはCrystalDiskInfoを直接インストールできないことが多い。以下はNASからHDDを取り外してチェックする、またはUSB接続でチェックする場合の手順。

#!/usr/bin/env python3
"""
NAS向け: HDDの定期チェック記録ツール
USB接続でNASのHDDをチェックした結果を記録・比較する
"""

import json
from pathlib import Path
from datetime import datetime
from typing import Dict, Optional


class NASHealthTracker:
    """NASのHDD健康状態を追跡"""
    
    def __init__(self, data_file: Path):
        self.data_file = data_file
        self.data = self._load()
    
    def _load(self) -> Dict:
        """データファイルを読み込む"""
        if self.data_file.exists():
            with open(self.data_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        return {'drives': {}, 'checks': []}
    
    def _save(self):
        """データファイルを保存"""
        with open(self.data_file, 'w', encoding='utf-8') as f:
            json.dump(self.data, f, ensure_ascii=False, indent=2)
    
    def add_check(self, drive_serial: str, drive_model: str, 
                  smart_data: Dict, notes: str = ""):
        """チェック結果を追加"""
        
        check_record = {
            'timestamp': datetime.now().isoformat(),
            'serial': drive_serial,
            'model': drive_model,
            'smart': smart_data,
            'notes': notes
        }
        
        self.data['checks'].append(check_record)
        
        # ドライブ情報を更新
        if drive_serial not in self.data['drives']:
            self.data['drives'][drive_serial] = {
                'model': drive_model,
                'first_seen': datetime.now().isoformat(),
                'check_count': 0
            }
        
        self.data['drives'][drive_serial]['check_count'] += 1
        self.data['drives'][drive_serial]['last_check'] = datetime.now().isoformat()
        
        self._save()
        
        # 前回との比較
        return self._compare_with_previous(drive_serial, smart_data)
    
    def _compare_with_previous(self, drive_serial: str, 
                               current_smart: Dict) -> Optional[Dict]:
        """前回のチェック結果と比較"""
        
        # 同じドライブの過去のチェックを探す
        previous_checks = [
            c for c in self.data['checks'][:-1]  # 最新を除く
            if c['serial'] == drive_serial
        ]
        
        if not previous_checks:
            return None
        
        previous = previous_checks[-1]
        changes = {}
        
        # 重要な属性の変化をチェック
        critical_attrs = ['05', 'C5', 'C6', '09']
        
        for attr in critical_attrs:
            if attr in current_smart and attr in previous['smart']:
                current_raw = current_smart[attr].get('raw', 0)
                previous_raw = previous['smart'][attr].get('raw', 0)
                
                if current_raw != previous_raw:
                    changes[attr] = {
                        'previous': previous_raw,
                        'current': current_raw,
                        'diff': current_raw - previous_raw
                    }
        
        return changes if changes else None
    
    def generate_report(self) -> str:
        """全ドライブのサマリーレポートを生成"""
        
        lines = []
        lines.append("=" * 60)
        lines.append("NAS HDD健康状態サマリー")
        lines.append(f"生成日時: {datetime.now().strftime('%Y/%m/%d %H:%M')}")
        lines.append("=" * 60)
        lines.append("")
        
        for serial, info in self.data['drives'].items():
            lines.append(f"{info['model']}")
            lines.append(f"  シリアル: {serial}")
            lines.append(f"  初回チェック: {info['first_seen'][:10]}")
            lines.append(f"  最終チェック: {info.get('last_check', '不明')[:10]}")
            lines.append(f"  チェック回数: {info['check_count']}")
            
            # 最新のS.M.A.R.T.データを表示
            latest_check = next(
                (c for c in reversed(self.data['checks']) 
                 if c['serial'] == serial),
                None
            )
            
            if latest_check:
                smart = latest_check['smart']
                if '05' in smart:
                    lines.append(f"  代替処理済セクタ: {smart['05'].get('raw', 0)}")
                if '09' in smart:
                    hours = smart['09'].get('raw', 0)
                    years = hours / 8760
                    lines.append(f"  使用時間: {hours}時間 ({years:.1f}年)")
            
            lines.append("")
        
        return "\n".join(lines)


def main():
    # 使用例
    tracker = NASHealthTracker(Path('./nas_health_data.json'))
    
    # CrystalDiskInfoから手動で読み取った値を入力
    # (実際の運用では、DiskInfo.txtをパースする)
    smart_data = {
        '05': {'raw': 0, 'current': 100},
        'C5': {'raw': 0, 'current': 100},
        'C6': {'raw': 0, 'current': 100},
        '09': {'raw': 45678, 'current': 95},
        'C2': {'raw': 38, 'current': 62}
    }
    
    changes = tracker.add_check(
        drive_serial='WD-XXXXXXXXXXXXX',
        drive_model='WD Red Plus 4TB',
        smart_data=smart_data,
        notes='定期チェック'
    )
    
    if changes:
        print("⚠️ 前回からの変化を検出:")
        for attr, change in changes.items():
            print(f"  属性 {attr}: {change['previous']}{change['current']} (差分: {change['diff']})")
    else:
        print("✅ 前回から大きな変化なし")
    
    print()
    print(tracker.generate_report())


if __name__ == "__main__":
    main()

ユースケースを把握できたところで、この先の学習パスを確認しよう。


7. 学習ロードマップ

この記事を読んだ後、次のステップとして以下をおすすめする。

初級者向け(まずはここから)

  1. CrystalDiskInfoをインストールして自分のPCをチェック

    • 公式サイトからダウンロード
    • 健康状態が「正常」か確認
  2. 重要な5つの属性を覚える

    • 05: 代替処理済セクタ数
    • C5: 代替処理保留中セクタ数
    • C6: 回復不能セクタ数
    • C2: 温度
    • 09: 使用時間

中級者向け(実践に進む)

  1. 常駐機能を有効にしてログを蓄積

    • メニュー → 機能 → 常駐
    • 1週間程度運用してグラフを確認
  2. イベントログと連携

    • メニュー → 機能 → 警報機能 → イベントログ
    • Windowsタスクスケジューラで定期レポート生成

上級者向け(さらに深く)

  1. S.M.A.R.T.の仕様を理解

  2. smartmontoolsとの連携

    • Linux/macOSでの監視
    • Prometheus/Grafanaでの可視化

8. まとめ

この記事では、CrystalDiskInfoについて以下を解説した:

  1. CrystalDiskInfoの正体: S.M.A.R.T.情報を可視化する無料の健康診断ツール
  2. 画面の見方: 健康状態(正常/注意/異常/不明)、温度、属性値の意味
  3. ログの場所: インストールフォルダ/Smart/[ディスク名]/Smart.ini
  4. グラフ機能: メニュー → 機能 → グラフ で経時変化を確認
  5. 監視設定: 常駐、イベントログ、メール通知の設定方法

私の所感

CrystalDiskInfoは、日本製のフリーソフトとして非常に完成度が高い。特に以下の点が素晴らしい。

  1. 日本語対応が完璧: 属性名まで日本語化されている
  2. USB外付けドライブ対応: 多くのツールが対応していない領域
  3. 継続的なアップデート: NVMe、RAID、新しいSSDコントローラへの対応が迅速

一方で注意点もある。

  • 起動中しかログが蓄積されない: 常駐機能を使わないとグラフが歯抜けになる
  • S.M.A.R.T.の限界: Googleの研究によると、故障の36%は事前兆候なしに発生する
  • NAS直接対応不可: ネットワーク経由でのS.M.A.R.T.取得には対応していない

CrystalDiskInfoは「万能の予防ツール」ではないが、「気づける故障を気づかないまま見逃す」リスクを大幅に減らせる。最も重要なのは、定期的にチェックする習慣常にバックアップを取る習慣 だ。ツールはあくまで補助であり、データを守るのは自分自身だということを忘れないでほしい。


参考文献

関連記事

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?