tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトルプラグアンドプレイでポートの開放、切断をしたい
記事No14729
投稿日: 2010/06/02(Wed) 14:37
投稿者たまご
使用言語:VB6
経験:半年
環境:windows2000
お世話になっております。
現在システム情報コントロールとMSCommを利用して
USB接続の仮想COMポートをプラグアンドプレイで認識したら
ポート開放、切断したらポートも切断するようなプログラムを
作りました。ですが、いざ動かすと意図しない現象が発生し原因が
さっぱりわからないので助言をいただきたいと思い質問しました。
現象としては
一回目の接続:イベント発生でポート開放
一回目の切断:イベント発生せず
二回目の接続:イベント発生せず、右下のタスクバーにハードウェア取り外しアイコンが表示
二回目の切断:イベント発生せず。アイコンは消える
三回目の接続:イベント発生せず。
三回目の切断:イベント発生でポート切断
四回目以降同じループ。
何回やってもイベントが発生しないならまだしもこのような現象は
どうして起こるのでしょうか。
希望する動作はやはり接続・切断されるたびにイベントが発生してほしいのです。
ポートを開放しなければこのような現象は現れません。
何か意見をいただけたら幸いです。よろしくお願いします。

※参考コード
Private Sub Form_Load()
MSComm1.CommPort = 3 'ポート番号設定
  ・・・ポート設定
End Sub

'接続イベント
Private Sub SysInfo1_DeviceArrival(ByVal DeviceType As Long, ByVal DeviceID As Long, ByVal _
DeviceName As String, ByVal DeviceData As Long)
        MSComm1.PortOpen = True  'ポートオープン
        Debug.Print "IN"; DeviceType 'イベント発生確認用


End Sub

'切断イベント
Private Sub SysInfo1_DeviceRemoveComplete(ByVal DeviceType As Long, ByVal DeviceID As Long, _
ByVal DeviceName As String, ByVal DeviceData As Long)
        MSComm1.PortOpen = False  'ポートクローズ
        Debug.Print "OUT "; DeviceType 'イベント発生確認用
    
End Sub

[ツリー表示へ]
タイトルRe: プラグアンドプレイでポートの開放、切断をしたい
記事No14732
投稿日: 2010/06/02(Wed) 20:03
投稿者K.J.K.
> 現在システム情報コントロールとMSCommを利用して
> USB接続の仮想COMポートをプラグアンドプレイで認識したら
> ポート開放、切断したらポートも切断するようなプログラムを
> 作りました。ですが、いざ動かすと意図しない現象が発生し原因が
> さっぱりわからないので助言をいただきたいと思い質問しました。

結構前のですが、
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=37850&forum=7&start=16
辺りに似たような事例がありますね。

> 何回やってもイベントが発生しないならまだしもこのような現象は
> どうして起こるのでしょうか。

推測ですけれども、USBで接続されたデバイスの検知と、仮想COMポートとしての
デバイスの検知が一致している理由がないからでしょう。USB機器が外されていても、
仮想COMポートがOpenされている以上は勝手には閉じないのでしょう。

で、抜き差ししてPortOpenは一切しない場合に、起きるイベントは一度ずつだけなの
でしょうか? イベント引数の DeviceID に異なる値が入ったイベントが発生している、
ということはないでしょうか?

もっとも手元のWindowsXP機でデバイスを抜き差ししてみたところ、起きて欲しかった
イベントが起きていないということもありましたので、真面目にやるのであれば、
直接 API を用いないといけないのかもしれません。

[ツリー表示へ]
タイトルRe^2: プラグアンドプレイでポートの開放、切断をしたい
記事No14734
投稿日: 2010/06/02(Wed) 22:25
投稿者たまご
KJKさん。回答ありがとうございます。

portOpenを行わなかった場合、USBを抜き差しすれば差すたびに一回、抜くたびに一回イベントが発生します。引数DeviceIDには意図した値が格納されることも確認しています。

USBで接続されたデバイスの検知と、仮想COMポートとしてのデバイスの検知が一致している理由がないということですが、一致させるということは可能でしょうか?

COMポートが開いた状態でUSBを抜いたときにVB側で何かしら検知させイベントを発生させる方法は他にないのでしょうか?
(たとえばデバイスマネージャーを見ながらUSBを抜き差しするとそのたびにデバイス名が表示/非表示をするのでこれを何かうまく利用できないかなと思ったりもしています;;WMIというものを使ってみましたが抜いた瞬間にイベントが発生させる方法が見つかりませんでした。)

[ツリー表示へ]
タイトルRe: プラグアンドプレイでポートの開放、切断をしたい
記事No14735
投稿日: 2010/06/03(Thu) 10:53
投稿者K.J.K.
> USBで接続されたデバイスの検知と、仮想COMポートとしてのデバイスの検知が一致している理由がないということですが、一致させるということは可能でしょうか?

別デバイス扱いだと思いますので無理でしょう。

> COMポートが開いた状態でUSBを抜いたときにVB側で何かしら検知させイベントを発生させる方法は他にないのでしょうか?

API 関数の RegisterDeviceNotification 辺りを使い、メッセージを受け取るべき
ウィンドウを登録することになると思います。第2引数の
DEV_BROADCAST_HDR.dbch_devicetype に DBT_DEVTYP_OEM を指定するケース辺りだと
みています。

[ツリー表示へ]
タイトルRe: プラグアンドプレイでポートの開放、切断をしたい
記事No14736
投稿日: 2010/06/03(Thu) 12:07
投稿者K.J.K.
> API 関数の RegisterDeviceNotification 辺りを使い、メッセージを受け取るべき
> ウィンドウを登録することになると思います。第2引数の
> DEV_BROADCAST_HDR.dbch_devicetype に DBT_DEVTYP_OEM を指定するケース辺りだと
> みています。

USBデバイスだと、DBT_DEVTYP_DEVICEINTERFACE で、
DEV_BROADCAST_DEVICEINTERFACE.dbcc_classguid に GUID_DEVINTERFACE_USB_DEVICE
辺りでしょうか。

[ツリー表示へ]
タイトルRe^2: プラグアンドプレイでポートの開放、切断をしたい
記事No14737
投稿日: 2010/06/03(Thu) 15:35
投稿者たまご
KJKさん。回答ありがとうございます。

> API 関数の RegisterDeviceNotification 辺りを使い、メッセージを受け取るべき
> ウィンドウを登録することになると思います。第2引数の
> DEV_BROADCAST_HDR.dbch_devicetype に DBT_DEVTYP_OEM を指定するケース辺りだと
> みています。

わかりました。これらについて調べて勉強したいと思います。
知識不足で的外れな質問だったら申し訳ないのですが、このAPI関数はVB6に実装することはできるものなのでしょうか?

[ツリー表示へ]
タイトルRe: プラグアンドプレイでポートの開放、切断をしたい
記事No14738
投稿日: 2010/06/03(Thu) 18:50
投稿者K.J.K.
> 知識不足で的外れな質問だったら申し訳ないのですが、このAPI関数はVB6に実装することはできるものなのでしょうか?

アプリケーションをプログラミングする側からみたら、API関数は利用するものであって
実装するものではありませんよ。

[ツリー表示へ]
タイトルRe: プラグアンドプレイでポートの開放、切断をしたい
記事No14739
投稿日: 2010/06/03(Thu) 20:05
投稿者K.J.K.
Windows 2000 なので確認ができませんが、とりあえず、SysInfo.ocx を
使い続けるのであれば、こういう感じかも。

Private Type UUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0& To 7&) As Byte
End Type

Private Type DeviceBroadcastInterfaceWithoutName
    Size As Long
    DeviceType As Long
    Reserved As Long
    GUID As UUID
    DeviceName(0& To 3&) As Byte
End Type

Private Declare Sub RtlMoveMemory Lib "Kernel32.dll" _
(ByRef Dest As Any, ByRef Src As Any, _
Optional ByVal Length As Long = 4&)

Private Declare Function RegisterDeviceNotification _
    Lib "User32.dll" Alias "RegisterDeviceNotificationA" _
(ByVal hRecipient As OLE_HANDLE, _
ByRef NotificationFilter As Any, _
Optional ByVal Flags As Long = 0&) As OLE_HANDLE
' Flags = 0& : DEVICE_NOTIFY_WINDOW_HANDLE

Private Declare Function UnregisterDeviceNotification Lib "User32.dll" _
(ByVal hDeviceNotification As OLE_HANDLE) As Long

Private mhDeviceNotification As OLE_HANDLE

Private Function SysInfoRegisterForUSB(ByVal SysInfoControl As SysInfo) As OLE_HANDLE
    Dim uFilter As DeviceBroadcastInterfaceWithoutName
    Dim hWndSysInfo As OLE_HANDLE
    Dim fVersion As Single
    Dim iFlag As Long

    If SysInfoControl Is Nothing Then Exit Function

    fVersion = SysInfoControl.OSVersion

    With uFilter
        .Size = LenB(uFilter)
        .DeviceType = &H5& ' DBT_DEVTYP_DEVICEINTERFACE
    End With

    If fVersion < 5.01 Then
    ' GUID_DEVINTERFACE_USB_DEVICE
        With uFilter.GUID
            .Data1 = &HA5DCBF10
            .Data2 = &H6530
            .Data3 = &H11D2
            .Data4(0&) = &H90
            .Data4(1&) = &H1F
            .Data4(2&) = &H0
            .Data4(3&) = &HC0
            .Data4(4&) = &H4F
            .Data4(5&) = &HB9
            .Data4(6&) = &H51
            .Data4(7&) = &HED
        End With
        iFlag = 0&
    Else
        iFlag = 4&
    End If
    ' Offset = 208&

    Call RtlMoveMemory(hWndSysInfo, ByVal (ObjPtr(SysInfoControl.Object) + 208&))
    SysInfoRegisterForUSB = RegisterDeviceNotification(hWndSysInfo, uFilter, iFlag)
End Function

Private Function SysInfoUnregister(ByRef hDeviceNotification As OLE_HANDLE) As Boolean
    If 0& = hDeviceNotification Then
        SysInfoUnregister = True
        Exit Function
    End If
    If UnregisterDeviceNotification(hDeviceNotification) Then
        hDeviceNotification = 0&
        SysInfoUnregister = True
    End If
End Function

' SysInfo コントロールの名前は moSysInfo であると仮定しています。
Private Sub Form_Load()
    mhDeviceNotification = SysInfoRegisterForUSB(moSysInfo)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Call SysInfoUnregister(mhDeviceNotification)
End Sub

[ツリー表示へ]
タイトルRe^2: プラグアンドプレイでポートの開放、切断をしたい
記事No14741
投稿日: 2010/06/03(Thu) 21:24
投稿者たまご
KJKさん。サンプルコードまで載せていただけて大変感謝いたします!
このコードを理解しつつうまくプラグアンドプレイを認識させるよう努力します。
本当にありがとうございました。
また行き詰ったら質問させていただきますのでよろしくおねがいします。;;;

[ツリー表示へ]
タイトルRe: プラグアンドプレイでポートの開放、切断をしたい
記事No14742
投稿日: 2010/06/03(Thu) 21:50
投稿者K.J.K.
> このコードを理解しつつうまくプラグアンドプレイを認識させるよう努力します。

動くかどうかは確認していません。ですから、まだ結論を出すのは早すぎます。

まず提示したコードですが、こちらの環境下では XP 以降で使える機能を
利用するようにしています。というのも提示した GUID: GUID_DEVINTERFACE_USB_DEVICE
では RegisterDeviceNotification の呼び出しに失敗したからです。

これで上手く行く環境ならばそれでもいいのですが、失敗した場合には
どこを換えていくのかも考えないといけません。

まず、上記コードでうまく行った場合は、USB デバイスを抜き差しすると発生する
イベントの DeviceArrival や DeviceRemoveComplete の発生回数が増えます。
おそらくそのときは、イベントプロシージャの第一引数の DeviceType が 5 に
なっていると思います。これが USB デバイスそのものの抜き差しをあらわします。

しかし上手く行かない場合もあり得ます。API関数の RegisterDeviceNotification
の戻り値が 0 の場合です。こうなると別の GUID を探して指定しなければなりません。

目視で探すのであれば、regedit を起動して、
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB
より2段階下のキーを一つずつ調べて、該当すると思われるデバイスを "DeviceDisc"
フィールドから探して、それらしいものが見つかったらそのときの "ClassGUID" の値を
記録し、その GUID を With uFilter.GUID 以下で指定してください。

[ツリー表示へ]
タイトルRe: プラグアンドプレイでポートの開放、切断をしたい
記事No14744
投稿日: 2010/06/04(Fri) 20:12
投稿者K.J.K.
> Private Type DeviceBroadcastInterfaceWithoutName
>     Size As Long
>     DeviceType As Long
>     Reserved As Long
>     GUID As UUID
>     DeviceName(0& To 3&) As Byte
> End Type

DeviceName(0& To 255&) As Byte
に変更してください。

[ツリー表示へ]