Qtでファイルシステムジャンクションを極める: filesystemJunctionTarget()徹底解説

2025-05-31

以下に詳しく説明します。

std::filesystem::path QFileInfo::filesystemJunctionTarget() とは

この関数は、QFileInfo オブジェクトが表すファイルシステムのエントリがNTFSジャンクションポイントである場合に、そのジャンクションポイントが指すターゲットのパスstd::filesystem::path 型で返します。

  • std::filesystem::path: C++17 で導入された標準ライブラリのクラスで、ファイルシステムのパスを表現するための、より現代的でタイプセーフな方法を提供します。Qt は元々 QString をパス表現に広く使っていましたが、C++17 のファイルシステムライブラリとの連携を強化するために、std::filesystem::path を返す関数が追加されました。

  • NTFS ジャンクションポイント (NTFS Junction Point): Windows のファイルシステム (NTFS) の機能の一つで、あるディレクトリを別の場所にあるディレクトリへのシンボリックリンクのように機能させることができます。通常のシンボリックリンク(シンボリックリンクまたはショートカット)とは異なり、ジャンクションはボリューム間でも機能し、ファイルシステムレベルで透過的にリダイレクトされます。

主な用途

この関数は、以下のような場合に役立ちます。

  • ファイルシステムの分析: システム上のジャンクションポイントの構造を理解したり、それらが指す実際の場所を特定したりする際に使用します。
  • ジャンクションポイントの解決: QFileInfo が示すパスがジャンクションポイントであるかどうかを isJunction() で確認し、もしジャンクションポイントであれば、この関数を使ってそのジャンクションが実際に指している元のディレクトリのパスを取得できます。

戻り値

  • QFileInfo オブジェクトがジャンクションポイントでない場合、空の std::filesystem::path オブジェクトを返します。
  • QFileInfo オブジェクトがジャンクションポイントを表している場合、そのジャンクションが指すディレクトリの絶対パスstd::filesystem::path 型で返します。

注意点

  • Qt のバージョン: この関数はQt 6.2以降で利用可能です。それ以前のバージョンでは、同様の機能を提供する QString QFileInfo::junctionTarget() が使用できます。
  • ターゲットの存在: この関数が返すパスは、ジャンクションポイントが指し示している論理的なパスであり、そのターゲットのパスが実際に存在するかどうかは保証されません。ターゲットが存在するかどうかを確認するには、返されたパスを使って新しい QFileInfo オブジェクトを作成し、exists() 関数を呼び出す必要があります。
  • プラットフォーム依存: ジャンクションポイントは主にWindowsのNTFSファイルシステムに特有の概念です。Unix系システム(Linux, macOSなど)では、同様の機能としてシンボリックリンクが広く使われます。シンボリックリンクのターゲットを取得するには、QFileInfo::symLinkTarget() または QFileInfo::filesystemSymLinkTarget() を使用します。
#include <QCoreApplication>
#include <QFileInfo>
#include <QDebug>
#include <filesystem> // std::filesystem::path を使用するために必要

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

    // 例として、Windows上の一般的なジャンクションポイントを想定
    // 実際のパスは環境によって異なります
    QString junctionPath = "C:/Users/YourUser/AppData/Local/Application Data"; // これは通常ジャンクションです

    QFileInfo fileInfo(junctionPath);

    if (fileInfo.isJunction()) {
        qDebug() << "パスはジャンクションポイントです:" << fileInfo.filePath();

        std::filesystem::path targetPath = fileInfo.filesystemJunctionTarget();
        qDebug() << "ジャンクションのターゲット (std::filesystem::path):" << QString::fromStdWString(targetPath.c_str());

        // ターゲットが存在するかどうかを確認
        QFileInfo targetInfo(QString::fromStdWString(targetPath.c_str()));
        if (targetInfo.exists()) {
            qDebug() << "ターゲットは存在します。";
        } else {
            qDebug() << "ターゲットは存在しません。";
        }
    } else {
        qDebug() << "パスはジャンクションポイントではありません:" << fileInfo.filePath();
    }

    return 0;
}


    • エラー: QFileInfo オブジェクトがジャンクションポイントではないファイルやディレクトリを表している場合に filesystemJunctionTarget() を呼び出すと、空の std::filesystem::path が返されます。これはエラーというよりは期待される動作ですが、コードがその空のパスを処理しなかった場合にロジックエラーにつながります。
    • トラブルシューティング: filesystemJunctionTarget() を呼び出す前に、必ず QFileInfo::isJunction() を使用して、対象のパスがジャンクションポイントであるかどうかを確認します。
    QFileInfo fileInfo("C:/path/to/some/file.txt"); // ジャンクションではないと仮定
    
    if (fileInfo.isJunction()) {
        std::filesystem::path target = fileInfo.filesystemJunctionTarget();
        // ... ターゲットパスを処理 ...
    } else {
        qDebug() << "このパスはジャンクションポイントではありません。";
    }
    
  1. 存在しないジャンクションポイント

    • エラー: QFileInfo オブジェクトが、ファイルシステム上に存在しないジャンクションポイントのパスを表している場合。isJunction()false を返し、filesystemJunctionTarget() は意味のあるターゲットを返しません。
    • トラブルシューティング: QFileInfo::exists() を使って、まずそのパス自体がファイルシステム上に存在するかどうかを確認します。
    QString nonExistentJunction = "C:/NonExistentJunction";
    QFileInfo fileInfo(nonExistentJunction);
    
    if (!fileInfo.exists()) {
        qDebug() << "パスが存在しません:" << nonExistentJunction;
    } else if (fileInfo.isJunction()) {
        std::filesystem::path target = fileInfo.filesystemJunctionTarget();
        qDebug() << "ジャンクションのターゲット:" << QString::fromStdWString(target.c_str());
    } else {
        qDebug() << "存在しますが、ジャンクションではありません。";
    }
    
  2. ターゲットパスの欠落または無効化

    • エラー: ジャンクションポイント自体は存在するが、それが指し示すターゲットパスが移動、削除、または無効になっている場合。filesystemJunctionTarget() はターゲットパスを返しますが、そのパス自体は存在しない可能性があります。
    • トラブルシューティング: filesystemJunctionTarget() から返されたパスを使って新しい QFileInfo オブジェクトを作成し、その exists() メソッドを使ってターゲットが実際に存在するかどうかを確認します。
    QFileInfo junctionInfo("C:/path/to/my/junction"); // ジャンクションと仮定
    if (junctionInfo.isJunction()) {
        std::filesystem::path targetPath = junctionInfo.filesystemJunctionTarget();
        qDebug() << "ジャンクションのターゲット:" << QString::fromStdWString(targetPath.c_str());
    
        QFileInfo targetFileInfo(QString::fromStdWString(targetPath.c_str()));
        if (!targetFileInfo.exists()) {
            qWarning() << "警告: ジャンクションのターゲットは存在しません:" << QString::fromStdWString(targetPath.c_str());
        }
    }
    
  3. プラットフォームの非互換性

    • エラー: filesystemJunctionTarget() は、主にWindowsのNTFSジャンクションポイントに特化した機能です。LinuxやmacOSのようなUnix系システムでは、NTFSジャンクションポイントは存在しません。これらのシステムでこの関数を使用しても、常に空のパスが返されます(または未定義の動作を引き起こす可能性がありますが、通常は空パスを返します)。
    • トラブルシューティング: コードがマルチプラットフォームで動作する必要がある場合、#ifdef Q_OS_WIN のようなプリプロセッサディレクティブを使用して、プラットフォーム固有の処理を分ける必要があります。シンボリックリンクの解決には、QFileInfo::symLinkTarget() または QFileInfo::filesystemSymLinkTarget() を使用します。
    QFileInfo fileInfo(somePath);
    
    #ifdef Q_OS_WIN
    if (fileInfo.isJunction()) {
        std::filesystem::path target = fileInfo.filesystemJunctionTarget();
        qDebug() << "Windows ジャンクションターゲット:" << QString::fromStdWString(target.c_str());
    }
    #endif
    
    if (fileInfo.isSymLink()) {
        // シンボリックリンクの処理(全OSで利用可能)
        std::filesystem::path symLinkTarget = fileInfo.filesystemSymLinkTarget();
        qDebug() << "シンボリックリンクターゲット:" << QString::fromStdWString(symLinkTarget.c_str());
    }
    
  4. Qt と std::filesystem::path の混合使用による型変換の問題

    • エラー: QtのAPIは伝統的に QString をパス表現に多用していますが、filesystemJunctionTarget()std::filesystem::path を返します。これらの型間で頻繁に変換を行うと、コードが冗長になったり、潜在的なエンコーディングの問題が発生したりする可能性があります。
    • トラブルシューティング:
      • 可能な限り、std::filesystem::path を使用する場合は、C++標準ライブラリのファイルシステム機能と一貫して使用します。
      • QtのAPI(QFile, QDirなど)と連携する必要がある場合は、QString::fromStdWString(path.c_str())(Windowsの場合)や QString::fromStdString(path.string())(Unix系の場合)のように、明示的な型変換を行います。Qt 6では、QFileInfo のコンストラクタや他の関数で std::filesystem::path を直接受け入れるオーバーロードが追加されている場合があります。
      • Qt 6.2以降を使用していることを確認し、より新しいAPI(QFileInfo::filesystemJunctionTarget()QFileInfo::filesystemSymLinkTarget())を積極的に活用します。

std::filesystem::path QFileInfo::filesystemJunctionTarget() を使用する際のトラブルシューティングの鍵は、以下の点にあります。

  • 型変換の適切な処理: QStringstd::filesystem::path の間の変換を意識し、適切に行います。
  • プラットフォームの考慮: Windows固有の機能であることを理解し、クロスプラットフォーム対応が必要な場合は条件付きコンパイルを使用します。
  • ターゲットの検証: 取得したターゲットパスが実際に有効であるか (QFileInfo(targetPath).exists()) を確認します。
  • 前提条件の確認: ジャンクションポイントであること (isJunction()) と、パスが存在すること (exists()) を確認します。


例1:ジャンクションポイントの検出とターゲットの取得

この例では、指定されたパスがジャンクションポイントであるかどうかを検出し、もしそうであれば、そのターゲットパスを取得して表示します。

#include <QCoreApplication>
#include <QFileInfo>
#include <QDebug>
#include <filesystem> // std::filesystem::path を使用するために必要

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

    // テスト用のパス
    // 実際のWindows環境に合わせてパスを調整してください。
    // 一般的なジャンクションポイントの例:
    // "C:/Users/<UserName>/Application Data" -> "C:/Users/<UserName>/AppData/Roaming"
    // "C:/Users/<UserName>/My Documents" (古いシステムの場合)
    // テストのために、手動でジャンクションを作成することもできます:
    // 例: mklink /J C:\MyJunction C:\Users\Public\Documents

    QString pathToTest1 = "C:/Users/Public/Documents"; // 通常のディレクトリ
    QString pathToTest2 = "C:/Users/All Users/Application Data"; // これはWindowsのジャンクションポイントの典型例
    QString pathToTest3 = "C:/NonExistentJunction"; // 存在しないパス

    QList<QString> pathsToTest = {
        pathToTest1,
        pathToTest2,
        pathToTest3
    };

    for (const QString& currentPath : pathsToTest) {
        QFileInfo fileInfo(currentPath);

        qDebug() << "\n--- パスの確認: " << currentPath << " ---";

        if (!fileInfo.exists()) {
            qDebug() << "パスは存在しません。";
            continue;
        }

        if (fileInfo.isJunction()) {
            std::filesystem::path targetPath = fileInfo.filesystemJunctionTarget();
            qDebug() << "これはジャンクションポイントです。";
            qDebug() << "ターゲットパス (std::filesystem::path):" << QString::fromStdWString(targetPath.c_str());

            // ターゲットパスが存在するかどうかの確認
            QFileInfo targetFileInfo(QString::fromStdWString(targetPath.c_str()));
            if (targetFileInfo.exists()) {
                qDebug() << "ターゲットは存在し、ディレクトリです:" << targetFileInfo.isDir();
            } else {
                qWarning() << "警告: ターゲットは存在しません。";
            }
        } else {
            qDebug() << "これはジャンクションポイントではありません。";
            qDebug() << "ディレクトリですか?" << fileInfo.isDir();
            qDebug() << "ファイルですか?" << fileInfo.isFile();
        }
    }

    return a.exec();
}

解説

  1. QCoreApplication を初期化します。
  2. テストしたいパスを QString のリストに格納します。
    • pathToTest1 は通常のディレクトリとして扱われるべきです。
    • pathToTest2 は多くのWindowsシステムで実際のジャンクションポイントとして機能する可能性があります。これは C:\ProgramData に解決されることが多いです。
    • pathToTest3 は存在しないパスの例です。
  3. 各パスについて、QFileInfo オブジェクトを作成します。
  4. fileInfo.exists() でパスが実際に存在するかを確認します。
  5. fileInfo.isJunction() でパスがジャンクションポイントであるかを確認します。
  6. もしジャンクションポイントであれば、fileInfo.filesystemJunctionTarget() を呼び出して std::filesystem::path 型のターゲットパスを取得します。
  7. std::filesystem::pathQString に変換して表示します。Windowsでは std::wstring ベースなので QString::fromStdWString(targetPath.c_str()) が適切です。
  8. 取得したターゲットパスが実際に存在するかどうかも QFileInfo を使って確認しています。

例2:特定のディレクトリツリー内のジャンクションポイントを検索

この例では、与えられたディレクトリとそのサブディレクトリを再帰的に走査し、見つかったすべてのジャンクションポイントとそのターゲットを表示します。

#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QDebug>
#include <filesystem> // std::filesystem::path を使用するために必要

// 指定されたディレクトリ内のジャンクションポイントを再帰的に検索する関数
void findJunctionPoints(const QString& startDir)
{
    QDir dir(startDir);
    if (!dir.exists()) {
        qDebug() << "ディレクトリが存在しません:" << startDir;
        return;
    }

    // フィルタリング: ディレクトリとジャンクションをリストアップ
    // QDir::AllEntries はシンボリックリンクやジャンクションも含む
    QFileInfoList entries = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);

    for (const QFileInfo &entry : entries) {
        if (entry.isJunction()) {
            std::filesystem::path targetPath = entry.filesystemJunctionTarget();
            qDebug() << "ジャンクションポイントが見つかりました:" << entry.filePath();
            qDebug() << "  ターゲット:" << QString::fromStdWString(targetPath.c_str());

            // ターゲットが存在しない場合を警告
            QFileInfo targetFileInfo(QString::fromStdWString(targetPath.c_str()));
            if (!targetFileInfo.exists()) {
                qWarning() << "  警告: ターゲットは存在しません!";
            }
        }

        // ディレクトリであれば再帰的に探索(ただし、ジャンクション自体は再帰しない)
        // QFileInfo::isDir() はジャンクションポイントに対しては true を返すことがあるため注意
        // 実際のディレクトリかどうかは、isJunction() の後にチェックするのが安全
        if (entry.isDir() && !entry.isJunction()) {
            findJunctionPoints(entry.filePath()); // 再帰呼び出し
        }
    }
}

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

    // 検索を開始するディレクトリ
    // 例: "C:/Users/YourUser/AppData/Local" はジャンクションが多い場所です
    // 環境に合わせて調整してください
    QString startDirectory = "C:/Users/Public"; // 公開されているドキュメントなど

    qDebug() << "ジャンクションポイントを検索中 (開始ディレクトリ: " << startDirectory << ")";
    findJunctionPoints(startDirectory);

    return a.exec();
}
  1. findJunctionPoints 関数は、指定されたディレクトリ内のすべてのエントリ(ファイルとディレクトリ)をリストアップします。
  2. 各エントリについて、entry.isJunction() を使用してそれがジャンクションポイントであるかどうかをチェックします。
  3. ジャンクションポイントが見つかった場合、entry.filesystemJunctionTarget() を呼び出してターゲットパスを取得し、表示します。
  4. 再帰的な検索のために、entry.isDir() でディレクトリであるかを確認します。重要な点として、ジャンクションポイントも isDir()true を返すことがあるため、無限ループを避けるために !entry.isJunction() の条件も追加して、ジャンクション自体には再帰しないようにしています。 これにより、ジャンクションポイントをたどってその先のディレクトリツリーを再帰的に探索するのを防ぎます。もしジャンクションの先をたどりたい場合は、この条件を削除するか、またはターゲットパスを引数に再帰呼び出しを行う必要があります(ただし、無限ループや循環参照に注意が必要です)。
  • Windows 環境でのテスト
    Windows では、管理者権限でコマンドプロンプトを開き、mklink /J C:\MyTestJunction C:\Users\Public\Documents のようにしてテスト用のジャンクションポイントを作成できます。
  • CMakeLists.txt
    cmake_minimum_required(VERSION 3.16)
    project(JunctionScanner LANGUAGES CXX)
    
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    
    find_package(Qt6 COMPONENTS Core REQUIRED)
    
    add_executable(JunctionScanner main.cpp)
    target_link_libraries(JunctionScanner Qt6::Core)
    
  • Qt プロジェクトファイル (.pro)
    std::filesystem を使用するには、C++17 以上を有効にする必要があります。
    QT += core
    CONFIG += c++17 # または c++1z
    


Qt 5 以前での代替: QString QFileInfo::junctionTarget()

Qt 6.2より前では、QString QFileInfo::junctionTarget() という関数が提供されていました。これは std::filesystem::path ではなく QString を返す点を除けば、機能的にはほぼ同じです。

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

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

    QString junctionPath = "C:/Users/All Users/Application Data"; // Windowsのジャンクションポイントの例

    QFileInfo fileInfo(junctionPath);

    if (fileInfo.isJunction()) {
        // Qt 6.2 より前のバージョンで利用可能
        QString targetPath = fileInfo.junctionTarget();
        qDebug() << "これはジャンクションポイントです (Qt5互換)。";
        qDebug() << "ターゲットパス (QString):" << targetPath;

        QFileInfo targetFileInfo(targetPath);
        if (targetFileInfo.exists()) {
            qDebug() << "ターゲットは存在します。";
        } else {
            qWarning() << "警告: ターゲットは存在しません。";
        }
    } else {
        qDebug() << "これはジャンクションポイントではありません。";
    }

    return a.exec();
}

利点

  • Qt のパス表現 (QString) と一貫しています。
  • Qt 5 との互換性があります。

欠点

  • std::filesystem::path のようなC++17標準ライブラリの利点を活用できません。

シンボリックリンクのターゲット取得: QFileInfo::symLinkTarget() または QFileInfo::filesystemSymLinkTarget()

filesystemJunctionTarget() はNTFSジャンクションポイントに特化していますが、ファイルシステムにはシンボリックリンクもあります。クロスプラットフォームでシンボリックリンクのターゲットを取得したい場合は、以下の関数を使用します。

  • std::filesystem::path QFileInfo::filesystemSymLinkTarget(): std::filesystem::path を返します。Qt 6.2 以降で利用可能です。
  • QString QFileInfo::symLinkTarget(): QString を返します。Qt 5 および Qt 6 で利用可能です。

ジャンクションポイントとシンボリックリンクは異なる概念ですが、ファイルシステムのリンクを一般的に扱う場合にはこれらが代替となりえます。

#include <QCoreApplication>
#include <QFileInfo>
#include <QDebug>
#include <filesystem> // std::filesystem::path のために必要

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

    // テスト用のパス
    // Linux/macOS でのシンボリックリンクの例: ln -s /path/to/original /path/to/symlink
    // Windows でのシンボリックリンクの例: mklink /D C:\MySymLink C:\path\to\original_dir
    QString symLinkPath = "/usr/bin/python"; // Linux/macOS の一般的なシンボリックリンク

    QFileInfo fileInfo(symLinkPath);

    if (fileInfo.isSymLink()) {
        // QString を返すバージョン
        QString symLinkTargetQString = fileInfo.symLinkTarget();
        qDebug() << "これはシンボリックリンクです。 (QString)";
        qDebug() << "ターゲットパス:" << symLinkTargetQString;

        // std::filesystem::path を返すバージョン (Qt 6.2以降)
        #if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0))
        std::filesystem::path symLinkTargetStdFs = fileInfo.filesystemSymLinkTarget();
        qDebug() << "ターゲットパス (std::filesystem::path):" << QString::fromStdWString(symLinkTargetStdFs.c_str());
        #endif

        QFileInfo targetFileInfo(symLinkTargetQString);
        if (targetFileInfo.exists()) {
            qDebug() << "ターゲットは存在します。";
        } else {
            qWarning() << "警告: ターゲットは存在しません。";
        }
    } else {
        qDebug() << "これはシンボリックリンクではありません。";
    }

    return a.exec();
}

利点

  • QFileInfo の一部として、ファイルシステムからの情報取得が容易です。
  • クロスプラットフォームでシンボリックリンクを扱えます。

欠点

  • WindowsのNTFSジャンクションポイントを直接識別・解決するものではありません(ただし、Windows上のディレクトリシンボリックリンクはisSymLink()で検出される可能性があります)。

Windows API を直接使用する (低レベル)

Qt を使用せず、Windows API を直接叩いてジャンクションポイントのターゲットを取得することも可能です。これは最も低レベルな方法であり、より詳細な制御が必要な場合や、Qt 以外の環境で使用する場合に検討されます。

CreateFile 関数と DeviceIoControl を使用して、リパースポイント (reparse point) データを読み取ります。ジャンクションポイントはリパースポイントの一種です。

#include <windows.h>
#include <QDebug>
#include <QString>
#include <string> // For std::wstring
#include <winioctl.h> // For IOCTL_STORAGE_GET_DEVICE_NUMBER

// Reparse point buffer structure (simplified for junction points)
typedef struct _REPARSE_DATA_BUFFER {
    ULONG ReparseTag;
    USHORT ReparseDataLength;
    USHORT Reserved;
    union {
        struct {
            USHORT SubstituteNameOffset;
            USHORT SubstituteNameLength;
            USHORT PrintNameOffset;
            USHORT PrintNameLength;
            ULONG Flags;
            WCHAR PathBuffer[1];
        } SymbolicLinkReparseBuffer;
        struct {
            USHORT SubstituteNameOffset;
            USHORT SubstituteNameLength;
            USHORT PrintNameOffset;
            USHORT PrintNameLength;
            WCHAR PathBuffer[1];
        } MountPointReparseBuffer; // This is for junction points
    };
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;

// ジャンクションポイントのターゲットを取得する関数
QString getJunctionTargetWinAPI(const QString& junctionPath)
{
    HANDLE hFile = CreateFileW(
        junctionPath.toStdWString().c_str(),
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        qWarning() << "CreateFileW failed for" << junctionPath << ":" << GetLastError();
        return QString();
    }

    char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
    DWORD bytesReturned;
    QString targetPath;

    if (DeviceIoControl(
        hFile,
        FSCTL_GET_REPARSE_POINT,
        NULL,
        0,
        buffer,
        sizeof(buffer),
        &bytesReturned,
        NULL
    )) {
        REPARSE_DATA_BUFFER* reparseData = (REPARSE_DATA_BUFFER*)buffer;

        if (reparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { // ジャンクションポイント
            // MOUNT_POINT の構造は SymbolicLinkReparseBuffer とほぼ同じだが、Flagsがない
            // PathBufferからターゲットパスを抽出
            LPCWSTR targetBuffer = reparseData->MountPointReparseBuffer.PathBuffer;
            targetPath = QString::fromWCharArray(
                targetBuffer + (reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)),
                reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)
            );
            // NTパス形式(\??\C:\...)をC:\...に変換
            if (targetPath.startsWith("\\??\\")) {
                targetPath = targetPath.mid(4);
            }
        } else {
            qDebug() << "これはジャンクションポイントではありません (ReparseTag:" << QString::number(reparseData->ReparseTag, 16) << ").";
        }
    } else {
        qWarning() << "DeviceIoControl failed for" << junctionPath << ":" << GetLastError();
    }

    CloseHandle(hFile);
    return targetPath;
}

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

    QString junctionPath = "C:/Users/All Users/Application Data"; // Windowsのジャンクションポイントの例

    QString target = getJunctionTargetWinAPI(junctionPath);
    if (!target.isEmpty()) {
        qDebug() << "ジャンクションのターゲット (WinAPI):" << target;
    } else {
        qDebug() << "ジャンクションのターゲットを取得できませんでした。";
    }

    return a.exec();
}

利点

  • 非常に低レベルな制御が可能です。
  • Qt のバージョンに依存せず、Windows API を直接利用します。
  • REPARSE_DATA_BUFFER の構造を正確に理解し、解析する必要があります。
  • コードが複雑になり、エラーハンドリングも手動で行う必要があります。
  • Windows 専用のコードになります。クロスプラットフォームでは使用できません。
  • 非常に特殊な要件があり、Windows API を直接制御したい場合
    • 上記のような Windows API を直接使用する方法を検討しますが、通常はQtの提供する高レベルAPIで十分です。
  • Qt 5 以前を使用している場合
    • Windows の NTFS ジャンクションポイントを扱うなら、QFileInfo::junctionTarget() を使用します。
    • クロスプラットフォームでシンボリックリンクを扱うなら、QFileInfo::symLinkTarget() を使用します。
  • Qt 6.2 以降を使用している場合
    • Windows の NTFS ジャンクションポイントを扱うなら、最もシンプルで推奨されるのは QFileInfo::filesystemJunctionTarget() です。
    • クロスプラットフォームでシンボリックリンクを扱うなら、QFileInfo::filesystemSymLinkTarget() が最善です。