トップ回答者
特定のUSBメモリを、イジェクトするには?

質問
-
次のURLを参考に、特定のUSBメモリをイジェクトする方法を模索しています。
http://winofsql.jp/VA003334/infoboard.php?mid=usb&id=080803163310&pid=1
「device.LogicalDrive には、"E:" という形のドライブレターが入っています」
ということですから、'device.LogicalDrive'に"J:"を代入すればいいのでは? と遣って見ましたがなんら反応はありませんでした。
特定のUSBメモリを、イジェクトするにはどうしたら善いですか?
回答
-
ちょっとUSBデバイスを入手できたので、複数での動作確認を行ないました。
結果からすると、ライブラリから取得できるデバイス情報のドライブ番号「device.LogicalDrive」は、
正しい値が入っていません。
私の環境では、USBスティックメモリと、IPodの2つを接続して確認したところ、
マイコンピュータでの表示
IPod(G:)
USB DISC(H:)
ライブラリ配布元のサンプルプログラムでの表示
USB DISC(G:)
IPod(H:)
ライブラリの「device.LogicalDrive」では正しい値が取得出来ていないと思われます。
問題の回避策としては、ドライブ番号で比較して切断するのではなく、
ライブラリのデバイス情報の文字列と比較することが簡単と思います。
ただし、あくまで切断したいUSBデバイスのデバイス情報を、あらかじめわかっている
という前提にはなります。
また、同一のUSBスティックを2本接続してある場合等は、正常に動作しないと思います。
つまり、接続されているUSBデバイス情報で同一のものがない、という前提です。
例えば、USBメモリは、ライブラリのデバイス情報から、以下の文字列が取得できました。
・デバイス説明(device.Description)・・・汎用ボリューム
・ディスク説明(disk.Description)・・・ディスク ドライブ
・ディスク名(disk.FriendlyName)・・・USB 2.0 Flash Disk USB Device
上記を利用して、以下のようにメソッドを変更しました。
/// <summary> /// USBデバイスの切断 /// </summary> /// <param name="deviceDesc">デバイス説明</param> /// <param name="discDesc">ディスク説明</param> /// <param name="discName">ディスク名</param> public static void usbEject(string deviceDesc, string discDesc, string discName) { VolumeDeviceClass volumeDeviceClass = new VolumeDeviceClass(); // 優先的に見つかったUSBデバイスを、Ejectする。 foreach (Volume device in volumeDeviceClass.Devices) { // USBデバイス以外はスキップ if (!device.IsUsb) continue; // デバイスの説明が一致しない場合はスキップ if (!deviceDesc.Equals(device.Description)) continue; // ディスクがない場合はスキップ if (device.Disks.Count == 0) continue; // ディスクの説明が一致しない場合はスキップ if (!discDesc.Equals(device.Disks.First().Description)) continue; // ディスクの名称が一致しない場合はスキップ if (!discName.Equals(device.Disks.First().FriendlyName)) continue; // 指定デバイスを切断 device.Eject(false); break; } }
しかしながら、やはり、ドライブ番号で切断できるのが理想と思います。
その場合、ライブラリ自体を解析して原因を特定する必要があるのではないでしょうか。
- 回答としてマーク taokato 2010年9月8日 8:12
すべての返信
-
-
honefai 様
お返事を戴きまして、有難うございます。
現状報告をいたしますと、まだ、旨く言っておりません。
御質問にありました「上記のような手順は踏んでいますか?」は、LoadItemsの中身と、<http://winofsql.jp/VA003334/infoboard.php?mid=usb&id=080803163310&pid=1>で紹介されているVBの構文と同じように見えます。
そういった意味では、サンプルコード<http://winofsql.jp/VA003334/infoboard.php?mid=usb&id=080803163310&pid=1>のメッセージ出力で何らかの操作をすればいいように思えますが、如何でしょうか。
<http://www.developerfusion.com/tools/convert/vb-to-csharp/>似て、VBをC#に変換したのが下記になります。
----------
using Microsoft.VisualBasic; using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; using UsbEject.Library; public class Form1 { private void Button1_Click(System.Object sender, System.EventArgs e) { VolumeDeviceClass volumeDeviceClass = new VolumeDeviceClass(); foreach (Volume device in volumeDeviceClass.Devices) { // is this volume on USB disks? if (!device.IsUsb) { continue; } // is this volume a logical disk? if ((device.LogicalDrive == null) || (device.LogicalDrive.Length == 0)) { continue; } MessageBox.Show(device.LogicalDrive); // allow Windows to display any relevant UI device.Eject(false); } } }
----------
if ((device.LogicalDrive == null) ~
から、MessageBoxの間に目的のドライブ名かを判断させてあげれば良い筈なんですが、
USBメモリが複数在ってた場合、思惑通りに検出してくれないようです。
何かよい案があったら、お知恵を貸して頂けませんか。
-
上記コードで問題なくデバイスを切断できました。
ただしUSBデバイスが手元に一つしかないので複数での確認は出来ていません。
ちなみに、以下はIsNullOrEmptyメソッドでよいと思います。
if ((device.LogicalDrive == null) || (device.LogicalDrive.Length == 0)) ↓ if (string.IsNullOrEmpty(device.LogicalDrive))
ちなみに、
>USBメモリが複数在ってた場合、思惑通りに検出してくれないようです。
とは、具体的にはどのようなことでしょうか。
デバッグで以下をチェックしてみてください。
1.接続しているUSBデバイスの数分、「MessageBox.Show(device.LogicalDrive);」まで
到達しているかどうか。
2.「MessageBox.Show(device.LogicalDrive);」で表示される内容はどのような内容に
なっているのか。
-
>デバッグで以下をチェックしてみてください。
>1.接続しているUSBデバイスの数分、「MessageBox.Show(device.LogicalDrive);」まで
>到達しているかどうか。
>2.「MessageBox.Show(device.LogicalDrive);」で表示される内容はどのような内容に
>なっているのか。
1.に関しては、到達しています。
2.は、USBデバイスが二つ(K: & L:)あった場合、アルファベット'A'に近いデバイスを表示します。
そこで、ソースコードを次のようにしてみました。
public static void usbEject(string dr)
{
VolumeDeviceClass volumeDeviceClass = new VolumeDeviceClass();// 優先的に見つかったUSBデバイスを、Ejectする。
foreach (Volume device in volumeDeviceClass.Devices)
{
// is this volume on USB disks?
if (!device.IsUsb)
continue;// is this volume a logical disk?
if (string.IsNullOrEmpty(device.LogicalDrive))
{
continue;
}// is this volume a remove Drive ?
if (device.LogicalDrive == dr)
{
MessageBox.Show(device.LogicalDrive);
// allow Windows to display any relevant UI
device.Eject(false);
}
}
}USBデバイスは、‘K’と‘L’の二つ存在したとして、‘L’をイジェクトしようとします。
.「MessageBox.Show(device.LogicalDrive);」では、‘L’を示しました。
デバイス‘L’をイジェクトし、再びそのデバイスをPCに差戻しました。
そして、上記を再実行させると今度は、‘K’と‘L’の両方とも「if (!device.IsUsb)」で認識されずに弾かれてしまいます。
今、分かっているのは、ここ迄です。
何かしらの初期化が必要でしょうか?
-
>何かしらの初期化が必要でしょうか?
配布元のサンプルコードを見てみましたが、特に何かしらの初期化は不要のようです。
「VolumeDeviceClass volumeDeviceClass = new VolumeDeviceClass();」
上記を行うことで、「volumeDeviceClass」にデバイス情報が格納されます。
逆に言えば、上記を行えば最新のデバイス情報が取得できるので、
USBを抜き差しした後では、必ずVolumeDeviceClassのインスタンスを作成する所から
コードを書いておく必要があります。
この件は、ご提示したコードを見る限り問題ないと思います。
サンプルコードでは、「WM_DEVICECHANGE」メッセージを受け取って再表示していました。
※WM_DEVICECHANGE・・・デバイスに変更が発生した際にFormで受け取れるメッセージ
ちなみにUSBデバイスが1つしかないので複数での検証は出来ませんが、
「device.Eject(false)」で切断した後で抜いて差して、再びプログラムで「device.Eject(false)」
を行ったところ、正しく切断できました。
そちらの環境では、一つのデバイスの場合でも認識されない現象が発生しますか?
また、認識されずに弾かれた後、少し時間をおいてもう一度試してみてもダメですか?
-
> そちらの環境では、一つのデバイスの場合でも認識されない現象が発生しますか?
正常動作します。
動作を観察している限りでは、取得されるドライブ名(device.LogicalDrive)はMessageBoxと同じ働きで、
何かを制御するものではなさそうです。
現に、ドライブが複数在った場合、「if (device.LogicalDrive == dr)」でドライブLのみをイジェクトする(MessageBoxの表示は、ドライブL)筈が
ドライブKをイジェクトされ、再び「usbEject(string dr)」をコールしたら、ドライブLをイジェクトする。(MessageBoxの表示は、ドライブL)
これらの事から、(device.LogicalDrive)の内容とは関係なく、片っ端からドライブを見つけた順にイジェクトするようです。
-
まとめると、以下のような現象ということでしょうか。
1.端末にはUSBデバイスを2つ接続し、「K」と「L」のドライブ番号が割当たっている。
2.「L」のUSBデバイスを切断するために、usbEject("L:")をコールする。
3.ForEachで、「K」ドライブは「if (device.LogicalDrive == dr)」判定でFalseとなりスキップされる。
4.ForEachで、「L」ドライブは「if (device.LogicalDrive == dr)」判定でTrueとなりMessageBoxで「L:」と表示される。
5.続いて「device.Eject(false)」が実行されてデバイスが切断されるが、切断されたのは「K」ドライブ。
6.再び切断したUSBデバイスを抜いて挿し直しusbEject("L:")をコールする。
7.「if (!device.IsUsb)」の判定で、2つのUSBデバイスは、USBデバイスでないと認識されてしまい次の処理へ進めない。
-
ちょっとUSBデバイスを入手できたので、複数での動作確認を行ないました。
結果からすると、ライブラリから取得できるデバイス情報のドライブ番号「device.LogicalDrive」は、
正しい値が入っていません。
私の環境では、USBスティックメモリと、IPodの2つを接続して確認したところ、
マイコンピュータでの表示
IPod(G:)
USB DISC(H:)
ライブラリ配布元のサンプルプログラムでの表示
USB DISC(G:)
IPod(H:)
ライブラリの「device.LogicalDrive」では正しい値が取得出来ていないと思われます。
問題の回避策としては、ドライブ番号で比較して切断するのではなく、
ライブラリのデバイス情報の文字列と比較することが簡単と思います。
ただし、あくまで切断したいUSBデバイスのデバイス情報を、あらかじめわかっている
という前提にはなります。
また、同一のUSBスティックを2本接続してある場合等は、正常に動作しないと思います。
つまり、接続されているUSBデバイス情報で同一のものがない、という前提です。
例えば、USBメモリは、ライブラリのデバイス情報から、以下の文字列が取得できました。
・デバイス説明(device.Description)・・・汎用ボリューム
・ディスク説明(disk.Description)・・・ディスク ドライブ
・ディスク名(disk.FriendlyName)・・・USB 2.0 Flash Disk USB Device
上記を利用して、以下のようにメソッドを変更しました。
/// <summary> /// USBデバイスの切断 /// </summary> /// <param name="deviceDesc">デバイス説明</param> /// <param name="discDesc">ディスク説明</param> /// <param name="discName">ディスク名</param> public static void usbEject(string deviceDesc, string discDesc, string discName) { VolumeDeviceClass volumeDeviceClass = new VolumeDeviceClass(); // 優先的に見つかったUSBデバイスを、Ejectする。 foreach (Volume device in volumeDeviceClass.Devices) { // USBデバイス以外はスキップ if (!device.IsUsb) continue; // デバイスの説明が一致しない場合はスキップ if (!deviceDesc.Equals(device.Description)) continue; // ディスクがない場合はスキップ if (device.Disks.Count == 0) continue; // ディスクの説明が一致しない場合はスキップ if (!discDesc.Equals(device.Disks.First().Description)) continue; // ディスクの名称が一致しない場合はスキップ if (!discName.Equals(device.Disks.First().FriendlyName)) continue; // 指定デバイスを切断 device.Eject(false); break; } }
しかしながら、やはり、ドライブ番号で切断できるのが理想と思います。
その場合、ライブラリ自体を解析して原因を特定する必要があるのではないでしょうか。
- 回答としてマーク taokato 2010年9月8日 8:12