C#でドライブを指定してUSBメモリを取り出す【失敗談】

ネットの掲示板に挙がっていた質問。

Q.「C#でUSBメモリのドライブを指定して取り出し(Eject)するにはどうしたらよいか?」

実際の操作は物理ディスクに対して行うのですが、指定するのは論理ディスクなのでどうやって対応付けしたらよいのか??回答を追いながら実際にコードを書いてみます、ただしタイトルが【失敗談】であることに十分注意いただきたい。

単純なWindows Formsで、見た目はこんな感じで行きます。

eject_form

UsbMemEjectForm.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Management;

namespace UsbMemEject
{
    public partial class UsbMemEjectForm : Form
    {
        private Dictionary<string, ManagementObject> recognizedDevices = new Dictionary<string, ManagementObject>();
        public UsbMemEjectForm()
        {
            InitializeComponent();
        }

        private void buttonUpdate_Click(object sender, EventArgs e)
        {
            comboBoxDrives.Items.Clear();
            recognizedDevices.Clear();

            // Win32_LogicalDisk
            var logicalDevices = new ManagementObjectSearcher(
                @"root\CIMV2", @"SELECT * FROM Win32_LogicalDisk").Get();
            foreach (ManagementObject logicalDevice in logicalDevices)
            {
                string logicalID = logicalDevice["DeviceID"].ToString();
                recognizedDevices[logicalID] = logicalDevice;
                comboBoxDrives.Invoke(
                    (MethodInvoker)delegate { comboBoxDrives.Items.Add(logicalID); }
                );
            }
        }

        private void UpdateDevices(object stateInfo)
        {
            comboBoxDrives.Invoke(
                (MethodInvoker)delegate { comboBoxDrives.Items.Clear(); }
            );
            recognizedDevices.Clear();

            // Win32_LogicalDisk
            var logicalDevices = new ManagementObjectSearcher(
                @"root\CIMV2", @"SELECT * FROM Win32_LogicalDisk").Get();
            foreach (ManagementObject logicalDevice in logicalDevices)
            {
                string logicalID = logicalDevice["DeviceID"].ToString();
                recognizedDevices[logicalID] = logicalDevice;
                comboBoxDrives.Invoke(
                    (MethodInvoker)delegate { comboBoxDrives.Items.Add(logicalID); }
                );
            }
        }

        private void buttonEject_Click(object sender, EventArgs e)
        {
            var logicalDevice = recognizedDevices[(string)comboBoxDrives.SelectedItem];

            // Win32_DiskPartition
            string logicalID = logicalDevice["DeviceID"].ToString();
            var logToDisks = new ManagementObjectSearcher(
                @"ASSOCIATORS OF {Win32_LogicalDisk.DeviceID='" + logicalID +
                @"'} WHERE ResultClass=Win32_DiskPartition").Get();
            foreach (ManagementObject logToDisk in logToDisks)
            {
                // Win32_DiskDrive
                string diskID = logToDisk["DeviceID"].ToString();
                var diskDrives = new ManagementObjectSearcher(
                    @"ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + diskID +
                    @"'} WHERE ResultClass=Win32_DiskDrive").Get();
                foreach (ManagementObject diskDrive in diskDrives)
                {
                    Eject(diskDrive);
                }
            }
        }

        private void Eject(ManagementObject diskDrive)
        {
            // ???
        }
    }
}

まず、Updateボタンを押すと論理ディスクを列挙しています。単純なドライブの列挙だけならSystem.Environment.GetLogicalDrives()で十分ですが、ここではManagementObjectを保持するためにWMIのWQLを使ってこのようにしています。

コンボボックスにドライブが追加されたら、Eject対象のドライブを選択してEjectボタンを押す。すると、再びWMIを使って [論理ドライブ] → [物理パーティション] → [物理ディスク] と対応付けて物理ディスクを取得できます。”DeviceID”を引き回して次のクエリに渡しているのがミソですね。

さぁあとはEjectするだけ!なんですが、コレできません。

Ejectするにはsetupapi.dll にある CM_Request_Device_Ejectを呼び出す必要があります。この関数の第1引数にデバイスインスタンスハンドルを渡すのですがこのハンドルがどうにも取得できないのです。散々調べてみましたがダメでした。

CODE PROJECTで同じ話題のライブラリがありましたが、これも結局CM_Request_Device_Ejectを呼ぶためにその前から色々DllImportしてました。確かにC#なんだけど、System.Management名前空間はつかってなかったようです。

System.Management名前空間はCPUの状態やアプリケーションアンインストール、USBデバイスシリアル取得など色々できるみたいですが、Ejectだけはダメだったぽいです。WMIのコードを吐き出してくれるツールも探しましたが、USBのEjectはないぽいですね。残念。

でも、論理ディスク→物理ディスクへの対応付けは勉強になりました☆

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中