Eject your optical drives!
2010-12-15
        こんにちはこんにちは、1日仕事を休んだらすっかり土曜日気分になってしまったturuginaです。
さて、今回は、Windowsにおいて、記憶デバイス(HDDやCDROM等)の制御を行ってくれるAPIのwrapperを提供してくれるWin32API::Fileを使ったサンプルを紹介したいと思います。
というわけで、いきなりコードですが
# eject.pl
use strict;
use warnings;
use v5.10.0;
use Win32API::File qw ( :ALL );
my @drives = do {
  if ( @ARGV ) {
    # ドライブが引数で指定されていればそれを使う
    map uc, grep /^[abcdefghijklmnopqrstuvwxyz]:$/i, @ARGV;
  }
  else {
    # 指定されていなければ有効なドライブを全て列挙する。
    my $buf = ' ' x (4*26+1);
    GetLogicalDriveStrings(length($buf), $buf);
    # $bufの中身は "[ドライブレター]:\\\0[ドライブレター]:\\\0..."
    split /\\\0/, $buf;
  }
};
# 光学ドライブだけ取り出す
my @cddrives = grep { GetDriveType($_) == DRIVE_CDROM } @drives;
if ( !@cddrives ) {
  say "no CD/DVD drives!";
  exit 0;
}
for my $d ( @cddrives ) {
  # ドライブのハンドルを取得
  my $h = createFile("//./$d", 'r', 'r');
  if ( !$h ) {
    say $^E, " [$d]";
    next;
  }
  # イジェクト!
  if ( DeviceIoControl( $h, IOCTL_STORAGE_EJECT_MEDIA, [], [], [], [], [], [] ) ) {
    say "EJECT! [$d]";
  }
  else {
    say $^E, " [$d]";
  }
}
こんな感じで、引数に何も指定せずに実行すると全ての光学ドライブを、引数でドライブを指定するとそれが光学ドライブであればイジェクトしてまわります。
このスクリプトの使い道ですが、日常使いの他、ちょっとコードを書き足してメールを受信したらトレイを排出するとかTwitterでmentions_timelineやdirect messagesに新しいメッセージが来たらトレイを排出するとか、トレイの前にピタゴラ装置的な物をこしらえて楽しむとか、色々考えられますね!
ちなみに、Unix系OSにはejectコマンドというものがあって、これで光学ドライブやフロッピーなどのイジェクトを行えるようになっています。もし↑のスクリプトで光学ドライブ以外にイジェクトしたいものがあれば、GetDriveType APIの戻り値チェックでDRIVE_REMOVABLEも候補に入れてあげれば良いでしょう。(多分(ぇ
と、ここまで書いてきましたが、実は上記のスクリプト相当のものはSDLというモジュールをインストールすれば、
use strict;
use warnings;
use SDL;
use SDL::CDROM;
use SDL::CD;
SDL::init(SDL_INIT_CDROM);
my @cddrives = 0 .. SDL::CDROM::num_drives() - 1;
die "no available drives" if !@cddrives;
if ( @ARGV ) {
  my %m = map { SDL::CDROM::name($_) => $_ } @cddrives;
  @cddrives = grep defined,@m{@ARGV};
  die 'no available drives: choose ', join ', ',grep defined $m{$_},keys %m if !@cddrives;
}
SDL::CD->new($_)->eject for @cddrives
という風にとても短く書けます。 しかもWindows以外の環境でも動作するという優れもの!(白目
さて、綺麗に(?)オチがついたところで今回はこの辺で...
次はどなたでしょうかー。