リモートデスクトップ時に、バッチでサーバ側のPCをスリープさせる

リモートデスクトップとかやっていると、WoL で遠隔起動はできるのに、コマンドプロンプトにも PowerShell にも、(休止じゃなくて)スリープができるコマンドがなくて困る。
そんなときに、Windows7 以降で標準搭載している PowerShwll を使って、Win32API を直呼び出しして、休止する方法をご紹介。

使い方はとっても簡単。
下記の一行を hoge.bat fuga.cmd などと、適当なバッチファイルにして保存するだけ。
後はダブルクリックすれば、その PC をスリープ状態にできる。

@echo (Add-Type TempClass -Member '[DllImport("powrprof.dll")]public static extern bool SetSuspendState(bool Hibernate,bool ForceCritical,bool DisableWakeEvent);' -PassThru)::SetSuspendState($false,$false,$false) | powershell -c -

呼び出している Win32API については、MSDN の記事を参照: SetSuspendState function (Windows)
「SetSuspendState を呼び出す C# の関数を、PowerShell でコンパイルし、PowerShell から呼び出すようになっているコード」を、コマンドプロンプトで echo し、実行コマンドとして PowerShell にパイプで渡しているだけだ。

やってることはめんどくさいが、Windows7 以降なら追加のツールのインストールとかも必要なくすぐに作れるのがミソ。
PowerShell + Add-Type は本当に何でもできるから困る。

ちなみに、ちまたで見かける

shellrundll32.exe PowrProf.dll,SetSuspendState 0,1,0

とか言うのは間違った使い方だ。
Rundll32.exe の仕様 を見ればわかるとおり、このアプリケーションは、特定のプロトタイプに従った関数を呼び出すためのもので、任意の関数を呼ぶためのものではない。
上記のような呼び出し方をすると、実際には

   hwnd = (parent window handle)
   hinst = HINSTANCE of PowrProf.dll
   lpszCmdLine = "0,1,0"
   nCmdShow = (whatever the nCmdShow was passed to CreateProcess)

としたうえで、

   SetSuspendState(hwnd, hinst, lpszCmdLine, nCmdShow)

という呼び出しをしていることになってしまう。
これはもちろん SetSuspendState の本来の引数である (BOOLEAN Hibernate, BOOLEAN ForceCritical, BOOLEAN DisableWakeEvent) の bool 値3つに換算すると、(true, true, true) となるため (hwnd, hinst, lpszCmdLine いずれも 0 にならないj)、意図したスリープ状態にならない。
それどころか、イベントやタイマ、WoL から起動できなくなったりする為、Rundll32.exe を使うのは避けた方がいいだろう。

SetSuspendState に限っては、呼ばれる関数に対して呼び出す引数の数(正確には引数の数ではなく、引数の合計サイズ)が多いので問題にはならないが、場合によってはメモリ破壊を引き起こすこともあり得るので、 Rundll32.exe の扱いには注意が必要だ。

Windows コマンドプロンプトで、パイプでつないだコマンドの終了コードを取得

パイプにつないだとあるコマンドの終了コードを読みたい。

しかし、パイプ後の %errorlevel% の中身は、パイプ先の最後のコマンドの終了コードになる。
また、パイプの中身は独立したプロセスになっているようで、この中でいくら環境変数の設定を行っても、消えてしまうのだ。

…となっては仕方ない、いったん一時ファイルに書き出した後、その値を読み出すしかない。

set tmplog=%TEMP%\errlv_%DATE:/=%%RANDOM%%RANDOM%.log
(cd d:\nonexist & call echo %^^errorlevel% ^>"%tmplog%") | sort
for /f "usebackq" %A in ("%tmplog%") do echo result %A
del "%tmplog%"

パイプの呼び出し時と、all echo 時、そして実行時の 合計 3回 環境変数の展開が行われるので、最初の2回分を キャレットで回避しているのがミソ。

ちなみに、cd d:\nonexist は、フォルダが存在しない場合に errorlevel が返されるのを利用したもので、 sort はパイプで文字を受け取るデモになっている。(一行だけなので意味は無いけど…)

ただし、バッチファイルに書くときは、以下の様に % を重ねて書かなくてはならないので注意。

set tmplog=%TEMP%\errlv_%DATE:/=%%RANDOM%%RANDOM%.log
(cd d:\nonexist & call echo %%^^errorlevel%% ^>"%tmplog%") | sort
for /f "usebackq" %%A in ("%tmplog%") do echo result %%A
del "%tmplog%"

続きを読む

64bit 環境で AutoHotkey の ThumbRemap を動かす

タッチパッドの革新的な操作感を提供する、 ThumbSense というソフトがあるのだが、これには中クリックを実現できない欠点があった。
そこで、AutoHotkey を利用して、スクリプトで自由にキーを割り当てつつ、 ThumbSense の機能を実現してしまおうという、 ThumbRemap.ahk というものが存在する。
(オリジナルの配布もとは既に無くなっており、ミラーサイトで配布中 → 残念ながら、ミラーサイトからは拾えなくなってしまったようだ。 ただ、 元サイトの Wayback Machine で拾える模様

これ自体もなかなか古く、最新の 64bit OS で 普通に AutoHotkey を導入してしてもおそらく動かないだろう。

続きを読む

System.Data.SQLite でどれをインストールするべきか

ちょっとした宣伝:

NuGet パッケージマネージャを使う場合については、別途

の記事にまとめている。

以下、本題。

メモ: 内容が 2012 年当時のものなので、 引用元の文章がすでになくなっていたりするが、 内容的には問題ないはずだ。

System.Data.SQLite を導入する

SQLite の ADO.NET アダプタである、System.Data.SQLite。
単なるラッパではなく、SQLite 自体もパッケージに持っているので、別途 SQLite をパッケージに含めなくても良いのが利点。
しかも、ライセンスが Public Domain であるのが、非常に使い勝手が良い。

注意: 詳しくは System.Data.SQLite の 著作権表記 を読んでほしいが、"System.Data.SQLite.Linq" と "System.Data.SQLite.EF6" については、ソースコードの一部が Ms-PL ライセンスとなっている。
Ms-PL ライセンスとなっている SQL Generation ディレクトリ のソースは、public domain と明示されているものをのぞくと、すべて copyright (c) Microsoft Corporation と書いてある。 ビルド済みバイナリには Microsoft の著作権表記などが出てこないため、バイナリ配布であれば Public Domain と書かれたライセンスに従えば良いので、特に何も気にする必要はなさそうだ。 ソースコードを配布する場合は、上記ソースコード先頭などに書かれているの著作権表記等の部分をそのまま消さずに表示しなくてはならない点に注意すべきだろう。

さて、いざ使おうとダウンロードページに飛ぶと、それはもうすごい数のパッケージが配布されている。

続きを読む

音量をノーマライズ(ゲイン調整)して、ATRAC 形式で取り込む

いつもとはちょっと違う話題を。

WALKMAN や LISMO! で、ギャップレス再生をさせるために ATRAC 形式で取り込ませたいのだが、x-アプリ からの取り込みでは、音量の均一化をできない。
MP3Gain や、AACGain などのように、ロスレスで音量を均一化するソフトがあればよいのだが、残念ながら、ATRAC には存在しない模様。
どうせ取り込む前の1回だけなのだから、1回ぐらい不可逆変換してもいいじゃん、と言うことで、WaveGain を使って、 Wav の段階で音量を均一化して、x-アプリに取り込ませる方法をご紹介。

# ちなみに普段は、Nero 使って aac に変換し、AACGain でノーマライズしてから、x-アプリ に取り込ませている。あとからほかのツールに移行する可能性を考えると、直接CDからツールの独自形式で取り込ませるのはできるだけ避けたいから…
# ちなみに、WALKMAN 側に「ダイナミックノーマライザ」という機能もあるが、これは曲中の強弱までノーマライズしてしまうのでかなりイマイチ。

続きを読む

[WPF] ウィンドウのリサイズが終了するまで、コントロールのリサイズを行わない方法 その2

間が開いてしまったが、ウィンドウのリサイズが終了するまで、コントロールのリサイズを行わない方法 その1 の続き。

このままだと、描画されていない部分が黒く残ってしまい気持ちが悪いので、レイアウトを行わない場合でも、ViewBox を使ってスケーリングして表示してみる。

続きを読む

[WPF] RadioButton の IsChecked バインディングがうまく動かない

ラジオボタンのデータバインディングを、チェックボタンと同様にやっているはずなのに、なぜか途中で動かなくなったりして、かなり詰まっていた。
解決方法…というか、回避策をメモしておこう。

続きを読む

[WPF] ウィンドウのリサイズが終了するまで、コントロールのリサイズを行わない方法 その1

ウィンドウ内にコントロールがたくさんあって、レイアウトが重くなってしまったので、リサイズすると凄まじくカクカクしてしまう。
そんなときに、ウィンドウのリサイズ中はコントロールの配置を変化させず、リサイズが終了してから再配置をさせる方法をご紹介。

続きを読む

非同期でプログラムを実行するバッチを、パイプする

start コマンドを使い、非同期でプログラムを実行するバッチを作成し、そのバッチの出力をパイプすると、実行したプログラムが終了するまでパイプの処理が返らない。

pause.cmd

@echo pause
@exit /b

process.cmd

@echo off
Echo 処理1開始
Rem 処理1
Echo 処理2開始
start "" cmd /s /c "pause.cmd"
exit /b

と作成し、コマンドプロンプトで単に

process.cmd

とすれば、pause.cmd を閉じなくても次の処理に進めるのだが、

process.cmd | sort

などとパイプしたとたん、処理が返らなくなってしまう。

これの対処法はいくつかある。 続きを読む