@@ -87,9 +87,114 @@ private static extern bool InitiateSystemShutdownEx(
8787 bool bRebootAfterShutdown ,
8888 ShutdownReason dwReason ) ;
8989
90+ private const string ShutdownPrivilegeName = "SeShutdownPrivilege" ;
91+
92+ private const uint TokenAdjustPrivileges = 0x20 ;
93+ private const uint TokenQuery = 0x8 ;
94+ private const uint SePrivilegeEnabled = 0x2 ;
95+
96+ [ StructLayout ( LayoutKind . Sequential ) ]
97+ private struct Luid
98+ {
99+ public uint LowPart ;
100+ public int HighPart ;
101+ }
102+
103+ [ StructLayout ( LayoutKind . Sequential ) ]
104+ private struct LuidAndAttributes
105+ {
106+ public Luid Luid ;
107+ public uint Attributes ;
108+ }
109+
110+ [ StructLayout ( LayoutKind . Sequential ) ]
111+ private struct TokenPrivileges
112+ {
113+ public uint PrivilegeCount ;
114+ public LuidAndAttributes Privileges ;
115+ }
116+
117+ [ DllImport ( "advapi32.dll" , SetLastError = true ) ]
118+ private static extern bool OpenProcessToken ( IntPtr processHandle , uint desiredAccess , out IntPtr tokenHandle ) ;
119+
120+ [ DllImport ( "advapi32.dll" , CharSet = CharSet . Unicode , SetLastError = true ) ]
121+ private static extern bool LookupPrivilegeValue ( string lpSystemName , string lpName , out Luid lpLuid ) ;
122+
123+ [ DllImport ( "advapi32.dll" , SetLastError = true ) ]
124+ private static extern bool AdjustTokenPrivileges (
125+ IntPtr tokenHandle ,
126+ bool disableAllPrivileges ,
127+ ref TokenPrivileges newState ,
128+ uint bufferLength ,
129+ IntPtr previousState ,
130+ IntPtr returnLength ) ;
131+
132+ [ DllImport ( "kernel32.dll" ) ]
133+ private static extern IntPtr GetCurrentProcess ( ) ;
134+
135+ [ DllImport ( "kernel32.dll" , SetLastError = true ) ]
136+ [ return : MarshalAs ( UnmanagedType . Bool ) ]
137+ private static extern bool CloseHandle ( IntPtr hObject ) ;
138+
90139 [ DllImport ( "winmm.dll" ) ]
91140 static extern Int32 mciSendString ( String command , StringBuilder buffer , Int32 bufferSize , IntPtr hwndCallback ) ;
92141
142+ private static bool TryEnableShutdownPrivilege ( out string failureMessage )
143+ {
144+ failureMessage = null ;
145+
146+ if ( ! OpenProcessToken ( GetCurrentProcess ( ) , TokenAdjustPrivileges | TokenQuery , out IntPtr token ) )
147+ {
148+ failureMessage = new Win32Exception ( ) . Message ;
149+ return false ;
150+ }
151+
152+ try
153+ {
154+ if ( ! LookupPrivilegeValue ( null , ShutdownPrivilegeName , out Luid luid ) )
155+ {
156+ failureMessage = new Win32Exception ( ) . Message ;
157+ return false ;
158+ }
159+
160+ TokenPrivileges tokenPrivileges = new TokenPrivileges
161+ {
162+ PrivilegeCount = 1 ,
163+ Privileges = new LuidAndAttributes
164+ {
165+ Luid = luid ,
166+ Attributes = SePrivilegeEnabled ,
167+ } ,
168+ } ;
169+
170+ if ( ! AdjustTokenPrivileges ( token , false , ref tokenPrivileges , 0 , IntPtr . Zero , IntPtr . Zero ) )
171+ {
172+ failureMessage = new Win32Exception ( ) . Message ;
173+ return false ;
174+ }
175+
176+ if ( Marshal . GetLastWin32Error ( ) == 1300 ) // ERROR_NOT_ALL_ASSIGNED
177+ {
178+ failureMessage = "The process does not hold the shutdown privilege." ;
179+ return false ;
180+ }
181+
182+ return true ;
183+ }
184+ finally
185+ {
186+ CloseHandle ( token ) ;
187+ }
188+ }
189+
190+ private static void TryEnableShutdownPrivilegeAndLog ( )
191+ {
192+ if ( ! TryEnableShutdownPrivilege ( out string failureMessage ) )
193+ {
194+ logger . LogError ( "Could not enable shutdown privilege: " + failureMessage ) ;
195+ }
196+ }
197+
93198 public void Sleep ( )
94199 {
95200 logger . Log ( "Sleeping." ) ;
@@ -99,24 +204,32 @@ public void Sleep()
99204 public void LogOff ( )
100205 {
101206 logger . Log ( "Logging off." ) ;
207+ TryEnableShutdownPrivilegeAndLog ( ) ;
102208 ExitWindowsEx ( ExitWindows . LogOff , ShutdownReason . MajorOther | ShutdownReason . MinorOther | ShutdownReason . FlagPlanned ) ;
103209 }
104210
105211 public void ShutDown ( )
106212 {
107213 logger . Log ( "Shutting down." ) ;
108- InitiateSystemShutdownEx (
109- null ,
110- null ,
214+ TryEnableShutdownPrivilegeAndLog ( ) ;
215+
216+ if ( ! InitiateSystemShutdownEx (
217+ null ,
218+ null ,
111219 0 ,
112- true ,
113- false ,
114- ShutdownReason . MajorOther | ShutdownReason . MinorOther | ShutdownReason . FlagPlanned ) ;
220+ true ,
221+ false ,
222+ ShutdownReason . MajorOther | ShutdownReason . MinorOther | ShutdownReason . FlagPlanned ) )
223+ {
224+ var error = new Win32Exception ( ) ;
225+ logger . LogError ( $ "System shutdown failed: { error . Message } ") ;
226+ }
115227 }
116228
117229 public void Restart ( )
118230 {
119231 logger . Log ( "Restarting." ) ;
232+ TryEnableShutdownPrivilegeAndLog ( ) ;
120233 ExitWindowsEx ( ExitWindows . Reboot , ShutdownReason . MajorOther | ShutdownReason . MinorOther | ShutdownReason . FlagPlanned ) ;
121234 }
122235
0 commit comments