Win7でUSBが外せない

解決


クラ  2012-09-13 22:03:53  No: 147788  IP: [192.*.*.*]

動作環境WinXP/Win7,VB2008

USBメモリを自動で取り外すコードを作成しています。
WinXPではうまく行きましたが、Win7ではうまく行きません。
解決策をどなたかご教授お願いします。
取り外すUSBはセキュアUSBメモリです。

処理手順は以下の通りです。

1.SetupDiGetClassDevs()を使って、デバイスを列挙。
2.GetDeviceIDを使ってDeviceIDを取得。
  ※後述しますがWin7の場合DeviceIDが正しく取得できていないようです。
3.DeviceIDがターゲットUSBメモリのDeviceIDと一致したら
  CM_Request_Device_EjectWを投げて、USBメモリを外す。

コードは以下の通り
  Dim GUID_DEVINTERFACE_VOLUME As Guid = 
New Guid(&H53F5630D, &HB6BF, &H11D0, &H94, &HF2, &H0, &HA0, &HC9, &H1E, &HFB, &H8B)

  Dim hDevInfo As Integer = SetupDiGetClassDevs(GUID_DEVINTERFACE_VOLUME, IntPtr.Zero, 0, DIGCF_PRESENT + DIGCF_DEVICEINTERFACE)

  If hDevInfo <> INVALID_HANDLE_VALUE Then
     Dim MemberIndex As Integer = 0
     Dim did As New SP_DEVINFO_DATA
     did.cbSize = Marshal.SizeOf(did)
     
     Dim didPtr As IntPtr = Marshal.AllocHGlobal(did.cbSize)
     Marshal.StructureToPtr(did, didPtr, True)
     While SetupDiEnumDeviceInfo(hDevInfo, MemberIndex, didPtr) <> 0
        Marshal.PtrToStructure(didPtr, did)
        Dim pDevInst As Integer
        Dim BusDeviceID As String = ""
        CM_Get_Parent(pDevInst, did.DevInst, 0)
        CM_Get_Parent(pDevInst, pDevInst, 0)
        BusDeviceID = GetDeviceID(pDevInst)

      //このときBusDeviceIDの取得値がWinXPとWin7で
      //違っている
      //WinXP:列挙した中にUSBメモリのID
      //      USB\VID_0411&PID_013D\0700078B0CAC1001
      //      が出てくる。
      //Win7:列挙したIDは全てHTREE\ROOT\0となっている。

      If BusDeviceID = "USB\VID_" & VID & "&PID_" & PID & _
         "\" & SN Then
           If CM_Request_Device_EjectW(pDevInst, IntPtr.Zero, _
              IntPtr.Zero, 0, 0) = 0 Then
              Res = 0
              Exit While
           Else
              Res = 1
              Exit While
           End If
     End If
            ・
            ・
            ・

編集 削除
オショウ  2012-09-13 22:37:48  No: 147789  IP: [192.*.*.*]

権限の問題です。

※  管理者権限付で実行したらどうなりますか?

以上。参考まで

編集 削除
クラ  2012-09-14 20:02:28  No: 147790  IP: [192.*.*.*]

不具合は管理者権限で実行した結果です。

GetDeviceID(pDevInst)で取得したデバイスIDが
WinXP(正)とWin7(誤)で違うのが原因のようです
が、なぜ違うのかがわかりません。

SetupDiEnumDeviceInfoで取得したdidPtrが正し
アドレスを差していないのでは?と思っています
がどう対処すればよいかわかりません。

  WinXPの場合:列挙した中にUSBメモリのデバイスID
              USB\VID_0411&PID_013D\0700078B0CAC1001
              が出てくる。

  Win7の場合  :列挙したIDは全てHTREE\ROOT\0となっている。
              ->このため、CM_Request_Device_EjectWが効か
                ない。

編集 削除
オショウ  2012-09-15 10:34:12  No: 147791  IP: [192.*.*.*]

Win7 も、32bitですよネ?

ソースコードの内容は未確認ですが・・・
自作やインターネットで拾ってきたコードでは、問題なく
イジェクトできています。

ガジェットでイジェクトするものも正常に動作しています
ので何でしょうかネ〜

以上。

編集 削除
クラ  2012-09-15 10:50:30  No: 147792  IP: [192.*.*.*]

32bitです。

オショウさん。支障なければ、問題なくイジェクト
できたコードを紹介していただければ助かります。

  フリーソフトUnhotplug.exe(conaさん作)を呼び出し
てイジェクトするコードも作ってみましたが、これは
問題ありませんでした。
  但し、外部アプリでイジェクトするのでなく、コード
を実装してイジェクトしたいと考えています。

編集 削除
オショウ  2012-09-15 11:06:58  No: 147793  IP: [192.*.*.*]

SetupDiGetClassDevs は、そのPCのドライブ数分、呼び出している
動作になっていますか?
列挙するので、該当するGUID数分ループする動作となりますので、そ
の一部のコードでは何とも・・・

イジェクトするにはいくつかの方法がありますので、あなたの方法と
は違う方法なので、どこがとは指摘できませんが、CM_Get_Parent を
2回行っている部分で、正しく動作しているのかご確認下さい。

以上。参考まで

編集 削除
クラ  2012-09-15 12:55:05  No: 147794  IP: [192.*.*.*]

>SetupDiGetClassDevs は、そのPCのドライブ数分、
>呼び出している動作になっていますか?
Dim GUID_DEVINTERFACE_VOLUME As Guid = New Guid(&H53F5630D, &HB6BF, &H11D0, &H94, &HF2, &H0, &HA0, &HC9, &H1E, &HFB, &H8B)
Dim hDevInfo As Integer = SetupDiGetClassDevs(GUID_DEVINTERFACE_VOLUME, IntPtr.Zero, 0, DIGCF_PRESENT + DIGCF_DEVICEINTERFACE)

でhDevInfoを取得して
While SetupDiEnumDeviceInfo(hDevInfo, MemberIndex, didPtr) <> 0
で0になるまで繰り返しています。

Whileループ時にレジストリをみるとWin7は別のキーを見ているようです。

<WinXPの場合>
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClasses
\{53F5630DHB6BF11D094F20A0C91EFB8B}を見ている感じ
(IDの戻りはUSB\VID_0411&PID_013D\0700078B0CAC1001など
ドライブのインスタンスIDが帰ってくる)

<Win7の場合>
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Enumを見ている
感じ(IDの戻りはHTREE\ROOT\0のみ)

>CM_Get_Parent を
>2回行っている部分で、正しく動作しているのかご確認下さい。
連休のため、手元にWin7がありません。
連休明けに確認します。

何がしかの値が返ってきていたのは確認しましたが、
この値が正しいかどうかはどこを見て確認すればよい
でしょうか?

編集 削除
オショウ  2012-09-15 14:10:48  No: 147795  IP: [192.*.*.*]

http://www.codeproject.com/Articles/13530/Eject-USB-disks-using-C
http://winofsql.jp/VA003334/usb080803163310.htm

それらで研究されては?

以上。参考まで

編集 削除
クラ  2012-09-15 18:09:09  No: 147796  IP: [192.*.*.*]

>http://www.codeproject.com/Articles/13530/Eject-USB-disks-using-C
>http://winofsql.jp/VA003334/usb080803163310.htm
オショウさん紹介ありがとうございます。

紹介があったコードも、以前、demo版UsbEject.exeを
ネットから落として確認してみました。その時は実行
時にエラーが発生したので使用を諦めた気がします。

来週、Win7 PCにC#の開発環境を入れて、デバッグ環境
で状況を調べてみます。

編集 削除
オショウ  2012-09-15 20:48:34  No: 147797  IP: [192.*.*.*]

Win7 32bit で、そのソースコードをリコンパイルして
正常に動作してます。管理者権限いるけど・・・

以上。

編集 削除
クラ  2012-09-16 09:58:49  No: 147798  IP: [192.*.*.*]

>正常に動作してます。管理者権限いるけど・・・
オショウさん了解。
連休中で手元にWin7がないので来週早速
やってみます。

編集 削除
クラ  2012-09-18 19:56:14  No: 147799  IP: [192.*.*.*]

>Win7 32bit で、そのソースコードをリコンパイルして
>正常に動作してます。管理者権限いるけど・・・
紹介があったUsbEject.slnをWin7 32bitでリコンパイルして、
デバッグモードで実行してみましたが、「アクセス拒否」エ
ラーがでてうまくいきませんでした。

そこで、管理者権限で問い合わせがあります。

アドミニ権限のユーザーアカウントで実行しましたが、

Volume.cs 185行
 throw new Win32Exception(Marshal.GetLastWin32Error());

で"アクセスが拒否されました。"のエラーがでています。

ユーザーアカウントの権限設定以外に何か設定が必要ですか?

ネットで調べると、メッセージによっては予めセキュリティ特権
を有効にするコードを追加する必要があるようですが。。。。  

☆C#の話になってしましましたが、上手く言った後VBに移植します。

編集 削除
オショウ  2012-09-18 20:34:00  No: 147800  IP: [192.*.*.*]

Volume.cs 185行?
そんなに行数ありませんが・・・改行コードの変換のダイアログが
出ませんでしたか?

因みに、何も変更せず、app.manifest を追加し、
<requestedExecutionLevel level="asInvokerr" uiAccess="false" />
を、
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
に変更しただけです。(アイコンとマニフェストの部分も変更)

因みに、そのPCにログインする際のユーザーは、管理者権限を
有せない設定のユーザーですか?
もしくはActiveDirectoryに参加しているPCですか?

Admin権限を持っているユーザーでも、管理者権限はUACダイ
アログで有効にしないと、管理者権限で実行されないので注意
が必要です。

以上。

編集 削除
クラ  2012-09-19 19:37:56  No: 147801  IP: [192.*.*.*]

><requestedExecutionLevel level="requireAdministrator" >uiAccess="false" >/>
>に変更しただけです。(アイコンとマニフェストの部分も変更)
この設定にC#デバッグ環境を変更したら上手く動作しました。
(USBが外せました)
時間がかかりそうですが、早速、VBへ移植します。

>Admin権限を持っているユーザーでも、管理者権限はUACダイ
>アログで有効にしないと、管理者権限で実行されないので注意
>が必要です。
先週からWin7用のプログラムを作り始めましたが、WinXPの感覚
でいました。アドミニでログインしても、WinXPとは違い一部の
権限は制限がかかっているのですね。

コードはC#ですがWinXP・Win7でもUSBが外せるコードが見つかっ
たので、VB移植を頑張ってみます。
このスレッドは「解決」です。

オショウさんありがとうございました。

編集 削除