2009/09/13に第12回北海道開発オフがあったので参加してきた。
今回はなんと参加者20人。ランチも二手に分かれなければならないほどの大盛況。
今回はノートPCを臨時に調達したから、前回設計した「「0時に強制的にシャットダウンするアプリケーション」の実装を行うことにした。とりあえずは実行したら即シャットダウンするまでを目指す。
ソースは以下
IShutdownインターフェイス
’Shutdownを行うメソッド群のインターフェース
Public Interface IShutdown
'Shutdownを実行する
Function Shutdown() As Boolean
End Interface
WinShutdownクラス
Windowsで実際にシャットダウンを行うクラス。シャットダウンはWinAPIを使用することになるが、何を使えば良いのだろう。調べてみるとDOBON.NETのこのページが見つかった。ExitWindowsEx関数を使用するとシャットダウンができる、とある。ただしWindowsNT系からはSE_SHUTDOWN_NAMEセキュリティ特権とやらを有効にする必要があるとのこと。これらAPIをPrivateで定義してShutdownメソッドてそれらを使用してシャットダウンする手順を踏む。
Imports System
Imports System.Runtime.InteropServices
'IShutdownインターフェースを実装した、Windows向けシャットダウンクラス
Public Class WinShutdown
Implements IShutdown
'Shutdownを実行する(IShutdownインターフェースの実装)
Public Function Shutdown() As Boolean Implements IShutdown.Shutdown
Const TOKEN_ADJUST_PRIVILEGES As Integer = &H20
Const TOKEN_QUERY As Integer = &H8
Const SE_PRIVILEGE_ENABLED As Integer = &H2
Const SE_SHUTDOWN_NAME As String = "SeShutdownPrivilege"
Dim procHandle As Intptr = GetCurrentProcess()
'トークンを取得する
Dim tokenHandle As IntPtr
OpenProcessToken(procHandle, TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, tokenHandle)
'LUIDを取得する
Dim tp As New TOKEN_PRIVILEGES()
tp.Attributes = SE_PRIVILEGE_ENABLED
tp.PrivilegeCount = 1
LookupPrivilegeValue(Nothing, SE_SHUTDOWN_NAME, tp.Luid)
'特権を有効にする
AdjustTokenPrivileges(tokenHandle, False, tp, 0, IntPtr.Zero, IntPtr.Zero)
ExitWindowsEx(ExitWindows.EWX_SHUTDOWN Or ExitWindows.EWX_FORCE, 0)
End Function
Private Enum ExitWindows
EWX_LOGOFF = &H0
EWX_SHUTDOWN = &H1
EXW_REBOOT = &H2
EWX_POWEROFF = &H8
EWX_RESTARTAPPS = &H40
EWX_FORCE = &H4
EWX_FORCEIFHUNG = &H10
End Enum
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function ExitWindowsEx(ByVal uFlags As ExitWindows, ByVal dwReason As Integer) As Boolean
End Function
<DllImport("kernel32.dll")> _
Private Shared Function GetCurrentProcess() As IntPtr
End Function
<DllImport("advapi32.dll", SetLastError:=True)> _
Private Shared Function OpenProcessToken(ByVal ProcessHandle As IntPtr, _
ByVal DesiredAccess As Integer, _
ByRef TokenHandle As IntPtr) As Boolean
End Function
<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function LookupPrivilegeValue(ByVal lpSystemName As String, _
ByVal lpName As String, _
ByRef lpLuid As Long) As Boolean
End Function
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Private Structure TOKEN_PRIVILEGES
Public PrivilegeCount As Integer
Public Luid As Long
Public Attributes As Integer
End Structure
<DllImport("advapi32.dll", SetLastError:=True)> _
Private Shared Function AdjustTokenPrivileges(ByVal TokenHandle As IntPtr, _
ByVal DisableAllPrivileges As Boolean, _
ByRef NewState As TOKEN_PRIVILEGES, _
ByVal BufferLength As Integer, _
ByVal PreviousState As IntPtr, _
ByVal ReturnLength As IntPtr) As Boolean
End Function
End Class
ShutdownFactoryクラス
IShutdownインターフェースを実装したクラスのインスタンスを生成するファクトリクラス。Windowsの場合は上記のWinShutdownクラスのインスタンスを返している。今回はWindowsだけ実装してみる。これそもそも他のOSで動くのだろうか。
Imports System
'Shutdownファクトリークラス
Public Class ShutdownFactory
Public Shared Function CreateShutdown() As IShutdown
Select Case System.Environment.OSVersion.Platform
Case System.PlatformID.Win32Windows, _
System.PlatformID.Win32NT, _
System.PlatformID.WinCE
'Windows
Return New WinShutdown()
Case System.PlatformID.Unix
Throw New NotSupportedException()
Case System.PlatformID.MacOSX
Throw New NotSupportedException()
Case Else
Throw New NotSupportedException()
End Select
End Function
End Class
Programクラス
メインクラス。ファクトリクラスからIShutdown実装されたインスタンスを受け取って、Shutdownメソッド呼んでいる。
Imports System
Imports System.Windows.Forms
Public Class Program
Public Shared Sub Main(args As String())
Dim sd As IShutdown = ShutdownFactory.CreateShutdown()
sd.Shutdown()
End Sub
End Class
さて、Visual Studioがあればここでメニューから「ビルド」を選択すれば良いのだが、今回は借り物のPCなのでVisual Studioをインストールするのは躊躇った。そこでSDKについてくるMSBuildをコマンドラインから使用する。
<Project DefaultTarget="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="*.vb" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Drawing" />
</ItemGroup>
<PropertyGroup>
<AssemblyName>Pumpkin</AssemblyName>
<OutputType>WinExe</OutputType>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.VisualBasic.targets" />
</Project>
上記のようなxmlを拡張子*.projで保存する。あとはMSBuildコマンドを実行すると、同ディレクトリの拡張子*.projのファイルを拾ってタスクを実行してくれる。
Setting environment to use Microsoft .NET Framework v2.0 SDK tools.
For a list of SDK tools, see the 'StartTools.htm' file in the bin folder.
>msbuild
Microsoft(R) Build Engine Version 2.0.50727.3053
[Microsoft .NET Framework, Version 2.0.50727.3082]
Copyright (C) Microsoft Corporation 2005. All rights reserved.
2009/09/14 23:15:43 にビルドを開始しました。
__________________________________________________
プロジェクト "Shutdown.proj" (既定のターゲット):
ターゲット PrepareForBuild:
ディレクトリ "bin\Debug\" を作成しています。
ディレクトリ "obj\Debug\" を作成しています。
ターゲット CoreCompile:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Vbc.exe /noconfig /define:"CONFIG=\"\",PLATFORM=\"AnyCPU\"" /reference
:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll,c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Drawing.d
ll,c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Windows.Forms.dll /debug+ /out:obj\Debug\Pumpkin.exe /target:win
exe CountDown.vb IShutdown.vb MainForm.vb Program.vb ShutdownFactory.vb WinShutdown.vb
ターゲット CopyFilesToOutputDirectory:
"obj\Debug\Pumpkin.exe" から "bin\Debug\Pumpkin.exe" へファイルをコピーしています。
Shutdown -> \bin\Debug\Pumpkin.exe
"obj\Debug\Pumpkin.pdb" から "bin\Debug\Pumpkin.pdb" へファイルをコピーしています。
ビルドに成功しました。
0 警告
0 エラー
経過時間 00:00:01.68
成功すると、bin\debugフォルダにPumplin.exeができているはず。これを実行するとシャットダウンされる。これをタスクスケジューラに登録すれば良い。
ただ、何のお知らせも無くいきなりシャットダウンするのは心臓によろしくない。「残り何秒」のような表示の画面が欲しいところ。次回はそのあたりを実装してみよう。
コメント
コメントを投稿