🙄

Windowsのファイルには、一意のIDがついているらしい。

に公開

注意 以下にコードを示すが、たぶん適切でない。そうゆうのがあるということを紹介するために提示する物なので、ご理解ください。

C#でWindows上のファイルのファイルID(MFTレコード番号)を取得するには、.NETの標準ライブラリには直接的なメソッドがないため、Windows API (Win32 API) を利用する必要があります。

以下に、P/Invoke(Platform Invoke)を使って、GetFileInformationByHandle関数を呼び出し、ファイルIDを取得する

ということらしいので、実際にAIにコードを書かせて、ファイルの場所が変更されても同じIDを取得可能かを検証してみた。

違う場所に移動させたファイルのパスを入力しても同じIDを出力しました。

これで、ユーザがファイルの場所を変更したとしても同一のファイルであることの証明ができるようになりました。

ファイルの移動変更、名前の変更等を監視する機能と一緒に使う感じになりそうですが、
ソフトが起動しているという前提なのがつらい所ではあります。

監視自体がよくないという問題は置いておいて。

using System;
using System.IO;
using System.Runtime.InteropServices;

public class FileIdProvider
{
    // 修正後の FILE_ID_DESCRIPTOR_UNION の定義
    // 共用体(Union)をバイト配列として定義し、その後の変換で安全性を確保する
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct FILE_ID_DESCRIPTOR_UNION
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] Identifier;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct FILE_ID_DESCRIPTOR
    {
        public int dwSize;
        public FILE_ID_TYPE Type;
        public FILE_ID_DESCRIPTOR_UNION Identifier;
    }

    public enum FILE_ID_TYPE
    {
        FileIdType_FileId128,
        FileIdType_ObjectId,
        FileIdType_UsnSourceId,
        FileIdType_Extended,
        FileIdType_Maximum,
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetFileInformationByHandleEx(
        IntPtr hFile,
        FILE_INFO_BY_HANDLE_CLASS FileInfoClass,
        out FILE_ID_DESCRIPTOR lpFileInformation,
        uint dwBufferSize);

    public enum FILE_INFO_BY_HANDLE_CLASS
    {
        FileIdInfo = 18,
    }

    /// <summary>
    /// 指定されたファイルのMFTレコード番号(ファイルID)を取得します。
    /// </summary>
    /// <param name="filePath">ファイルへのパス</param>
    /// <returns>MFTレコード番号(ulong)</returns>
    public static ulong GetMftRecordNumber(string filePath)
    {
        using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            var descriptor = new FILE_ID_DESCRIPTOR();
            descriptor.dwSize = Marshal.SizeOf(descriptor);

            bool success = GetFileInformationByHandleEx(
                fs.SafeFileHandle.DangerousGetHandle(),
                FILE_INFO_BY_HANDLE_CLASS.FileIdInfo,
                out descriptor,
                (uint)descriptor.dwSize);

            if (!success)
            {
                throw new InvalidOperationException($"Failed to get file ID for '{filePath}'. Error: {Marshal.GetLastWin32Error()}");
            }

            // バイト配列から64ビット整数に変換する
            return BitConverter.ToUInt64(descriptor.Identifier.Identifier, 0);
        }
    }
}

// ファイルIDを取得したいファイルのパス
string filePath = @"フルパス";

try
{
    ulong fileId = FileIdProvider.GetMftRecordNumber(filePath);
    Console.WriteLine($"ファイルID: {fileId}");
}
catch (FileNotFoundException)
{
    Console.WriteLine("ファイルが見つかりませんでした。");
}
catch (Exception ex)
{
    Console.WriteLine($"エラーが発生しました: {ex.Message}");
}

Discussion