相対パスを指定して、UAC が働く「管理者として」、バッチを起動するショートカットを作る

相対パスを指定して、コマンドやアプリケーションを管理者権限で実行するような、ショートカットを作成したい。

管理者としてでなければ、ショートカットのプロパティで「作業フォルダ」を空白にしておくと、エクスプローラからショートカットを起動したときに、ショートカットの場所がカレントディレクトリ(作業フォルダ)になることを利用して、

"%ComSpec%" /C START "" "<相対パス>" [引数...]

としたショートカットを作成することで実現できる。
(一瞬コマンドプロンプトが立ち上がる問題は、「実行時の大きさ」を最小化にしておくことである程度回避可能)

ところが、ショートカットの詳細設定から「管理者として実行」を選んでしまうと、カレントディレクトリが %SystemRoot%\system32 へ勝手に変更されてしまう。
こうなると、上記のショートカットでは %SystemRoot%\system32 からの相対パスを開こうとするため、意図した動作にならない。

このため、 UAC を起動させる ものと、カレントディレクトリの情報を渡す ものの 2つのショートカットを作成することで実現させる。

START コマンドを使うので、内容を理解する場合はこのコマンドのヘルプをよく読み込んでおくと良いかも知れない。

START ["タイトル"] [/D パス] (中略) [コマンド/プログラム] [パラメーター]

    "タイトル"  ウィンドウのタイトル バーに表示するタイトル。
    パス        開始するディレクトリ。

    コマンド/プログラム
                内部コマンドまたはバッチ ファイルの場合、コマンド プロセッサ
                は cmd.exe の /K オプションを使用して実行されます。
                これは コマンドの後でもウィンドウが残ることを意味
                します。

                内部コマンドまたはバッチ ファイルではない場合、そのプログラム
                はウィンドウ モードのアプリケーションまたはコンソール
                アプリケーションとして動作します。

   パラメーター
               コマンド/プログラムに渡すパラメーターです。

UAC を起動させるショートカット

まずは、指定した プログラム/コマンド を パラメータ付きで 管理者として実行するショートカットを作成する。

以下の様にプロパティを設定したショートカットを作成しよう。
続きを読む

リモートデスクトップ時に、バッチでサーバ側の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 は本当に何でもできるから困る。

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

rundll32.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 の扱いには注意が必要だ。