WindowプロシージャでUSBデバイス抜き差しを検出【Wpf】

.Net FrameWork

はじめに

今回の記事は、WpfでシリアルポートにUSBデバイスを抜き差しした際にプログラムで検出する方法についてです。

例えば、良く利用されるものを挙げるのであればUSBメモリUSBカメラがあります。
以下ではシリアルポートにUSBメモリを抜き差しを行った時に検出出来るプログラムを作成します。

使うのは、Windowプロシージャです。

Windowプロシージャとは?

Windowsでは、利用しているアプリケーションにWindowsから送信されたメッセージを処理する関数のことをWindowプロシージャと言います。

C#の場合にWindowプロシージャを利用する関数

protected IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)

引数は4つです。
・hwndはWindowへのハンドル
・msgはメッセージコード
・wParamとIParamには、メッセージに関連する追加データが格納

ハンドルとは?

ハンドルとは、Windowsでオブジェクトを生成するとそのオブジェクトを識別するための用意される数値のこと(システム内ではユニーク)。

上記の引数に含まれるメッセージコードを確認します。
シリアルポートでデバイスの抜き差しが行われると、ここにメッセージコードがセットされます。

CheckPoint

デバイスの抜き差しが行われると、msg引数にWM_DEVICECHANGE = 0x00000219 がセットされます。

Windowプロシージャの使い方

一般的な利用方法は、第2引数メッセージコードをswitch文を使用してメッセージ毎に処理を分岐するように利用します。

switch (msg)
{
    case XXX: // 任意のメッセージ 

    // etc
}

参考にしたサイト

ウィンドウ プロシージャの記述 - Win32 apps
ウィンドウ プロシージャの記述

WpfでWindowプロシージャをフックする方法

フックとは、メッセージやマウスアクションなどのイベントをアプリケーションがインターセプトできるメカニズムのこと。

フックの概要-MSDM

実行アプリのハンドルを取得

まずは、実行するアプリのハンドルを取得します。

IntPtr hWnd = new WindowInteropHelper(System.Windows.Application.Current.MainWindow).EnsureHandle();

HwndSourceオブジェクトを取得してすべてのウィンドウ メッセージを受け取るイベント ハンドラーを追加

HwndSource source = HwndSource.FromHwnd(hWnd);
source.AddHook(new HwndSourceHook(WndProc));
HwndSource.FromHwnd(IntPtr) メソッド (System.Windows.Interop)
指定したウィンドウの HwndSource オブジェクトを返します。
HwndSource.AddHook(HwndSourceHook) メソッド (System.Windows.Interop)
すべてのウィンドウ メッセージを受け取るイベント ハンドラーを追加します。

UsbDeviceWatcherクラス

以下でUSBメモリを抜き差しを行った時に検出するクラス作成します。

using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;

//name space
namespace ScrennTransmittionSample.Model
{
    /// <summary>
    /// USBデバイス監視クラス
    /// </summary>
    public class UsbDeviceWatcher
    {
        /// <summary>
        /// Windowメッセージ
        /// </summary>
        private enum WindowMessage
        {
            WM_DEVICECHANGE = 0x00000219
        }
        /// <summary>
        /// Windowハンドル
        /// </summary>
        IntPtr _hWnd;

        HwndSourceHook _hock;
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public UsbDeviceWatcher()
        {
            _hWnd = new WindowInteropHelper(System.Windows.Application.Current.MainWindow).EnsureHandle();
        }
        /// <summary>
        /// USBデバイス検出開始
        /// </summary>
        public void WatchStart()
        {
            HwndSource source = HwndSource.FromHwnd(_hWnd);
            _hock = new HwndSourceHook(WndProc);
            source.AddHook(_hock);
        }
        /// <summary>
        /// USBデバイス検出停止
        /// </summary>
        public void WatachStop()
        {
            if (_hock != null)
            {
                HwndSource source = HwndSource.FromHwnd(_hWnd);
                source.RemoveHook(_hock);
                _hock = null;
            }
        }
        /// <summary>
        /// Windowsからのメッセージを受け取るWindowsプロシージャ
        /// </summary>
        protected IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch ((WindowMessage)msg)
            {
                case WindowMessage.WM_DEVICECHANGE:
                    Task.Run(() => CheckUSBDevices());
                    break;
                default:
                    break;
            }
            return IntPtr.Zero;
        }
        /// <summary>
        /// static関数 USBデバイス検出
        /// </summary>
        public static void CheckUSBDevices()
        {
            int count = 0;
            using (var searcher = new System.Management.ManagementObjectSearcher(@"SELECT * FROM Win32_PnPEntity WHERE Service='USBSTOR'"))
            {
                System.Management.ManagementObjectCollection collection = searcher.Get();
                foreach (var device in collection)
                {
                    //取得したデバイスに対する処理
                    count++;
                }
                //その他処理を記述.
                // リソース解放.
                collection.Dispose();
            }
            /*それ以降の処理を記述*/
        }
    }
}
ポイント

デバイスオブジェクトを取得について

ManagementObjectSearcherクラスを使用します。
そこでクエリ文を使用します。
上のコード内だと@”SELECT * FROM Win32_PnPEntity WHERE Service=’USBSTOR'”
ここについてはデバイスマネージャー⇒利用したいオブジェクトのプロパティから情報を取得しています。(以下の画像を参考)

実行

//デバイス検出クラスインスタンス化
UsbDeviceWatcher watacher = new UsbDeviceWatcher();
//デバイス監視スタート
watacher.WatchStart();
//デバイス監視停止
watacher.WatachStop();

USBデバイスを差し込んだ時の実行結果。(※上記クラスには記載していませんが、メッセージボックスの処理を追加)

注意

上記クラスを実行した際に複数回メッセージが発生します。
これについては解決方法が不明だったため何かしらの対策が必要です。利用する際に問題がある場合はご注意下さい。

参考

デバイスのUSB接続状況を監視するアプリの作成 - Qiita
#はじめにWindowsを使っていると、時々USBデバイスを外した時の音が聞こえるようになりました。(ポロン↓、ポンポロロン↑)接続の音も聞こえているため、おそらくは接続が一瞬切れているのだと思…

コメント

タイトルとURLをコピーしました