鍋あり谷あり

テーマを決めずに適当に書いています。

ドライブが使えるかどうか調べる

USB経由でメモリカードリーダーがつながっていて、しかもメモリが刺さっていたらなんかする、というプログラムを ruby で書こうと思い。
ドライブが存在して、そのドライブにメモリカードが刺さっている状態なら黙って真を返す関数というのがあるといいなと思ったんだが、見あたらなかった。
ドライブの 存在/非在 とはちょっと違うので、FileTest.directory? とか FileTest.exist? は、この目的には合わない。
やっぱりドライブなどというものは存在しない世界が主たるターゲットなので、そんなものはなさそうだ(見落としてる?)
というわけで、書いてみた:

require 'Win32API'

module FileTest
  GetVolumeInformation = Win32API.new( 'Kernel32.dll', 'GetVolumeInformationA', 'ppippppi', 'n' )
end

def FileTest.drive_ready?( path )
  if /^([a-z]+\:)/oi===path
    args=[ # BOOL GetVolumeInformation(
      $1+'\\', #   LPCTSTR lpRootPathName,
      '\0'*1024, #   LPTSTR lpVolumeNameBuffer,
      1024, #   DWORD nVolumeNameSize,
      '\0'*4, #   LPDWORD lpVolumeSerialNumber,
      '\0'*4, #   LPDWORD lpMaximumComponentLength,
      '\0'*4, #   LPDWORD lpFileSystemFlags,
      '\0'*1024, #   LPTSTR lpFileSystemNameBuffer,
      1024#   DWORD nFileSystemNameSize
    ] # );
    0!=FileTest::GetVolumeInformation.call( *args )
  else
    raise "bad path <#{path}> in FileTest.drive_ready?"
  end
end

最初は GetFileAttribute だと思ったんだが、ディスクを刺せというプロンプトが出てしまう。
で。いろいろ試した結果。どうも GetVolumeInformation が正解らしい。引数がもっと少ない方が呼びやすいんだが、見あたらなかった。
手元で動かす気しかないつもりで書いたので、ドライブレター + コロン の形式しかサポートしてないし、決めうちで GetVolumeInformationA*1にしているし、いい加減な例外を投げている。バッファの長さも 1024 で決めうち。
製品に入れるのには不十分だが、手元で使うには十分なので、同じことで悩んでいる人のためにここにソースを書いてみた。
それにしても。
こういうときって、勝手に FileTest とかに入れちゃうのが筋なのか、それとも自分でモジュールを定義するのが筋なのか。
あまり気にせず入れちゃったけど。

*1:つまり、GetVolumeInformationWではなく