[BlueLeaf1336]> PROGRAM>

NTFSドライブの情報取得

historyTOP

2006/01/08:作成

overviewTOP

いつものようにまず最初に。このページの内容に独自性はありません。完全無欠にパクリです。

感動的なフリーウェアが多く公開されている Sysinternals Freeware で、ソースコードもいくつか公開されているのですが、このうちの NTFSドライブの情報を取得するサンプルをDelphiに翻訳しただけです。

WindowsXP と Windows2000 で試したんですが、オリジナル と同じ答えを戻しているようです。でもいつものごとく思うのは、Delphiでなくても...

それから、Delphiに翻訳するにあたってよくわからない箇所はそのままに動くように動くようにを目標にしています。記述にまずい点があるかも知れません。

NtFsControlFile というドキュメント化されてない(Last Updated: November 11, 1997 v1.0 時点で...)関数を使っているんですが、コレを使うためのフラグや結果を受けとるための構造体がわからないことには、使い道がありません。どなたかが既に調べられていると思いますが、検索してしまうとここでやった内容が陳腐化する可能性が高いので、横に置いときます。

XPでやっても2000でやっても同じだったんですが、メタファイル情報を出力するはずの処理で全く何も出力されません。最初に定数値で書いているメタファイルの名称が変更になっているのかも知れません。

コードTOP

//====================================================================
//
// Ntsinfo.c
//
// Copyright (C) 1997 Mark Russinovich
// http://www.ntinternals.com
//
// Shows NTFS volume information.
//
//====================================================================

unit Ntsinfo;

interface

uses
    Windows, SysUtils, Classes;

type
    //  NTFS volume information
    PNTFS_VOLUME_DATA_BUFFER = ^NTFS_VOLUME_DATA_BUFFER;
    NTFS_VOLUME_DATA_BUFFER = record
        SerialNumber        : LARGE_INTEGER;
        NumberOfSectors     : LARGE_INTEGER;
        TotalClusters       : LARGE_INTEGER;
        FreeClusters        : LARGE_INTEGER;
        Reserved            : LARGE_INTEGER;
        BytesPerSector      : ULONG;
        BytesPerCluster     : ULONG;
        BytesPerMFTRecord   : ULONG;
        ClustersPerMFTRecord: ULONG;
        MFTLength           : LARGE_INTEGER;
        MFTStart            : LARGE_INTEGER;
        MFTMirrorStart      : LARGE_INTEGER;
        MFTZoneStart        : LARGE_INTEGER;
        MFTZoneEnd          : LARGE_INTEGER;
    end;

procedure DumpMetaFiles(const DriveId: Char; Results: TStrings);
function GetNTFSInfo(const DriveId: Char; var VolumeInfo: NTFS_VOLUME_DATA_BUFFER;
                    var Err: String): Boolean;

implementation

const
    //  File System Control command for getting NTFS information
    FSCTL_GET_VOLUME_INFORMATION = $90064;

//  return code type
type NTSTATUS = UINT;

const
    //  Error codes returned by NtFsControlFile (see NTSTATUS.H)
    STATUS_SUCCESS                = NTSTATUS($00000000);
    STATUS_BUFFER_OVERFLOW        = NTSTATUS($80000005);
    STATUS_INVALID_PARAMETER      = NTSTATUS($C000000D);
    STATUS_BUFFER_TOO_SMALL       = NTSTATUS($C0000023);
    STATUS_ALREADY_COMMITTED      = NTSTATUS($C0000021);
    STATUS_INVALID_DEVICE_REQUEST = NTSTATUS($C0000010);

    //  Metadata file names
    MetaFileNames: array[0..10] of string =
    (
        '$mft',
        '$mftmirr',
        '$logfile',
        '$volume',
        '$attrdef',
        '$bitmap',
        '$boot',
        '$badclus',
        '$quota',
        '$badclust',
        '$upcase'
    );

type
    PVOID = Pointer;
    HANDLE = Cardinal;

    //  Io Status block (see NTDDK.H)
    PIO_STATUS_BLOCK = ^_IO_STATUS_BLOCK;
    _IO_STATUS_BLOCK = record
        Status: NTSTATUS;
        Information: ULONG;
    end;

    //  Apc Routine (see NTDDK.H)
    PIO_APC_ROUTINE = procedure(
                            ApcContext: PVOID;
                            IoStatusBlock: PIO_STATUS_BLOCK;
                            Reserved: ULONG
                            );

    //  The undocumented NtFsControlFile
    //  This function is used to send File System Control (FSCTL)
    //  commands into file system drivers. Its definition is
    //  in ntdll.dll (ntdll.lib), a file shipped with the NTDDK.
    TNtFsControlFile = function(
                            FileHandle          : HANDLE;
                            Event               : HANDLE;           //  optional
                            ApcRoutine          : PIO_APC_ROUTINE;  //  optional
                            ApcContext          : PVOID;            //  optional
                            IoStatusBlock       : PIO_STATUS_BLOCK;
                            FsControlCode       : ULONG;
                            InputBuffer         : PVOID;            //  optional
                            InputBufferLength   : ULONG;
                            OutputBuffer        : PVOID;            //  optional
                            OutputBufferLength  : ULONG
                            ): NTSTATUS; stdcall;

var
    NtFsControlFile: TNtFsControlFile = nil;

//--------------------------------------------------------------------
//                  F U N C T I O N S
//--------------------------------------------------------------------

//--------------------------------------------------------------------
//  Translates an NTDLL error code into its text equivalent. This
//  only deals with ones commonly returned by defragmenting FS Control
//  commands.
//--------------------------------------------------------------------
function PrintNtError(const Status: NTSTATUS): String;
begin
    case (Status) of
    STATUS_SUCCESS:                 Result := 'STATUS_SUCCESS';
    STATUS_INVALID_PARAMETER:       Result := 'STATUS_INVALID_PARAMETER';
    STATUS_BUFFER_TOO_SMALL:        Result := 'STATUS_BUFFER_TOO_SMALL';
    STATUS_ALREADY_COMMITTED:       Result := 'STATUS_ALREADY_COMMITTED';
    STATUS_INVALID_DEVICE_REQUEST:  Result := 'STATUS_INVALID_DEVICE_REQUEST';
    else                            Result := Format('%8d', [Status]);
    end;
end;

//--------------------------------------------------------------------
//  Does directory lookups of NTFS metadata files.
//--------------------------------------------------------------------
procedure DumpMetaFiles(const DriveId: Char; Results: TStrings);
var
    FileName: String;
    FindData: WIN32_FIND_DATA;
    FindHandle: HANDLE;
    i: Integer;
    Line: String;
begin
    //  Loop through array of files
    for i := 0 to Length(MetaFileNames) - 1 do
    begin
        FileName := Format('%s:\%s', [DriveId, MetaFileNames[i]]);
        FindHandle := FindFirstFile(PChar(FileName), FindData);
        if (FindHandle <> INVALID_HANDLE_VALUE) then
        begin
            Line := Format('%-11s    %d bytes', [findData.cFileName, findData.nFileSizeLow]);
            Windows.FindClose(FindHandle);
            Results.Add(Line);
        end;
    end;
end;

//--------------------------------------------------------------------
//  Open the volume and query its data.
//--------------------------------------------------------------------
function GetNTFSInfo(const DriveId: Char; var VolumeInfo: NTFS_VOLUME_DATA_BUFFER;
                    var Err: String): Boolean;
var
    VolumeName: String;
    VolumeHandle: HANDLE;
    IoStatus: _IO_STATUS_BLOCK;
    Status: NTSTATUS;
begin
    Result := False;

    //  関数ロード成功チェック
    if (@NtFsControlFile = nil) then
    begin
        Err := 'Not running on supported version of Windows NT.';
        Exit;
    end;

    //  open the volume
    VolumeName := Format('\\.\%s:', [DriveId]);
    VolumeHandle := CreateFile(
                        PChar(VolumeName),
                        GENERIC_READ,
                        FILE_SHARE_READ or FILE_SHARE_WRITE,
                        nil,
                        OPEN_EXISTING,
                        0,
                        0);
    if (VolumeHandle = INVALID_HANDLE_VALUE) then
    begin
        Err := 'Error opening volume:' + SysErrorMessage(GetLastError());
        Exit;
    end;

    //  Query the volume information
    Status := NtFsControlFile(
                        VolumeHandle,
                        0,
                        nil,
                        nil,
                        @IoStatus,
                        FSCTL_GET_VOLUME_INFORMATION,
                        nil,
                        0,
                        @VolumeInfo,
                        SizeOf(NTFS_VOLUME_DATA_BUFFER));

    if (Status <> STATUS_SUCCESS) then
    begin
        Err := 'Error obtaining NTFS information:' + PrintNtError(Status);
        CloseHandle(VolumeHandle);
        Exit;
    end;

    //  Close the volume
    CloseHandle(VolumeHandle);

    Result := True;
end;

initialization

    //  Loads function entry points in NTDLL
    @NtFsControlFile := GetProcAddress(GetModuleHandle('ntdll.dll'), 'NtFsControlFile');

finalization
    //
end.

使い方TOP

こんな感じで。

uses
    Ntsinfo;

//--------------------------------------------------------------------
//  Just open the volume and dump its information.
procedure TForm1.Button1Click(Sender: TObject);
var
    Drive: Char;
    VolumeInfo: NTFS_VOLUME_DATA_BUFFER;
    Err: String;
begin
    Drive := 'C';

    //  Get ntfs volume data
    if (GetNTFSInfo(Drive, VolumeInfo, Err)) then
    begin
        Memo1.Lines.Add(IntToStr(VolumeInfo.NumberOfSectors.QuadPart));
        DumpMetaFiles(Drive, Memo1.Lines);
    end
    else
    begin
        Memo1.Lines.Add(Err);
    end;
end;

実行結果TOP

NtfsInfoTest.zip(192,936bytes)

EOFTOP