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ができているはず。これを実行するとシャットダウンされる。これをタスクスケジューラに登録すれば良い。
ただ、何のお知らせも無くいきなりシャットダウンするのは心臓によろしくない。「残り何秒」のような表示の画面が欲しいところ。次回はそのあたりを実装してみよう。
コメント
コメントを投稿