[WMI] XP SP3 だと Win32_LogicalDisk が FDD の空き容量を取れないことがある (GetDiskFreeSpaceEx なら取れますが…。) Ver 1.0

皆さんごきげんよう。ういこです。
さらに WMI ネタ「Windows XP SP3 だと Win32_LogicalDisk が FDD のデータを取れないことがある」をご紹介させていただきます。

どうも、最近気づいたのが、Win32_LogicalDisk とフロッピーディスクドライブとの相性が良くないっぽい様子で、Windows XP SP3 以降だと、例えば空き容量とかが取れないことがあるみたいです。私の Virtual PC の環境と、同じチームの人から無理やり借りた実機では取れませんでした。ただ、環境によって取れることもあるらしい(私の手元では未確認)ので、障害かどうかについては後日判明したら掲載する予定です。

さてその場合の代替手段ですが、ズバリ "Win32 API の GetDiskFreeSpaceEx() を使うこと" です。C++ 使ってるなら楽なんですが、.NET の場合、.NET Framework 2.0 以降なら System.IO.DriveInfo が使えるかもしれませんが、未確認です。実際使って取れたよ!というかたはこっそり教えてください。(後日一応試す予定です)また、それ以前のバージョンの場合は使えないので、環境がどれか怪しい場合は Win32 API を使うのがよさそうです。ちなみに、.NET Framework のドキュメントには GetDiskFreeSpaceEx() の変わりに WMI を使えと書いてあるんですが、残念です…。 スクリプトの場合は…ちょっと方法を考えます…。

プラットフォーム SDK
GetDiskFreeSpaceEx
指定されたディスクの容量に関する情報を取得します。ディスク全体の容量、全体の空き容量、呼び出し側スレッドに関連するユーザーが利用できる空き容量を取得します。
https://msdn.microsoft.com/ja-jp/library/cc429308.aspx

※ 注意
Windows XP では、ディスククォータ機能によって利用できるディスク容量を制限されたユーザで GetDiskFreeSpaceEx() を実行した結果と、実際の空き容量の値が異なる可能性がありますので、総空き容量を示す lpTotalNumberOfFreeBytes 引数ではなく、呼び出し側が利用できる空き容量を示す lpFreeBytesAvailable 引数の値を利用することをお勧めいたします。

以下、コード例です。Win32 API のほうは MSDN に例があるので、C# をご紹介させていただきます。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace GetDiskFreeSpaceEx_CS
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

[DllImport("kernel32.dll", BestFitMapping = false , CharSet = CharSet.Auto ,
EntryPoint = "GetDiskFreeSpaceEx", SetLastError = true)]
public static extern bool GetDiskFreeSpaceEx(
[MarshalAs(UnmanagedType.LPWStr)] string lpDirectoryName,
[Out] out long lpFreeBytesAvailable,
[Out] out long lpTotalNumberOfBytes,
[Out] out long lpTotalNumberOfFreeBytes
);

private void button1_Click(object sender, EventArgs e)
{
long lpFreeBytesAvailable; // ドライブ名。C: とか、A: とか。
long lpTotalNumberOfBytes;
long lpTotalNumberOfFreeBytes;
string lpbuf;

GetDiskFreeSpaceEx(@"A:\", out lpFreeBytesAvailable,
out lpTotalNumberOfBytes, out lpTotalNumberOfFreeBytes);

lpbuf = lpFreeBytesAvailable + " : 呼び出し側が利用できるバイト数 (単位 : Byte)" + "\r\n" +
lpTotalNumberOfBytes + " : ディスク全体のバイト数 (単位 : Byte)" + "\r\n" +
lpTotalNumberOfFreeBytes + " : ディスク全体の空きバイト数 (単位 : Byte)";
MessageBox.Show(lpbuf);
}
}
}

VB.NET の場合はこんな感じ(色は許してください)↓

<DllImport("kernel32.dll", BestFitMapping:=False, CharSet:=CharSet.Auto, EntryPoint:="GetDiskFreeSpaceEx", SetLastError:=True)> _
    Public Shared Function GetDiskFreeSpaceEx(<MarshalAs(UnmanagedType.LPWStr)> ByVal lpDirectoryName As String, ByRef lpFreeBytesAvailable As Long, ByRef lpTotalNumberOfBytes As Long, ByRef lpTotalNumberOfFreeBytes As Long) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function

    Dim lpFreeBytesAvailable As Long '呼び出し側が利用できるバイト数
    Dim lpTotalNumberOfBytes As Long 'ディスク全体のバイト数
    Dim lpTotalNumberOfFreeBytes As Long 'ディスク全体の空きバイト数
    Dim lpbuf As String

    GetDiskFreeSpaceEx("C:\", lpFreeBytesAvailable, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes)
    lpbuf = lpFreeBytesAvailable.ToString() + " 呼び出し側が利用できるバイト数 (単位 : Byte)" + ControlChars.CrLf _
        + lpTotalNumberOfBytes.ToString() + "ディスク全体のバイト数 (単位 : Byte)" + ControlChars.CrLf _
        + lpTotalNumberOfFreeBytes.ToString() + "ディスク全体の空きバイト数 (単位 : Byte)"
    MessageBox.Show(lpbuf)

これで実際、ういこうの環境で空き容量は取れたので、お困りの方はお試しください。また、間違ってたらこっそり教えてください。(“EntryPoint” は、別に宣言してる関数名と対応する Win32 API の名前一緒なんだけどなんとなくつけちゃいました。癖で…)

アップデートがありましたら随時更新しますです。それでは、また!

ういこう@ドクター中松発明の傑作「しょうゆちゅるちゅる(多分正式名称)」を去年の出張の時に US に持って行きました

(残念なおまけ) Microsoft Win32 と Microsoft .NET Framework API との対応

https://msdn.microsoft.com/ja-jp/library/aa302340.aspx

GetDiskFreeSpaceEx

ディスクの空き容量など、指定されたディスクに関する情報を取得します。

System.Management.ManagementObject("Win32_LogicalDisk.DeviceID=\"C:\"").Get System.Management.ManagementObject.Properties

…なんてこったいです… orz