Qtプログラミングで役立つ!QFileInfo::isExecutable()の実践コード例

2025-05-31

QFileInfo::isExecutable() とは?

QFileInfo::isExecutable() は、Qt の QFileInfo クラスのメンバー関数で、ファイルが実行可能であるかどうかをチェックするために使用されます。この関数は、対象のファイルが現在のユーザーによって実行する権限を持っている場合に true を返し、そうでない場合は false を返します。

動作の仕組み

ファイルが実行可能であるかどうかの判断は、オペレーティングシステムに依存します。

  • Windows系システム
    Windowsでは、ファイルの実行可能性は主にファイル拡張子によって判断されます。.exe, .com, .bat, .cmd などの拡張子を持つファイルは通常、実行可能と見なされます。ただし、ファイルに実行権限が設定されていることも確認されます。
  • Unix系システム(Linux, macOSなど)
    これらのシステムでは、ファイルのパーミッション(読み取り、書き込み、実行)が重要です。isExecutable() は、ファイルに実行パーミッションが付与されているかどうかを調べます。ファイルの拡張子(例: .sh, .py)は、実行可能性に直接関係ありません。

使用例

#include <QCoreApplication>
#include <QFileInfo>
#include <QDebug>

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

    // 存在し、実行可能なファイルへのパスを指定
    QString executableFilePath = "/usr/bin/ls"; // Linuxの場合の例
    // Windowsの場合の例: "C:/Windows/System32/notepad.exe"

    QFileInfo fileInfo(executableFilePath);

    if (fileInfo.exists()) { // ファイルが存在するかどうか
        if (fileInfo.isExecutable()) {
            qDebug() << executableFilePath << "は実行可能です。";
        } else {
            qDebug() << executableFilePath << "は実行可能ではありません。";
        }
    } else {
        qDebug() << executableFilePath << "は存在しません。";
    }

    // 実行可能ではないファイルの例 (例えば、テキストファイル)
    QString textFilePath = "my_document.txt";
    QFile file(textFilePath);
    if (file.open(QIODevice::WriteOnly)) {
        file.write("これはテストファイルです。");
        file.close();
    }

    QFileInfo textFileInfo(textFilePath);
    if (textFileInfo.exists()) {
        if (textFileInfo.isExecutable()) {
            qDebug() << textFilePath << "は実行可能です。";
        } else {
            qDebug() << textFilePath << "は実行可能ではありません。";
        }
    }

    return 0;
}
  • パフォーマンス
    QFileInfo の関数は、ファイルの情報を取得するためにシステムコールを行う場合があります。大量のファイルをチェックする場合は、パフォーマンスに影響を与える可能性があります。
  • シンボリックリンク
    QFileInfo は、シンボリックリンクを追跡して、そのリンクが指すファイルの情報を提供します。したがって、シンボリックリンク自体ではなく、リンク先のファイルが実行可能かどうかを isExecutable() は返します。
  • 権限
    isExecutable()true を返す場合でも、実際にそのファイルを起動するには、ユーザーがそのファイルを実行する適切な権限を持っている必要があります。


QFileInfo::isExecutable() はファイルの実行権限をチェックする便利な関数ですが、環境やファイルの種類によって予期しない結果を返すことがあります。

ファイルが存在しない、またはパスが間違っている

エラーの症状
isExecutable() が常に false を返す。QFileInfo::exists()false を返す。

原因
QFileInfo オブジェクトに渡されたファイルパスが正しくないか、ファイル自体が存在しません。

トラブルシューティング

  • デバッグ出力
    qDebug() を使って QFileInfo オブジェクトの filePath() を出力し、実際にどのパスをチェックしているのかを確認します。
  • exists() の利用
    isExecutable() を呼び出す前に、必ず QFileInfo::exists() を使ってファイルが存在するかどうかを確認します。
  • パスの確認
    ファイルパスが正しいことを確認します。絶対パスを使用している場合は、それが本当にその場所にあることを確認します。相対パスを使用している場合は、現在の作業ディレクトリ(QDir::currentPath() で取得できます)からの相対パスが正しいことを確認します。
QString filePath = "/path/to/your/file.exe"; // または "nonexistent_file.txt"
QFileInfo fileInfo(filePath);

if (!fileInfo.exists()) {
    qDebug() << "エラー: ファイルが存在しません。" << filePath;
} else {
    if (fileInfo.isExecutable()) {
        qDebug() << filePath << "は実行可能です。";
    } else {
        qDebug() << filePath << "は実行可能ではありません。";
    }
}

Windows で .exe 以外の実行ファイルが検出されない

エラーの症状
.bat.ps1.py など、Windows で実行可能なはずのファイルが isExecutable()false と判定される。

原因
Windows 上の Qt の isExecutable() は、主にファイルの拡張子(.exe, .com, .bat など)に基づいて実行可能性を判断します。他のスクリプトファイルやカスタムな実行ファイルは、デフォルトでは実行可能と認識されないことがあります。

トラブルシューティング

  • プロセス起動
    実際に実行したいだけであれば、QProcess を使って直接ファイルを起動してみる方が確実な場合があります。その場合、isExecutable() の結果に頼るのではなく、起動が成功するかどうかで判断します。
  • MIME タイプやカスタムチェックの追加
    実行したいファイルが .exe 以外のスクリプトファイルなどの場合は、QMimeDatabase を使用して MIME タイプをチェックしたり、ファイルの内容を解析してマジックナンバーを確認したりするなど、独自の実行可能性チェックロジックを追加することを検討します。
  • ファイル拡張子の確認
    Windows では、isExecutable() は一般的に以下の拡張子をチェックします: .exe, .com, .bat。これら以外の拡張子のファイルは、たとえシステムで実行可能と設定されていても false を返す可能性があります。
#ifdef Q_OS_WIN
    QString scriptPath = "C:/Scripts/my_script.bat";
    QFileInfo scriptInfo(scriptPath);
    if (scriptInfo.exists()) {
        if (scriptInfo.isExecutable()) {
            qDebug() << scriptPath << "は実行可能です。";
        } else {
            qDebug() << scriptPath << "は実行可能ではありません。(WindowsのisExecutable()の制限かもしれません)";
            // 代替案: .bat ファイルを起動するなら QProcess::start を試す
            // QProcess process;
            // process.start(scriptPath);
        }
    }
#endif

Unix 系システムで実行権限があるはずのファイルが検出されない

エラーの症状
Linux や macOS などで、chmod +x で実行権限を与えたファイルが isExecutable()false と判定される。

原因

  • 実際のパーミッション不足
    ファイルに実行権限が +x で付与されていても、現在のユーザーにその実行権限がない場合。例えば、other の実行権限がないファイルに対して、現在のユーザーが ownergroup に属していない場合など。
  • ファイルパスの問題
    上記の「ファイルが存在しない」と同じくパスが正しくない。

トラブルシューティング

  • permissions() 関数
    QFileInfo::permissions() 関数を使用して、より詳細なパーミッション情報を取得し、QFile::ExeUser, QFile::ExeGroup, QFile::ExeOther のフラグを確認します。
  • パーミッションの確認
    ターミナルで ls -l <ファイル名> を実行し、ファイルのパーミッション(例: -rwxr-xr-x)を確認します。特に、現在のユーザーに実行権限があるかどうかを確認します。
#ifdef Q_OS_UNIX
    QString filePath = "/home/user/my_script.sh";
    QFileInfo fileInfo(filePath);

    if (fileInfo.exists()) {
        if (fileInfo.isExecutable()) {
            qDebug() << filePath << "は実行可能です。";
        } else {
            qDebug() << filePath << "は実行可能ではありません。";
            // 詳細なパーミッション情報を出力
            QFile::Permissions perms = fileInfo.permissions();
            if (!(perms & QFile::ExeUser)) qDebug() << "  ユーザーに実行権限がありません。";
            if (!(perms & QFile::ExeGroup)) qDebug() << "  グループに実行権限がありません。";
            if (!(perms & QFile::ExeOther)) qDebug() << "  その他のユーザーに実行権限がありません。";
        }
    }
#endif

NTFS ファイルシステムでのパフォーマンスと権限チェックの問題(Windows)

原因
Qt はパフォーマンス上の理由から、NTFS ファイルシステムでの所有権およびパーミッションチェックをデフォルトで無効にしている場合があります。

トラブルシューティング

  • NTFS パーミッションチェックの有効化
    Qt 6.6 以降では、QNtfsPermissionCheckGuard クラスを使用するか、qEnableNtfsPermissionChecks() を呼び出すことで、NTFS のパーミッションチェックを一時的または永続的に有効にできます。
#ifdef Q_OS_WIN
#include <QFileDevice> // QNtfsPermissionCheckGuard を使用する場合

void checkExecutableOnNtfs(const QString& filePath) {
    // 処理中にNTFSパーミッションチェックを有効にするRAIIガード
    QNtfsPermissionCheckGuard permissionGuard; 

    QFileInfo fileInfo(filePath);
    if (fileInfo.exists()) {
        if (fileInfo.isExecutable()) {
            qDebug() << filePath << "は実行可能です。(NTFSチェック有効)";
        } else {
            qDebug() << filePath << "は実行可能ではありません。(NTFSチェック有効)";
        }
    } else {
        qDebug() << filePath << "は存在しません。";
    }
}

// または、より細かく制御する場合
// qEnableNtfsPermissionChecks(); // 永続的に有効にする(非推奨、Qt 6.6から)
// ...
// qDisableNtfsPermissionChecks(); // 無効にする
#endif

注意
qt_ntfs_permission_lookup グローバル変数を直接操作する方法は非推奨です。

キャッシュによる古い情報

エラーの症状
ファイルのパーミッションを変更したのに、isExecutable() が以前の情報を返し続ける。

  • refresh() の呼び出し
    ファイル情報が変更された可能性がある場合は、isExecutable() を呼び出す前に QFileInfo::refresh() を呼び出して、キャッシュをクリアし、最新の情報を取得するようにします。
QString filePath = "my_script.sh";
// ファイルを書き込み、実行権限を設定するなどの外部操作
// system("touch my_script.sh && chmod +x my_script.sh"); // 例

QFileInfo fileInfo(filePath);
fileInfo.refresh(); // 最新の情報を取得

if (fileInfo.exists()) {
    if (fileInfo.isExecutable()) {
        qDebug() << filePath << "は実行可能です。(リフレッシュ後)";
    } else {
        qDebug() << filePath << "は実行可能ではありません。(リフレッシュ後)";
    }
}
  • キャッシュの無効化
    ごくまれなケースですが、非常に頻繁にファイルの属性が変化するような場合は、QFileInfo::setCaching(false) を呼び出してキャッシュを完全に無効にすることもできます。ただし、これはパフォーマンスに影響を与える可能性があります。


QFileInfo::isExecutable() は、特定のファイルが現在のユーザーによって実行可能であるかどうかをチェックする際に非常に役立ちます。ここでは、いくつかの一般的な使用シナリオとそれに対応するコード例を紹介します。

例 1: 基本的な実行可能性チェック

最も基本的な使用法です。指定されたパスのファイルが存在し、実行可能かどうかを調べます。

#include <QCoreApplication>
#include <QFileInfo>
#include <QDebug> // デバッグ出力用

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

    // 1. 存在する実行可能ファイル (例: Linuxの 'ls' コマンド)
    // Windows の場合: "C:/Windows/System32/notepad.exe" など
    QString executablePath = "/usr/bin/ls";
    QFileInfo executableFileInfo(executablePath);

    qDebug() << "--- " << executablePath << " ---";
    if (executableFileInfo.exists()) {
        if (executableFileInfo.isExecutable()) {
            qDebug() << "このファイルは存在し、実行可能です。";
        } else {
            qDebug() << "このファイルは存在しますが、実行可能ではありません。";
        }
    } else {
        qDebug() << "このファイルは存在しません。";
    }

    // 2. 存在するが実行可能ではないファイル (例: このソースコードファイル自身)
    QString sourceCodePath = argv[0]; // 実行ファイル自身を指す
    if (sourceCodePath.isEmpty()) {
        sourceCodePath = "main.cpp"; // デバッグ実行時など、argv[0]が空の場合のフォールバック
    }
    QFileInfo sourceCodeFileInfo(sourceCodePath);

    qDebug() << "\n--- " << sourceCodePath << " ---";
    if (sourceCodeFileInfo.exists()) {
        if (sourceCodeFileInfo.isExecutable()) {
            qDebug() << "このファイルは存在し、実行可能です。";
        } else {
            qDebug() << "このファイルは存在しますが、実行可能ではありません。";
        }
    } else {
        qDebug() << "このファイルは存在しません。";
    }

    // 3. 存在しないファイル
    QString nonExistentPath = "/path/to/nonexistent/file.xyz";
    QFileInfo nonExistentFileInfo(nonExistentPath);

    qDebug() << "\n--- " << nonExistentPath << " ---";
    if (nonExistentFileInfo.exists()) {
        if (nonExistentFileInfo.isExecutable()) {
            qDebug() << "このファイルは存在し、実行可能です。";
        } else {
            qDebug() << "このファイルは存在しますが、実行可能ではありません。";
        }
    } else {
        qDebug() << "このファイルは存在しません。";
    }

    return 0;
}

解説

  • その後、isExecutable() を呼び出して実行可能性をチェックします。
  • exists() を使ってファイルが存在するかどうかを確認することが重要です。存在しないファイルに対して isExecutable() を呼び出しても、常に false を返します。
  • QFileInfo オブジェクトを作成し、ファイルパスをコンストラクタに渡します。

例 2: ディレクトリ内の実行可能ファイルをリストアップする

特定のディレクトリを走査し、その中にある実行可能なファイルを識別する例です。

#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QDebug>

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

    // チェックしたいディレクトリのパス
    // 例: Linuxの /bin または Windowsの C:/Windows/System32
    QString directoryPath = "/bin"; // Linux/macOS の場合
    // QString directoryPath = "C:/Windows/System32"; // Windows の場合

    QDir directory(directoryPath);

    if (!directory.exists()) {
        qDebug() << "エラー: ディレクトリが見つかりません: " << directoryPath;
        return 1;
    }

    qDebug() << "ディレクトリ内の実行可能ファイルをリストアップ: " << directoryPath;
    qDebug() << "------------------------------------------";

    // ディレクトリ内のすべてのファイルとディレクトリのリストを取得
    QFileInfoList entries = directory.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);

    for (const QFileInfo &entryInfo : entries) {
        if (entryInfo.isExecutable()) {
            qDebug() << "  [実行可能] " << entryInfo.fileName();
        } else {
            // qDebug() << "  [非実行可能] " << entryInfo.fileName(); // 非実行可能ファイルも表示する場合
        }
    }

    qDebug() << "------------------------------------------";

    return 0;
}

解説

  • ループ内で各 QFileInfo オブジェクトに対して isExecutable() を呼び出します。
  • entryInfoList() を使って、指定されたディレクトリ内のファイル(QDir::Files)のみを取得します。... は除外(QDir::NoDotAndDotDot)します。
  • QDir クラスを使ってディレクトリを扱います。

例 3: 特定のパスで実行可能なプログラムを探す

例えば、ユーザーが入力したコマンド名がどの場所にあるかを PATH 環境変数を使って探すようなシナリオです。

#include <QCoreApplication>
#include <QFileInfo>
#include <QDir>
#include <QProcessEnvironment> // 環境変数を取得するため
#include <QDebug>

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

    QString commandName = "grep"; // 探したいコマンド名 (例: Linuxの 'grep')
    // Windows の場合: "notepad.exe" など

    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    QString pathVariable = env.value("PATH");

    if (pathVariable.isEmpty()) {
        qDebug() << "PATH 環境変数が設定されていません。";
        return 1;
    }

    QStringList pathDirs = pathVariable.split(QDir::listSeparator());

    qDebug() << "コマンド '" << commandName << "' を PATH から探しています...";
    bool foundExecutable = false;

    for (const QString &dirPath : pathDirs) {
        // パスが空でないことを確認し、スラッシュで終わるように正規化
        QString normalizedDirPath = QDir::cleanPath(dirPath);
        if (normalizedDirPath.isEmpty()) {
            continue;
        }

        QDir dir(normalizedDirPath);
        if (!dir.exists()) {
            continue; // ディレクトリが存在しない場合はスキップ
        }

        QString fullPath = QDir::cleanPath(normalizedDirPath + QDir::separator() + commandName);
#ifdef Q_OS_WIN
        // Windows では .exe, .bat などの拡張子も考慮する
        if (!fullPath.endsWith(".exe", Qt::CaseInsensitive) &&
            !fullPath.endsWith(".com", Qt::CaseInsensitive) &&
            !fullPath.endsWith(".bat", Qt::CaseInsensitive) &&
            !fullPath.endsWith(".cmd", Qt::CaseInsensitive))
        {
            // 拡張子がない場合は、一般的な実行ファイルの拡張子を試す
            QStringList possibleExtensions = {".exe", ".com", ".bat", ".cmd"};
            for (const QString& ext : possibleExtensions) {
                QFileInfo fileInfo(fullPath + ext);
                if (fileInfo.exists() && fileInfo.isExecutable()) {
                    qDebug() << "  見つかりました (PATH): " << fileInfo.absoluteFilePath();
                    foundExecutable = true;
                    // 見つかったらループを抜ける
                    break;
                }
            }
        } else {
            // 既に拡張子がある場合
            QFileInfo fileInfo(fullPath);
            if (fileInfo.exists() && fileInfo.isExecutable()) {
                qDebug() << "  見つかりました (PATH): " << fileInfo.absoluteFilePath();
                foundExecutable = true;
            }
        }
#else // Unix系システム
        QFileInfo fileInfo(fullPath);
        if (fileInfo.exists() && fileInfo.isExecutable()) {
            qDebug() << "  見つかりました (PATH): " << fileInfo.absoluteFilePath();
            foundExecutable = true;
        }
#endif
        if (foundExecutable) {
            break; // 見つかったらパスの検索を終了
        }
    }

    if (!foundExecutable) {
        qDebug() << "コマンド '" << commandName << "' は PATH 内で見つかりませんでした。";
    }

    return 0;
}
  • Windows 環境では、.exe.bat などの一般的な実行ファイルの拡張子を考慮するロジックを追加しています。これは、Windows の isExecutable() が主に拡張子に基づいて判断するためです。
  • 各ディレクトリ内でコマンド名に該当するファイルが存在し、かつ実行可能であるかを QFileInfo::exists()QFileInfo::isExecutable() でチェックします。
  • PATH 環境変数の値を QDir::listSeparator() で分割し、ディレクトリのリストを作成します。
  • QProcessEnvironment::systemEnvironment() を使ってシステム環境変数を取得します。


QFile::permissions() を使った詳細な権限チェック (Unix系システム向け)

コード例

#include <QCoreApplication>
#include <QFileInfo>
#include <QDebug>
#include <QFile> // QFile::Permissions のために必要

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

    QString filePath = "/usr/bin/bash"; // 実行可能ファイルの例
    // QString filePath = "/etc/hosts"; // 実行可能ではないファイルの例

    QFileInfo fileInfo(filePath);

    if (!fileInfo.exists()) {
        qDebug() << "ファイルが存在しません:" << filePath;
        return 0;
    }

    qDebug() << "ファイル:" << filePath;

    // isExecutable() でのチェック
    if (fileInfo.isExecutable()) {
        qDebug() << "  QFileInfo::isExecutable() は 'true' を返しました。";
    } else {
        qDebug() << "  QFileInfo::isExecutable() は 'false' を返しました。";
    }

    // permissions() を使った詳細なチェック
    QFile::Permissions perms = fileInfo.permissions();

    qDebug() << "  詳細なパーミッション:";
    if (perms & QFile::ReadUser)   qDebug() << "    - 所有者は読み取り可能";
    if (perms & QFile::WriteUser)  qDebug() << "    - 所有者は書き込み可能";
    if (perms & QFile::ExeUser)    qDebug() << "    - 所有者は実行可能";
    if (perms & QFile::ReadGroup)  qDebug() << "    - グループは読み取り可能";
    if (perms & QFile::WriteGroup) qDebug() << "    - グループは書き込み可能";
    if (perms & QFile::ExeGroup)   qDebug() << "    - グループは実行可能";
    if (perms & QFile::ReadOther)  qDebug() << "    - その他のユーザーは読み取り可能";
    if (perms & QFile::WriteOther) qDebug() << "    - その他のユーザーは書き込み可能";
    if (perms & QFile::ExeOther)   qDebug() << "    - その他のユーザーは実行可能";

    // 特定の権限の組み合わせをチェック
    if ((perms & QFile::ExeUser) || (perms & QFile::ExeGroup) || (perms & QFile::ExeOther)) {
        qDebug() << "  いずれかの実行権限があります。";
    } else {
        qDebug() << "  実行権限がありません。";
    }

    return 0;
}

利点

  • Windows ではパーミッションの概念が Unix 系システムと異なるため、QFile::Permissions のフラグが期待通りに機能しない場合があります。Windows では、主にファイルの拡張子とセキュリティ記述子(ACL)が実行可能性に影響します。
  • ファイルのパーミッションに関するより詳細な情報が得られます。

QProcess::start() や QProcess::execute() を使って実際に起動を試みる

ファイルが実行可能かどうかを最終的に判断する最も確実な方法は、実際にそのファイルをプロセスとして起動してみることです。起動に成功すれば実行可能と判断できます。

コード例

#include <QCoreApplication>
#include <QProcess>
#include <QFileInfo>
#include <QDebug>

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

    // 存在し、実行可能なファイル (例: Linuxの 'ls' コマンド)
    QString programToRun = "/usr/bin/ls";
    // Windows の場合: "C:/Windows/System32/notepad.exe" など

    qDebug() << "プログラムの実行を試行中: " << programToRun;

    // QProcess::execute() は新しいプロセスを実行し、終了するまで待機します。
    // 成功した場合 (終了コード 0) にのみ、実行可能と判断できます。
    // エラー (-2) は QProcess::FailedToStart を示します。
    int exitCode = QProcess::execute(programToRun, QStringList());

    if (exitCode == 0) {
        qDebug() << "  実行に成功しました。これは実行可能と判断できます。";
    } else if (exitCode == -2) {
        qDebug() << "  実行に失敗しました (QProcess::FailedToStart)。このファイルは実行できない可能性があります。";
        // ファイルが見つからない、パーミッションがない、不正な形式など
        QFileInfo fileInfo(programToRun);
        if (!fileInfo.exists()) {
            qDebug() << "    理由: ファイルが存在しません。";
        } else if (!fileInfo.isExecutable()) {
            qDebug() << "    理由: QFileInfo::isExecutable() は false を返しました。";
        }
    } else {
        qDebug() << "  実行は完了しましたが、終了コードは " << exitCode << " でした。";
        qDebug() << "  (これはプログラムが実行されたことを示しますが、正常終了ではないかもしれません)";
    }

    // 存在しないプログラムの例
    QString nonExistentProgram = "/usr/bin/nonexistent_command";
    qDebug() << "\nプログラムの実行を試行中: " << nonExistentProgram;
    exitCode = QProcess::execute(nonExistentProgram);
    if (exitCode == -2) {
        qDebug() << "  実行に失敗しました (QProcess::FailedToStart)。ファイルが存在しないか、実行できません。";
    }

    return 0;
}

利点

  • 起動に成功しても、プログラムが内部でエラーを返す場合(終了コードが0以外)もあります。
  • 短命のプロセスであっても、起動に時間がかかったり、副次的な作用(ログ出力、ファイル作成など)を引き起こす可能性があります。
  • 実際にプロセスが起動されるため、余計なリソースを消費する可能性があります。
  • クロスプラットフォームで一貫した結果が得やすいです。 欠点
  • 最も確実な実行可能性チェックです。ファイルが存在し、かつ実行する権限があり、さらにOSがそのファイルをプログラムとして認識して起動できる場合にのみ成功します。

プラットフォーム固有の API を直接呼び出す

Qt はクロスプラットフォームな抽象化を提供していますが、特定のOSの機能に深く依存する場合は、プラットフォーム固有のAPIを直接呼び出すことで、よりきめ細かい制御や、Qt が提供しない情報へのアクセスが可能になります。

a. Unix系システム (Linux, macOS): access() 関数

access() 関数は、ファイルパスに対して特定のアクセス権限があるかをチェックするPOSIX標準の関数です。

コード例

#include <QCoreApplication>
#include <QFileInfo>
#include <QDebug>

#ifdef Q_OS_UNIX
#include <unistd.h> // access() のために必要
#endif

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

#ifdef Q_OS_UNIX
    QString filePath = "/usr/bin/ls";
    // QString filePath = "/etc/shadow"; // 通常のユーザーは実行不可

    qDebug() << "access() を使って実行可能性をチェック:" << filePath;

    // F_OK: ファイルが存在するか
    // R_OK: 読み取り可能か
    // W_OK: 書き込み可能か
    // X_OK: 実行可能か
    if (access(filePath.toLocal8Bit().constData(), X_OK) == 0) {
        qDebug() << "  access(X_OK) は成功しました。このファイルは実行可能です。";
    } else {
        qDebug() << "  access(X_OK) は失敗しました。このファイルは実行可能ではありません。";
        qDebug() << "  errno:" << errno << "(" << strerror(errno) << ")";
    }
#else
    qDebug() << "この例はUnix系システムでのみ動作します。";
#endif

    return 0;
}

利点

  • エラー処理(errno の確認)が必要になります。
  • クロスプラットフォームではありません。
  • 特定のパーミッションビットの存在を直接確認できます。 欠点
  • OSが提供する生のファイルシステム権限チェックであり、QFileInfo::isExecutable() よりも低レベルで信頼性が高い場合があります。

b. Windows: PathFindOnPath()ShellExecuteEx() (起動試行)

Windows API には、ファイルシステムパスから実行ファイルを見つけたり、実際に実行を試みたりする関数があります。

  • ShellExecuteEx(): ファイルを特定の動詞("open", "runas" など)で起動しようと試みます。QProcess::execute() と同様に、実際に起動を試みることで実行可能性を間接的に判断できます。

コード例 (概念のみ、完全な実装は複雑になる可能性があります)

// Windows API を直接使う場合 (C++コードとWinAPIの混在)
#ifdef Q_OS_WIN
#include <windows.h>
#include <shlwapi.h> // PathFindOnPath のために必要
#pragma comment(lib, "shlwapi.lib")

// ...
QString executableName = "notepad.exe";
WCHAR szPath[MAX_PATH];
lstrcpyW(szPath, executableName.toStdWString().c_str());

// PATH環境変数から実行ファイルを探す
if (PathFindOnPathW(szPath, NULL)) {
    qDebug() << "PathFindOnPathW で見つかりました:" << QString::fromWCharArray(szPath);
    // 見つかったファイルに対して QFileInfo::isExecutable() を適用することもできる
    QFileInfo foundFileInfo(QString::fromWCharArray(szPath));
    if (foundFileInfo.isExecutable()) {
        qDebug() << "  そして QFileInfo::isExecutable() で実行可能と判断されました。";
    }
} else {
    qDebug() << "PathFindOnPathW で見つかりませんでした。";
}

// ShellExecuteEx を使って実行を試みる (より確実な実行可能性チェック)
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.lpFile = executableName.toStdWString().c_str();
sei.nShow = SW_HIDE; // ウィンドウを非表示にする
sei.fMask = SEE_FLAG_NOASYNC | SEE_FLAG_NOCLOSEPROCESS; // 同期実行

if (ShellExecuteEx(&sei)) {
    WaitForSingleObject(sei.hProcess, INFINITE); // プロセス終了を待機
    DWORD exitCode;
    GetExitCodeProcess(sei.hProcess, &exitCode);
    CloseHandle(sei.hProcess);
    qDebug() << "ShellExecuteEx で実行に成功しました。終了コード:" << exitCode;
} else {
    qDebug() << "ShellExecuteEx で実行に失敗しました。";
    qDebug() << "  エラーコード:" << GetLastError();
}
#endif

利点

  • Windows API の知識が必要で、コードが複雑になりがちです。
  • クロスプラットフォームではありません。
  • PathFindOnPath のように、PATH 環境変数内の実行ファイル検索を効率的に行えます。 欠点:
  • Windowsのファイルシステムと実行ロジックに深く統合されており、より正確な結果が得られる可能性があります。

QFileInfo::isExecutable() はほとんどのユースケースで十分ですが、以下のような場合は代替方法を検討してください。

  • プラットフォーム固有の挙動(特にWindowsでの拡張子ルールなど)に厳密に対応したい場合
    プラットフォーム固有のAPIを直接呼び出す
  • 「実際に起動できるか」を厳密にチェックしたい場合
    QProcess::execute() またはプラットフォーム固有の起動API
  • 詳細なパーミッション情報が必要な場合
    QFile::permissions() (Unix系)