背景
ノートPCのタッチパッドの調子が悪く、
勝手にタッチの判定がされるなどの不具合事象が起きる端末がありました。
この状態になると、GUIでポインタを操作してパッドをオフにしてという操作をすることも中々難しくなる。
勿論マウスを持っていればいいですが、自分はマウスは持ち歩かないタイプの人なのです...
Winodowsさんは中々優秀で、ポインティングデバイスがなくてもキーボードだけで結構操作できてしまう。
それこそブラウザ操作もVimiumのようなプラグインを使えば全然操作できる(なんならマウスで操作するよりも快適)
ということで、こんな事象が起きたときに備えてコマンドでタッチパッドをOFFにできたらいいなということで、それ用のコマンドを作成しました。
事前まとめ
-
Get-PnpDevice
というコマンドを用いてデバイス情報を取得する -
Disable-PnpDevice
というコマンドを用いて対象のデバイスをオフにする(ONの場合はEnable-PnpDevice
) -
Disable-PnpDevice
やEnable-PnpDevice
を実行するには管理者権限で実行する必要がある。 -
Start-Process
を使い、一時的に管理者権限のプロセスを立ち上げその上でコマンドを実行させる必要がある。
対象のデバイスの情報を取得する。
今回では対象を「タッチパッド」とします。
以下のコマンドにより取得可能です。
Get-PnpDevice -FriendlyName "HID 準拠タッチ パッド"
Status Class FriendlyName InstanceId
------ ----- ------------ ----------
OK HIDClass HID 準拠タッチ パッド HID\DELL...
HIDは「Human Interface Device(ヒューマン・インターフェイス・デバイス)」の略です。
このFriendLyNameはWindowsに備わっている標準ドライバのひとつと思われるので、どのWindows環境でも同様と感がます(少なくとも私の別端末でも同様の名前で取得することができた)
必要となる操作
以下、タッチパッドのON/OFFを操作するためには、どのような操作を必要とするのか、
ひとつずつ見ていきます。
対象のデバイスを無効にする
Get-PnpDevice -FriendlyName "HID 準拠タッチ パッド" | Disable-PnpDevice
発生場所 行:1 文字:47
+ Get-PnpDevice -FriendlyName "HID 準拠タッチ パッド" | Disable-PnpDevice
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Win32_PnPEntity...E10349&0&0001"):ROOT\cimv2\Win32_PnPEntity) [Disable-PnpDevice], CimException
+ FullyQualifiedErrorId : HRESULT 0x80041001,Disable-PnpDevice
この情報だけだと何が要因でエラーが起きているのかを示す程に十分な情報がありませんが、
同様のコマンドを管理者権限のプロセス上で実行すると問題なく実行することができました。
Get-PnpDevice -FriendlyName "HID 準拠タッチ パッド" | Disable-PnpDevice
確認
この操作を実行しますか?
対象 "Win32_PnPEntity: HID 準拠タッチ パッド (DeviceID = "HID\XXXXXXXXXXXX\XXXXXXXXXXX")" に対して操作
"Disable" を実行しています。
[Y] はい(Y) [A] すべて続行(A) [N] いいえ(N) [L] すべて無視(L) [S] 中断(S) [?] ヘルプ (既定値は "Y"):
実行確認のダイアログメッセージが表示されました。
課題と対策
上記の操作をコマンドとしてひとまとめにするには、
以下のような仕様を盛り込む必要がありそうです。
- コマンド実行時に自動で管理者権限に移行し対象の操作を行う。
- 実行確認のダイアログをスキップさせる
PowerShellでsudoのように一時的に管理者権限に移行して実行する
Unix系の環境であれば、sudo
やsu -c
などが思い浮かびますが、
これをPowerShell上で実現するにはどうすればいいのか?
以下の方法で近しいことが可能です。
Start-Process powershell.exe -Verb runAs -ArgumentList ("コマンド")
Start-Process
を用いているように、
PowerShell自体にはsu
やsudo
と言った直接的に管理者権限に移行できるコマンドは無い模様。
上記のコマンドで行っていることは、新たなプロセスとして管理者権限でPowerShellのプロセスを立ち上げて、-ArgumentList
にコマンドを渡すことで、結果的にpowershell.exeが引数の文字列(コマンド)を解釈して実行するという流れになっています。
この時、管理者権限で実行されたシェルが新規ウィンドウとして立ち上がるため、Unix系に慣れている人からすると違和感が覚えると思います...
実行確認のダイアログをスキップさせる
こちらは簡単。
Disable-PnpDevice
に以下のオプションを付与することで実現可能です。
Disable-PnpDevice -Confirm:$false
コマンドを作る
以下作成したコマンドです。
無効にするコマンドと有効にするコマンドで2つ作っています。
ソースコード
タッチパッドを無効にするコマンド(Disable-TouchPad)
function Disable-TouchPad(){
#状態判定処理
$obj = Get-PnpDevice -FriendlyName "HID?準拠タッチ?パッド"
if($obj.Status -eq "Error"){
Write-Host "タッチパッドは既に無効です。"
return
}
#ArgumentListに渡すコマンドを事前に定義。
$command1 = 'Get-PnpDevice -FriendlyName "HID?準拠タッチ?パッド" '
$command2 = 'Disable-PnpDevice -Confirm:$false'
Start-Process powershell.exe -Verb runAs -ArgumentList (" $command1 | $command2 ")
#処理結果判定
$status = (Get-PnpDevice -FriendlyName "HID?準拠タッチ?パッド").Status
$limit = 10
#$count = 0
while($true){
#状態判定
if(!($Status -eq "Error")){
Start-Sleep -Seconds 0.5
$status = (Get-PnpDevice -FriendlyName "HID?準拠タッチ?パッド").Status
$count += 0.5
#$count
}else{
Write-Host "Disable-TouchPad:Success"
return
}
#ループbreak判定
if($count -eq $limit){
Write-Host "Disable-TouchPad:failure"
throw "Error:Coudn't Disable TouchPad"
}
}
}
タッチパッドを無効にするコマンド(Enable-TouchPad)
function Enable-TouchPad() {
#状態判定処理
$obj = Get-PnpDevice -FriendlyName "HID?準拠タッチ?パッド"
if($obj.Status -eq "OK"){
Write-Host "タッチパッドは既に有効です。"
return
}
#ArgumentListに渡すコマンド事前に定義
$command1 = 'Get-PnpDevice -FriendlyName "HID?準拠タッチ?パッド" '
$command2 = 'Enable-PnpDevice -Confirm:$false'
#有効化処理
Start-Process powershell.exe -Verb runAs -ArgumentList (" $command1 | $command2 ")
#処理結果判定
$status = (Get-PnpDevice -FriendlyName "HID?準拠タッチ?パッド").Status
$limit = 10
while($true){
#状態判定
if(!($Status -eq "OK")){
Start-Sleep -Seconds 0.5
$status = (Get-PnpDevice -FriendlyName "HID?準拠タッチ?パッド").Status
$count += 0.5
#$count
}else{
Write-Host "Enable-TouchPad:Success"
return
}
#ループbreak判定
if($count -eq $limit){
Write-Host "Enable-TouchPad:failure"
throw "Error:Coudn't Enable TouchPad"
}
}
}
作成時の備忘
-ArgumentListが思うように展開されない問題
ソースコード内では-FriendlyName
の指定を"HID?準拠タッチ?パッド"
としている。
これは文字列リテラル内にスペースが存在すると問題が生じたためでだ。
例えば以下のコードはコマンドがうまく通らない
$command = 'Get-PnpDevice -FriendlyName "HID 準拠タッチ パッド" | Disable-PnpDevice; pause'
Start-Process powershell.exe -Verb runAs -ArgumentList ($command)
Get-PnpDevice : 引数 '準拠タッチ' を受け入れる位置指定パラメータ
ーが見つかりません。
発生場所 行:1 文字:1
+ Get-PnpDevice -FriendlyName HID 準拠タッチ パッド | Disable-Pnp
Device; pause
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-PnpDevice]、ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Get-Pn
pDevice
続行するには、Enter キーを押してください...:
「準拠タッチ
を受け入れる位置指定パラメータがみつからない」というエラー内容から、
本来文字列リテラルとしてひとまとめで渡されるべき値がスペース区切りでそれぞれ引き渡されているということが推察できる。
つまり、以下のような形で渡されていると考えられる。
Get-PnpDevice -FriendlyName HID 準拠タッチ パッド
#-FriendlyName:HID
#args1:準拠タッチ
#args2:パッド
この対策としてスペースを作らないように書き換えたのが以下だ
$command1 = 'Get-PnpDevice -FriendlyName "HID?準拠タッチ?パッド" '
また以下のように全体をダブルクォーテーションで囲み、-FriendLyName
のパラメータをシングルクォーテーションで囲む方法でも可能だった。
$command = "Get-PnpDevice -FriendlyName 'HID 準拠タッチ パッド' | Disable-PnpDevice; pause"
これに関してはシングルクォーテーションとダブルクォーテーションの展開の仕様に違いが起因していることは間違いないが、明快な説明ができるほどあまりよくわかっていない。
ただ、シングルクォーテーションの方が、厳格な文字列リテラル表記となるため、恐らくダブルクォーテーションでは-ArumentList
のパラメータとして渡した値が展開され、結果クォーテーションが外れてしまうのだと考えられる。
状態判定が早すぎると失敗する
コードの中で切り替え後にステータスが変わっているか判定する処理を入れている。
先のコードでは、while文により無限ループを作り、$limit
で定義した時間まで判定処理を継続させるように作ってある。
元々はシンプルに実行後にif文で判定を処理を行うだけだったのだが、このやり方では必ず判定処理で失敗した。
ただし実際にはタッチパッドの有効/無効の切り替えは問題なく完了されているのであった。
結果、原因はステータスの反映までに時間が掛かるためであった。
0.5秒間隔で判定処理を行っているが、自分の環境では5回(2.5秒)分まで目的のステータスに遷移しなかった。
-ArgumentList内で指定するコマンドを変数に格納可能。さらにパイプで繋ぐことも可能。
これはtipsのような備忘
-ArgumentList
のパラメータとして長々とコマンドラインを記述するのはかなり可読性を低下させるため、
事前に変数に格納して実行する仕様にした。
このときInvoke-Expression
を使わずともそのまま実行可能であった。
また複数のコマンドをパイプ処理させることも可能であった。
留意点
このコマンドはOSからデバイスを外しているようなイメージ
Windowsの設定内のタッチパッドの項目にはタッチパッドのオン/オフをスイッチすることができるが、
これをコマンドで実現している訳ではない。
設定からのON/OFFの場合はあくまでデバイス自体は有効化されたまま、タッチパッドの機能をオフにしている。
CLI上でデバイスを有効化させたまま、タッチパッドだけをOFFにする操作が調べても見当たらなかったため、
デバイス自体を無効化する手段を取った。
余談
Get-PnpDeviceで取得できるデバイスはタッチパッドに限らないため、
他のデバイスを付け外しさせる際にも汎用的に使えるはず。
※PnP⇒プラグアンドプレイの略と考えられるため、このコマンドレットで取得できるデバイスはOS動作中に外しても問題ないデバイスのはずだ。
参考