Option Strict Off
Option Explicit On

Imports System.Runtime.InteropServices

Friend Class Form1
        Inherits System.Windows.Forms.Form
        
    ' システムをログオフまたはシャットダウン
    Public Enum ExitWindows
        EWX_LOGOFF = &H0        'ログオフ
        EWX_SHUTDOWN = &H1      'シャットダウン 98:電源オフ
        EWX_REBOOT = &H2        '再起動
        EWX_POWEROFF = &H8      '電源オフ    NT:電源オフ 98:無し
        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


        
        ' アクセストークンのタイプ
        Private Const TOKEN_QUERY As Short = &H8s
        Private Const TOKEN_ADJUST_PRIVILEGES As Short = &H20s
        
        ' プロセスと結び付けられたアクセストークンを開く
    <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


        ' ユニークな認証標識を定義する構造体
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure tagLUID
        Dim LowPart As Integer
        Dim HighPart As Integer
    End Structure
        
        ' SE_SHUTDOWN_NAME特権を示す定数
        Private Const SE_SHUTDOWN_NAME As String = "SeShutdownPrivilege"
        
        ' 指定した特権名のLUIDを検索
    <DllImport("advapi32.dll", SetLastError:=True, _
        CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _
    Private Shared Function LookupPrivilegeValue( _
        ByVal lpSystemName As String, _
        ByVal lpName As String, _
        ByRef lpLuid As Long) As Boolean
    End Function

    ' 指定したアクセストークンを設定
    <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

    ' ユニークな認証標識と属性を定義
    Private Structure LUID_AND_ATTRIBUTES
        Dim Luid As tagLUID
        Dim Attributes As Integer
    End Structure

    ' アクセストークンの特権セット情報を定義
    <StructLayout(LayoutKind.Sequential, Pack:=1)> _
    Private Structure TOKEN_PRIVILEGES
        Public PrivilegeCount As Integer
        Public Luid As Long
        Public Attributes As Integer
    End Structure



    ' 特権の属性を示す定数
    Private Const SE_PRIVILEGE_ENABLED As Integer = &H2




    Private 予定時刻 As Date
    Private Op As Long   '処理種別




    Private Sub Form1_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load
        '------------------
        ' 書式設定
        '------------------
        Me.Text = "指定時間後にシャットダウン/再起動/ログオフ"

        Button1.Text = "電源オフ"
        Button2.Text = "再起動"
        Button3.Text = "ログオフ"
        Button4.Text = "中止"
        Button4.Enabled = False

        With Combo1
            .Font = VB6.FontChangeSize(.Font, 11) 'フォントサイズ
            .ImeMode = System.Windows.Forms.ImeMode.Off 'IMEモード:オフ
            .Items.Add(0) '時間リスト 0分
            .Items.Add(1) '1分
            .Items.Add(10) '10分
            .Items.Add(30) '30分
            .SelectedIndex = 0 '初期選択位置:0分
        End With

        With Label1
            .Font = VB6.FontChangeSize(.Font, 16)
            .TextAlign = System.Drawing.ContentAlignment.TopCenter
            .Text = ""
        End With
        With Timer1
            .Enabled = False '初期状態は無効
            .Interval = 1000 '周期:1秒
        End With
    End Sub

    '----------------------------
    ' 指定時間の入力チェック
    '----------------------------
    Private Sub Combo1_KeyPress(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.KeyPressEventArgs) Handles Combo1.KeyPress
        Dim KeyAscii As Short = Asc(eventArgs.KeyChar)
        If (KeyAscii <> Asc(vbBack)) And (Not (Chr(KeyAscii) Like "[0-9]")) Then KeyAscii = 0 '0〜9以外は不許可
        eventArgs.KeyChar = Chr(KeyAscii)
        If KeyAscii = 0 Then
            eventArgs.Handled = True
        End If
    End Sub

    Private Sub Combo1_Validating(ByVal eventSender As System.Object, ByVal eventArgs As System.ComponentModel.CancelEventArgs) Handles Combo1.Validating
        Dim Cancel As Boolean = eventArgs.Cancel
        If Combo1.Text = "" Then
            MsgBox("指定時間:空白は無効")
            Cancel = True '空白は不許可
        End If
        eventArgs.Cancel = Cancel
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Call Go(ExitWindows.EWX_SHUTDOWN)
    End Sub
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Call Go(ExitWindows.EWX_REBOOT)
    End Sub
    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        Call Go(ExitWindows.EWX_LOGOFF)
    End Sub
    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
        Timer1.Enabled = False   'タイマー停止
        Label1.Text = ""
        Button1.Enabled = True
        Button2.Enabled = True
        Button3.Enabled = True
        Button4.Enabled = False
    End Sub

    Private Sub Go(ByVal Param As Integer)

        Op = Param    '処理種別

        Label1.ForeColor = System.Drawing.Color.Black '表示色=黒
        Timer1.Enabled = True
        If Val(Combo1.Text) = 0 Then '指定時間が0なら
            予定時刻 = System.DateTime.FromOADate(Now.ToOADate + TimeSerial(0, 0, 5).ToOADate) '5秒前から
        Else
            予定時刻 = System.DateTime.FromOADate(Now.ToOADate + TimeSerial(0, Val(Combo1.Text), 0).ToOADate) 'そのまま
        End If

        Button1.Enabled = False
        Button2.Enabled = False
        Button3.Enabled = False
        Button4.Enabled = True

    End Sub


    Private Sub Timer1_Tick(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles Timer1.Tick

        Dim t As TimeSpan '残り時間

        t = New TimeSpan(0, 0, (予定時刻 - Now()).TotalSeconds) '秒単位(ミリ秒は表示しない)
        Label1.Text = t.ToString
        Select Case t.TotalSeconds
            Case Is <= 0 '時間=0
                '-------------------
                ' 電源オフ実行
                '-------------------
                Call PowerOff() '電源オフ実行
                End
            Case Is <= 10 '10秒以内
                Label1.ForeColor = System.Drawing.Color.Red '表示色=赤
                Beep() 'カウントダウン音
            Case Else

        End Select

    End Sub


    Private Sub PowerOff()
        '
        ' 電源オフ用サブルーチン
        '
        Dim lngHandleProcess As IntPtr
        Dim lngHandleToken As IntPtr
        Dim udtLuid As New tagLUID
        Dim udtNewState As New TOKEN_PRIVILEGES
        Dim udtPreviousState As New TOKEN_PRIVILEGES
        Dim lngRc As Integer

        Dim OSPlatform As String = My.Computer.Info.OSPlatform 'OSバージョン取得

        Select Case OSPlatform
            Case "Win32NT" 'NT系OS?
                ' カレントプロセスの疑似ハンドルを取得
                lngHandleProcess = GetCurrentProcess()

                ' カレントプロセスのアクセストークンを開く
                lngRc = OpenProcessToken(lngHandleProcess, TOKEN_QUERY Or TOKEN_ADJUST_PRIVILEGES, lngHandleToken)

                ' アクセストークンのパラメータに特権を付与
                With udtNewState
                    .PrivilegeCount = 1
                    .Attributes = SE_PRIVILEGE_ENABLED
                    ' SE_SHUTDOWN_NAME特権のLUIDを検索
                    lngRc = LookupPrivilegeValue(Nothing, SE_SHUTDOWN_NAME, udtNewState.Luid)
                End With
                ' 新しいアクセストークンを設定 ★★★★★★例外発生★★★★★★★★
                lngRc = AdjustTokenPrivileges(lngHandleToken, False, udtNewState, 0, IntPtr.Zero, IntPtr.Zero)

                ' 終了処理(NTの場合、SHUTDOWNでは電源は切れない)
                Call ExitWindowsEx(Op, CInt(0))
            Case "Win32Windows"
                If Op = ExitWindows.EWX_POWEROFF Then Op = ExitWindows.EWX_SHUTDOWN
                Call ExitWindowsEx(Op, CLng(0))
            Case Else
        End Select

    End Sub

End Class