これで完璧!Qt QSettingsのエラーコード(AccessError/FormatError)と対処法

2025-05-27

この関数は QSettings::Status 型の値を返します。QSettings::Status は以下のいずれかの値を持ちます。

  • QSettings::FormatError (2):

    • 設定ファイルが不正な形式であるか、破損していることを示します。
    • 例えば、INI形式のファイルが正しくフォーマットされていない場合や、XML形式のファイルが破損している場合などに発生します。
    • このエラーが発生した場合、QSettings はそのファイルから設定を読み込むことができません。
  • QSettings::AccessError (1):

    • 設定ファイルまたはレジストリへのアクセスが拒否されたことを示します。
    • これは、パーミッションの問題(ファイルへの書き込み権限がない、レジストリへのアクセスが制限されているなど)が原因である可能性があります。
    • 例えば、Windowsでは管理者権限が必要なレジストリキーに書き込もうとした場合などに発生します。
  • QSettings::NoError (0):

    • 設定の読み書きが正常に行われたことを示します。
    • これは通常、設定ファイルが正常に開かれ、読み書き操作が成功したことを意味します。

使用例

#include <QCoreApplication>
#include <QSettings>
#include <iostream>

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    // QSettingsオブジェクトを作成(ここではINI形式のファイルを使用)
    // 通常は組織名とアプリケーション名を指定します
    QSettings settings("MyOrganization", "MyApp");

    // 設定を書き込む例
    settings.setValue("language", "Japanese");
    settings.setValue("theme", "Dark");

    // 設定の書き込み後にステータスを確認
    if (settings.status() == QSettings::NoError) {
        std::cout << "設定の書き込みは成功しました。" << std::endl;
    } else {
        std::cout << "設定の書き込みでエラーが発生しました。Status: " << settings.status() << std::endl;
    }

    // 設定を読み込む例
    QString language = settings.value("language").toString();
    QString theme = settings.value("theme", "Light").toString(); // デフォルト値を指定

    std::cout << "言語: " << language.toStdString() << std::endl;
    std::cout << "テーマ: " << theme.toStdString() << std::endl;

    // 設定の読み込み後にステータスを確認
    if (settings.status() == QSettings::NoError) {
        std::cout << "設定の読み込みは成功しました。" << std::endl;
    } else {
        std::cout << "設定の読み込みでエラーが発生しました。Status: " << settings.status() << std::endl;
    }

    // 例えば、存在しないファイルを開こうとした場合や、書き込み権限がない場合に
    // QSettings::AccessError や QSettings::FormatError が発生する可能性があります。
    // (この例では発生しませんが、状況によって起こりえます)

    return 0;
}

QSettings::status() を使用するメリット

  • ユーザーへのフィードバック
    エラーが発生した場合に、ユーザーに適切なメッセージを表示することができます。
  • デバッグ
    問題が発生した場合、具体的なエラーの種類(アクセスエラーかフォーマットエラーかなど)を特定するのに役立ちます。
  • エラーハンドリング
    設定の読み書きが正常に完了したか、または何らかの問題が発生したかをプログラム的に確認できます。


QSettings::AccessError (アクセス拒否エラー)

原因
設定ファイルやレジストリキーへのアクセス権限がない場合に発生します。これは、以下のような状況でよく見られます。

  • ファイルが他のプロセスによってロックされている
    • 設定ファイルが別のアプリケーションや、同じアプリケーションの別のインスタンスによって開かれている場合、アクセスが拒否されることがあります。
  • レジストリへの書き込み権限の不足 (Windows)
    • Windowsレジストリの特定のキー(特に HKEY_LOCAL_MACHINE の一部など)は、管理者権限がないと変更できません。
  • 書き込み権限がないディレクトリへの保存
    • Windowsでは、C:\Program Files のようなシステムディレクトリに設定ファイルを直接書き込もうとすると、管理者権限がない限り拒否されます。
    • macOSやLinuxでも、ユーザーが書き込み権限を持たないディレクトリに保存しようとすると発生します。

トラブルシューティング

  • ファイルのロック解除
    • 設定ファイルを使用している可能性のある他のアプリケーションを閉じてみてください。
    • 同じアプリケーション内で複数の QSettings インスタンスを使用している場合、同時に同じファイルにアクセスしないようにロジックを調整してください。QSettings は内部的にロックメカニズムを持っていますが、競合状態を完全に排除することはできません。
  • 権限の確認
    • ターゲットディレクトリやレジストリキーのファイルシステム権限を確認してください。
    • アプリケーションを管理者として実行してみることで、権限が問題であるかどうかを一時的に確認できます(ただし、これは恒久的な解決策ではありません)。
  • 適切な保存場所の選択
    • ユーザー固有の設定には、QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) (アプリケーションデータ)、QStandardPaths::ConfigLocation (設定ファイル) などのプラットフォーム独立の推奨パスを使用することを強くお勧めします。これにより、OSが適切に管理する場所に設定が保存され、権限の問題が軽減されます。
    • システム全体の設定を保存する場合は、管理者権限が必要となる場合があります。その場合は、インストーラーで設定を書き込むなどのアプローチを検討してください。

QSettings::FormatError (フォーマットエラー)

原因
設定ファイルが破損しているか、QSettings が想定する形式と異なる場合に発生します。

  • 異なるQtバージョン間の互換性問題
    • 稀に、異なるQtバージョン間で設定ファイルの形式が微妙に異なる場合に発生することがあります(ただし、これは非常に稀です)。
  • 不正なエンコーディング
    • QSettingssetIniCodec() などを使ってエンコーディングを指定せずに、非ASCII文字を含む設定を保存・読み込もうとした場合。または、不適切なエンコーディングで保存されたファイルを読み込もうとした場合。
  • ファイルの一部破損
    • ディスクエラーや不適切なシャットダウンなどにより、設定ファイルの一部が破損した場合。
  • 手動による設定ファイルの変更
    • INIファイルやXMLファイルを手動で編集し、構文エラーを導入してしまった場合。
    • 例えば、INIファイルで [Section]Key=Value の書式が崩れている、XMLファイルでタグが正しく閉じられていないなど。

トラブルシューティング

  • バイナリ形式の検討
    • 設定ファイルを手動で編集する必要がないのであれば、QSettings::NativeFormat を使用して、プラットフォームネイティブの形式(Windowsレジストリ、macOSのplistなど)を利用することを検討してください。これらの形式は通常、OSが管理するため、フォーマット破損のリスクが低減されます。
  • デフォルト設定へのリセット
    • エラーが発生した場合は、設定ファイルを削除し、アプリケーションを再起動してデフォルト設定を再生成することを検討します。これにより、破損したファイルの問題を回避できます。ユーザーには設定がリセットされることを明確に伝える必要があります。
  • バックアップからの復元
    • もし設定ファイルのバックアップがあれば、それを復元して問題が解決するか試してください。
  • 設定ファイルの確認
    • エラーが発生した設定ファイル(INI、XMLなど)をテキストエディタで開き、構文エラーがないか確認してください。特に、区切り文字、セクション名、キーと値のペアが正しい形式であるかを確認します。

QSettings::NoError (エラーなし) だが問題が発生している場合

status()NoError を返しても、意図しない動作が発生することがあります。

原因

  • QMLにおける問題
    • QMLで QSettings を使用する場合、JavaScriptの非同期実行とC++の同期的なQSettingsの動作が原因で、設定が期待通りに保存/読み込みされないことがあります。QSettingsインスタンスの初期化とorganizationName/applicationNameの設定が正しく行われているか確認が必要です。
  • sync() の不足
    • setValue() で値を設定した後、すぐにファイルに書き込まれるとは限りません。sync() を呼び出すことで、変更が確実に永続ストレージに書き込まれます。特に、アプリケーションの終了時など、データが失われるのを避けたい場合に重要です。
    settings.setValue("key", "value");
    settings.sync(); // これにより、変更がすぐにファイルに書き込まれます
    
  • QCoreApplication::setOrganizationName() / setApplicationName() の呼び出し順序
    • QSettings オブジェクトを作成する前に QCoreApplication::setOrganizationName()QCoreApplication::setApplicationName() を呼び出す必要があります。これらの設定は、QSettings のデフォルトコンストラクタや一部のコンストラクタで使用されるデフォルトの場所を決定します。
    • 正しい順序:
      int main(int argc, char *argv[]) {
          QApplication app(argc, argv);
          QCoreApplication::setOrganizationName("MyOrganization"); // 先に設定
          QCoreApplication::setApplicationName("MyApp");         // 先に設定
      
          QSettings settings; // デフォルトコンストラクタがこれらの設定を使用
          // ...
          return app.exec();
      }
      
  • 異なるアプリケーション名/組織名
    • QSettings のコンストラクタで指定する組織名やアプリケーション名が、設定を保存した時と読み込む時で異なっている場合。この場合、QSettings は異なる場所を見に行くため、設定が見つかりません。
  • キー名の誤り
    • settings.value("someKey")settings.value("somekey") のように、キー名の大文字・小文字が異なっている場合。WindowsレジストリやINIファイルは通常大文字・小文字を区別しませんが、macOSの CFPreferences は区別します。移植性を考慮し、常に同じ大文字・小文字を使用することが推奨されます。
  • デバッグ出力の活用
    • QSettings::allKeys() を使って、実際に保存されているすべてのキーを表示し、意図したキーが存在するか確認します。
    • qDebug() を使って、設定の読み書き前後に status() の値や読み書きする値をログに出力することで、どこで問題が発生しているかを特定しやすくなります。
  • sync() の明示的な呼び出し
    • 特にアプリケーション終了時や、設定がすぐに反映される必要がある場合は、settings.sync() を明示的に呼び出す習慣をつけましょう。
  • アプリケーション名/組織名の確認
    • 設定を読み書きするすべての場所で、QSettings コンストラクタに渡す組織名とアプリケーション名(または QCoreApplication で設定される名前)が一致していることを確認してください。
  • キー名の一貫性
    • キー名は大文字・小文字を含め、常に同じ文字列を使用してください。定数として定義して使用すると、タイプミスを防げます。


例1: 基本的な使用法とエラーチェック

この例では、QSettings を使って設定を保存し、その後に読み込みます。各操作の後に status() をチェックし、エラーがあればその種類を出力します。

#include <QCoreApplication>
#include <QSettings>
#include <QDebug> // qDebug() を使うために必要

// QSettings::Status を読みやすい文字列に変換するヘルパー関数
QString statusToString(QSettings::Status status) {
    switch (status) {
        case QSettings::NoError:
            return "NoError (エラーなし)";
        case QSettings::AccessError:
            return "AccessError (アクセス拒否)";
        case QSettings::FormatError:
            return "FormatError (フォーマットエラー)";
        default:
            return "Unknown Error (不明なエラー)";
    }
}

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    // QSettings::NativeFormat はプラットフォームネイティブな設定形式を使用します
    // Windows: レジストリ, macOS: plist, Linux: XDG (通常INI)
    // 組織名とアプリケーション名を指定
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("MyApplication");

    QSettings settings; // デフォルトコンストラクタは上記の名前を使用

    qDebug() << "--- 設定の書き込み開始 ---";

    // 設定値を書き込む
    settings.setValue("user/name", "山田太郎");
    settings.setValue("app/language", "Japanese");
    settings.setValue("window/width", 800);
    settings.setValue("window/height", 600);

    // 変更を永続ストレージに同期(強制書き込み)
    settings.sync();

    // 書き込み操作後のステータスを確認
    QSettings::Status writeStatus = settings.status();
    qDebug() << "書き込み後のステータス:" << statusToString(writeStatus);

    if (writeStatus != QSettings::NoError) {
        qWarning() << "設定の書き込み中にエラーが発生しました!";
        // ここでエラーに応じた処理(ログ出力、ユーザーへの通知など)を行う
    }

    qDebug() << "--- 設定の読み込み開始 ---";

    // 設定値を読み込む
    QString userName = settings.value("user/name", "名無し").toString();
    QString appLanguage = settings.value("app/language", "English").toString();
    int windowWidth = settings.value("window/width", 1024).toInt();
    int windowHeight = settings.value("window/height", 768).toInt();

    // 読み込み操作後のステータスを確認
    QSettings::Status readStatus = settings.status();
    qDebug() << "読み込み後のステータス:" << statusToString(readStatus);

    if (readStatus != QSettings::NoError) {
        qWarning() << "設定の読み込み中にエラーが発生しました!";
        // エラーが発生した場合、デフォルト値が使われるか、以前の設定が残る
    }

    qDebug() << "\n--- 読み込んだ設定値 ---";
    qDebug() << "ユーザー名:" << userName;
    qDebug() << "言語:" << appLanguage;
    qDebug() << "ウィンドウ幅:" << windowWidth;
    qDebug() << "ウィンドウ高さ:" << windowHeight;

    // もしINIファイル形式で設定を保存したい場合(例:Windows以外でINIを使いたい場合)
    // QSettings settings(QSettings::IniFormat, QSettings::UserScope, "MyCompany", "MyApplication");
    // settings.setIniCodec("UTF-8"); // 日本語など非ASCII文字を使う場合は必須

    return 0;
}

解説

  • statusToString ヘルパー関数は、QSettings::Status 列挙型をより分かりやすい文字列に変換し、デバッグ出力を改善します。
  • settings.sync() は、メモリ上の変更を直ちに永続ストレージ(ファイルやレジストリ)に書き込むことを保証します。これは、アプリケーション終了時や、設定がすぐに反映される必要がある場合に重要です。
  • QCoreApplication::setOrganizationName()setApplicationName() を先に設定することで、QSettings のデフォルトコンストラクタがどこに設定を保存・読み込むかを知ることができます。

この例はWindowsでのみ機能します。管理者権限が必要なレジストリパスに書き込もうとすることで AccessError を発生させます。このコードは管理者として実行しないでください。

#include <QCoreApplication>
#include <QSettings>
#include <QDebug>
#include <QDir> // QDir::homePath() のために必要

// 例1と同じ statusToString 関数をここにコピーまたは含める

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    qDebug() << "--- アクセスエラーのテスト ---";

    // QSettings::NativeFormat はWindowsではレジストリを使用
    // HKEY_LOCAL_MACHINE は通常、管理者権限がないと書き込めない
    // ここでは、レジストリパスを直接指定するコンストラクタを使用
    // QSettings::SystemScope を使うと、HKEY_LOCAL_MACHINE を対象とするが、
    // ここではより具体的なパスを指定してエラーを確実に発生させる
    QSettings settings("HKEY_LOCAL_MACHINE\\SOFTWARE\\MyUnauthorizedApp", QSettings::NativeFormat);

    qDebug() << "書き込もうとしているパス:" << settings.fileName(); // レジストリパスが表示されるはず

    // 値を書き込もうとする
    settings.setValue("testKey", "testValue");
    settings.sync();

    QSettings::Status writeStatus = settings.status();
    qDebug() << "書き込み後のステータス:" << statusToString(writeStatus);

    if (writeStatus == QSettings::AccessError) {
        qDebug() << "期待通り、アクセスエラーが発生しました。管理者権限が必要です。";
    } else if (writeStatus == QSettings::NoError) {
        qDebug() << "なぜか書き込みが成功しました。システム環境を確認してください。";
        // このパスへの書き込み権限がある場合など
    } else {
        qDebug() << "他のエラーが発生しました:" << statusToString(writeStatus);
    }

    qDebug() << "--- フォーマットエラーのテスト(架空の例) ---";

    // 存在しない、または不正なINIファイルを読み込もうとする
    // これを実際にテストするには、不正な内容のINIファイルを事前に作成するか、
    // 存在しないファイルを指定する必要があります。
    // 例: 不正なINIファイルを作成する(手動で中身を壊す)
    // 例: /non_existent_path/broken.ini

    // ここでは、ホームディレクトリに書き込み権限があるディレクトリを作成し、
    // そこに不正な内容のファイルを作成するシミュレーションをします。
    QString brokenIniPath = QDir::homePath() + "/broken_settings.ini";
    QFile brokenFile(brokenIniPath);
    if (brokenFile.open(QFile::WriteOnly | QFile::Text)) {
        QTextStream out(&brokenFile);
        out << "[[[Invalid_Section" << Qt::endl; // 不正なINI形式
        out << "key=value" << Qt::endl;
        brokenFile.close();
        qDebug() << "不正なINIファイルを生成しました:" << brokenIniPath;
    } else {
        qDebug() << "不正なINIファイルの生成に失敗しました。";
    }

    // 不正なINIファイルを読み込もうとするQSettingsオブジェクト
    QSettings brokenSettings(brokenIniPath, QSettings::IniFormat);
    qDebug() << "読み込もうとしているファイル:" << brokenSettings.fileName();

    // 何らかの値を読み込もうとすると、フォーマットエラーが発生する可能性が高い
    QString value = brokenSettings.value("test").toString();
    QSettings::Status readBrokenStatus = brokenSettings.status();
    qDebug() << "不正なINIファイル読み込み後のステータス:" << statusToString(readBrokenStatus);

    if (readBrokenStatus == QSettings::FormatError) {
        qDebug() << "期待通り、フォーマットエラーが発生しました。";
    } else {
        qDebug() << "他のエラーが発生したか、エラーなし:" << statusToString(readBrokenStatus);
    }

    // テスト用の不正なファイルを削除
    QFile::remove(brokenIniPath);

    return 0;
}
  • FormatError の例では、故意にINIファイルの書式を壊したファイルを生成し、それを QSettings で読み込もうとすることでエラーを発生させています。これにより、QSettings::status()FormatError を返すことを確認できます。
  • AccessError の例では、Windowsレジストリの HKEY_LOCAL_MACHINE 以下に直接書き込もうとすることで、権限エラーを発生させています。これにより、QSettings::status()AccessError を返すことを確認できます。


しかし、「status() を使用しない」という観点や、エラーの発生を間接的に示唆するような状況、あるいはエラーが発生した場合の代替手段として、以下のようなアプローチを考えることはできます。

デフォルト値の活用とロギング

QSettings::value() を呼び出す際にデフォルト値を指定できるため、設定ファイルが存在しない、または特定のキーが見つからない場合に、エラーが発生したとはみなさずにデフォルト値を使用するという考え方です。これは QSettings::NoError の範囲内での動作なので、厳密には status() の代替ではありませんが、ファイルが存在しないことによるエラーを「エラー」として扱わない場合に有効です。

Pros

  • 設定ファイルが初回起動時など存在しない場合に、自動的にデフォルト値で動作を開始できます。
  • アプリケーションがクラッシュすることなく、常に有効な値を持つことができます。

Cons

  • キーが見つからなかったのか、本当にデフォルト値が意図されたものなのかの区別がつきにくい。
  • ファイルの破損やアクセス拒否といったより深刻な問題を見落とす可能性があります。


#include <QCoreApplication>
#include <QSettings>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("MyApplication");

    QSettings settings;

    // 設定が存在しない場合はデフォルト値が使われる
    QString userName = settings.value("user/name", "ゲスト").toString();
    int fontSize = settings.value("editor/fontSize", 12).toInt();
    bool autoSave = settings.value("features/autoSave", true).toBool();

    qDebug() << "ユーザー名:" << userName;
    qDebug() << "フォントサイズ:" << fontSize;
    qDebug() << "自動保存:" << autoSave;

    // もし設定が存在しなければ、ここでデフォルト値で保存される
    settings.setValue("user/name", userName);
    settings.setValue("editor/fontSize", fontSize);
    settings.setValue("features/autoSave", autoSave);
    settings.sync(); // 変更を確実に保存

    // ここで status() をチェックしない場合、エラーがあっても気づかない

    return 0;
}

このアプローチだけでは、ファイルシステムレベルのエラー(アクセス拒否、破損)は検知できません。そのため、通常は status() と組み合わせて使用します。

ファイルの存在チェックと例外ハンドリング(限定的)

QSettings はファイルのI/O操作を行うため、直接的な例外をスローすることはありません。しかし、ファイルパスがわかっている場合(特に QSettings::IniFormatQSettings(const QString &fileName, Format format, ...) コンストラクタを使用する場合)、ファイルが存在するかどうかを事前に QFile::exists() などで確認することは可能です。

Pros

  • ファイルが存在しない場合に、QSettings の操作を行う前にその事実を知ることができます。

Cons

  • すべての QSettings 形式(レジストリなど)で有効なアプローチではありません。
  • ファイルが存在しても、フォーマットが不正だったり、アクセス権がなかったりする可能性は残ります。

例 (INIファイルの場合)

#include <QCoreApplication>
#include <QSettings>
#include <QDebug>
#include <QFile>

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("MyApplication");

    // INIファイル形式でQSettingsを初期化する場合
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, "MyCompany", "MyApplication");
    settings.setIniCodec("UTF-8"); // 日本語対応

    QString settingsFilePath = settings.fileName();
    qDebug() << "設定ファイルのパス:" << settingsFilePath;

    if (!QFile::exists(settingsFilePath)) {
        qDebug() << "設定ファイルが見つかりません。デフォルト設定を生成します。";
        settings.setValue("firstRun", true);
        settings.setValue("version", "1.0");
        settings.sync();
        // この時点で status() は NoError を返すはず
        qDebug() << "ステータス (ファイル生成後):" << settings.status();
    } else {
        qDebug() << "設定ファイルが見つかりました。既存の設定を読み込みます。";
        // ここで status() をチェックすると、AccessError や FormatError が発生している可能性がある
        qDebug() << "ステータス (ファイル読み込み前):" << settings.status();
        QString version = settings.value("version", "不明").toString();
        qDebug() << "バージョン:" << version;
        qDebug() << "ステータス (ファイル読み込み後):" << settings.status();
    }

    return 0;
}

この方法も、status() を完全に代替するものではなく、補完的な役割を持つものです。ファイルが存在しない場合は QSettings は自動的に新しいファイルを作成しようとするため、明示的な QFile::exists() チェックは、初期起動時の処理を区別したい場合などに特に有用です。

最も堅牢なアプローチは、QSettings のインスタンスを独自のクラスでラップし、そのクラス内でエラーハンドリングとデフォルト値の管理を一元的に行うことです。これにより、アプリケーションの他の部分が直接 QSettings の詳細に依存するのを避けることができます。

Pros

  • シングルトンパターンなどで、アプリケーション全体で設定へのアクセスを管理できる。
  • QSettings::status() を内部的に使用しつつ、より高レベルなエラー情報や処理を外部に提供できる。
  • 設定の読み書きに独自のバリデーションやフォールバックメカニズムを組み込める。
  • エラー処理ロジックを一か所に集約できる。

Cons

  • 追加のコードと設計が必要。
// settingsmanager.h
#ifndef SETTINGSMANAGER_H
#define SETTINGSMANAGER_H

#include <QObject>
#include <QSettings>
#include <QVariant>
#include <QDebug>

class SettingsManager : public QObject {
    Q_OBJECT
public:
    explicit SettingsManager(QObject *parent = nullptr);

    // 設定を読み込む
    QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
    // 設定を書き込む
    void setValue(const QString &key, const QVariant &value);
    // 設定を同期(保存)する
    void sync();

    // エラー状態を確認する(QSettings::status() をラップ)
    QSettings::Status lastStatus() const { return m_settings.status(); }

    // エラーメッセージを取得する(オプション)
    QString getErrorMessage() const;

signals:
    void settingsChanged(const QString &key);
    void errorOccurred(QSettings::Status status, const QString &message);

private:
    QSettings m_settings;
};

#endif // SETTINGSMANAGER_H

// settingsmanager.cpp
#include "settingsmanager.h"
#include <QCoreApplication>

SettingsManager::SettingsManager(QObject *parent) : QObject(parent),
    m_settings(QSettings::IniFormat, QSettings::UserScope, "MyCompany", "MyApplication") // 例としてINI形式
{
    // INI形式の場合、エンコーディングを設定
    m_settings.setIniCodec("UTF-8");

    // 初期化時のステータスチェック
    if (m_settings.status() != QSettings::NoError) {
        emit errorOccurred(m_settings.status(), getErrorMessage());
        qWarning() << "QSettingsの初期化エラー:" << getErrorMessage();
    }
}

QVariant SettingsManager::value(const QString &key, const QVariant &defaultValue) const {
    QVariant val = m_settings.value(key, defaultValue);
    if (m_settings.status() != QSettings::NoError) {
        // 読み込みエラーが発生した場合
        // ここでは通常、デフォルト値が返されるため、致命的ではない場合が多いが、ログには残す
        qWarning() << "設定の読み込みエラー (" << key << "):" << getErrorMessage();
        emit errorOccurred(m_settings.status(), "設定の読み込みエラー: " + getErrorMessage());
    }
    return val;
}

void SettingsManager::setValue(const QString &key, const QVariant &value) {
    m_settings.setValue(key, value);
    if (m_settings.status() != QSettings::NoError) {
        qWarning() << "設定の書き込みエラー (" << key << "):" << getErrorMessage();
        emit errorOccurred(m_settings.status(), "設定の書き込みエラー: " + getErrorMessage());
    }
    emit settingsChanged(key);
}

void SettingsManager::sync() {
    m_settings.sync();
    if (m_settings.status() != QSettings::NoError) {
        qWarning() << "設定の同期エラー:" << getErrorMessage();
        emit errorOccurred(m_settings.status(), "設定の同期エラー: " + getErrorMessage());
    }
}

QString SettingsManager::getErrorMessage() const {
    switch (m_settings.status()) {
        case QSettings::NoError: return "エラーなし";
        case QSettings::AccessError: return "設定ファイル/レジストリへのアクセスが拒否されました。";
        case QSettings::FormatError: return "設定ファイルの形式が不正です。";
        default: return "不明なQSettingsエラーです。";
    }
}

// main.cpp での使用例
#include <QCoreApplication>
#include "settingsmanager.h"

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("MyApplication");

    SettingsManager settingsManager;

    // エラーシグナルを捕捉する
    QObject::connect(&settingsManager, &SettingsManager::errorOccurred,
                     [](QSettings::Status status, const QString &message) {
        qCritical() << "設定マネージャーエラー:" << status << "-" << message;
        // ここでユーザーに通知するダイアログを表示したり、ログファイルに書き込んだりする
    });

    // 設定を書き込む
    settingsManager.setValue("user/displayName", "新しいユーザー");
    settingsManager.setValue("app/theme", "dark");
    settingsManager.sync();

    // 書き込み後のステータスは内部で処理される

    // 設定を読み込む
    QString userName = settingsManager.value("user/displayName", "ゲスト").toString();
    QString theme = settingsManager.value("app/theme", "light").toString();
    int nonExistentValue = settingsManager.value("nonexistent/key", 999).toInt(); // 存在しないキー

    qDebug() << "ユーザー表示名:" << userName;
    qDebug() << "テーマ:" << theme;
    qDebug() << "存在しないキーの値 (デフォルト):" << nonExistentValue;

    // 意図的にエラーを発生させる(例えば、iniファイルを手動で破損させるか、権限のない場所に設定する)
    // その後、再度読み込み/書き込みを試みると、errorOccurred シグナルが発行されるはず

    return 0;
}