HIDのフィルタドライバについて

HIDのフィルタドライバの理解が足りなかったので少し調べ直してみた。
なお、ここではHIDのゲームパッドに連射機能を追加するためにupper filterとして記述したフィルタドライバをインストールするものとする。


1. インストール
フィルタドライバを組み込むには、(a)個別のデバイスに追加する、(b)クラスに追加する、という方法がある。
フィルタドライバ用のレジストリエントリはREG_MULTI_SZ形式となっており、エントリUpperFiltersまたはLowerFiltersに複数のフィルタドライバを登録することができる。


(a)個別のデバイスに追加する場合、(ア)infファイルで代替ドライバのような形でインストールする、(イ)SetupDiSetDeviceRegistryProperty()を使う、という方法がある。
レジストリ領域としてはHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\以下を使用する。この領域は読み取り専用にロックされており、レジストリエディタでは変更できない。


(ア)はWDKのサンプルのfireflyが行っているやり方で、見かけ上ドライバが変更されるが、実際にはそのデバイスのUpperFiltersに追加が行われるだけであり、ドライバを通常のものに変更してもフィルタドライバは残る。
この方法は特定のデバイスだけにインストールしたい時に有効である(例えばデバイスを接続しておき、デバイスマネージャからそのデバイスだけにインストールすることができる)。infファイルにはFLG_ADDREG_TYPE_MULTI_SZ | FLG_ADDREG_APPEND(0x00010008)という値を使用してエントリの追記を行う。ただしinfファイルではREG_MULTI_SZの適切な削除(自分が追加した部分だけを取り除くこと)ができないので、アンインストール時には(イ)の方法を使う必要がある。


(イ)の方法では全てのデバイスを列挙できるので、インストールが必要なデバイスかどうかを個別に判定してインストールすることができる。また、SetupDiSetDeviceRegistryProperty()は追加・削除両方に利用することができる。


(b)クラスに追加するには、infファイルまたはインストールプログラムによりレジストリに追加する。レジストリ領域としてはHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\以下を使用する。この領域はロックされていないのでレジストリエディタで追加・削除することができる。


2. ドライバ形式
(この部分間違っている可能性あり)
通常のFDO(function device object)に対するフィルタドライバはWDM形式で記述すればよいが、HIDの場合は通常のタイミングでFDOが生成されないために、デバイスのフィルタドライバとして登録してもAddDeviceが呼び出されず、フィルタできない。そのため、非WDM形式のドライバを作成し(すなわち、sourcesのDRIVERTYPEを未定義にし、AddDevice関数も登録しない)、IoRegisterPlugPlayNotification()を使用してデバイスの開始・終了を監視する。(ドライバをWDM形式で作成していて、FindPlugInsとかいう謎のエラーが出て一日無駄にした。WDM形式かどうかでローダー処理が異なる模様)


3. フィルタ対象かどうかの判定
(HIDではなく)USBデバイスのエントリに対してフィルタを追加した場合、HIDとUSB両方がフィルタ対象に加わるようだ。このとき、もし片方だけフィルタしたければIoGetDeviceProperty()のDevicePropertyEnumeratorNameによりenum名がわかるので、wcscmpでHIDなのかUSBなのか比較すればよい。


4. 余談
一昨年作成した連射ドライバは、INFファイルの作り方が間違っており、UpperFiltersではなくドライバの置換そのものを行うというものだったが、「ドライバが必要でない」というものに対してはこの方法でもフィルタドライバのような動作が可能であるようだ。これは「ドライバが必要でない」というデバイスの場合は、全ての処理はPDOが行っておりFDOの処理が存在しないため、ドライバの置換=機能追加=フィルタドライバと等価、ということだと思われる。