Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
michael@0 | 1 | # A module to expose various thread/process/job related structures and |
michael@0 | 2 | # methods from kernel32 |
michael@0 | 3 | # |
michael@0 | 4 | # The MIT License |
michael@0 | 5 | # |
michael@0 | 6 | # Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se> |
michael@0 | 7 | # |
michael@0 | 8 | # Additions and modifications written by Benjamin Smedberg |
michael@0 | 9 | # <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation |
michael@0 | 10 | # <http://www.mozilla.org/> |
michael@0 | 11 | # |
michael@0 | 12 | # More Modifications |
michael@0 | 13 | # Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com> |
michael@0 | 14 | # Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com> |
michael@0 | 15 | # |
michael@0 | 16 | # By obtaining, using, and/or copying this software and/or its |
michael@0 | 17 | # associated documentation, you agree that you have read, understood, |
michael@0 | 18 | # and will comply with the following terms and conditions: |
michael@0 | 19 | # |
michael@0 | 20 | # Permission to use, copy, modify, and distribute this software and |
michael@0 | 21 | # its associated documentation for any purpose and without fee is |
michael@0 | 22 | # hereby granted, provided that the above copyright notice appears in |
michael@0 | 23 | # all copies, and that both that copyright notice and this permission |
michael@0 | 24 | # notice appear in supporting documentation, and that the name of the |
michael@0 | 25 | # author not be used in advertising or publicity pertaining to |
michael@0 | 26 | # distribution of the software without specific, written prior |
michael@0 | 27 | # permission. |
michael@0 | 28 | # |
michael@0 | 29 | # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
michael@0 | 30 | # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. |
michael@0 | 31 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
michael@0 | 32 | # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS |
michael@0 | 33 | # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, |
michael@0 | 34 | # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION |
michael@0 | 35 | # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
michael@0 | 36 | |
michael@0 | 37 | from ctypes import c_void_p, POINTER, sizeof, Structure, Union, windll, WinError, WINFUNCTYPE, c_ulong |
michael@0 | 38 | from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WORD, ULONG |
michael@0 | 39 | from qijo import QueryInformationJobObject |
michael@0 | 40 | |
michael@0 | 41 | LPVOID = c_void_p |
michael@0 | 42 | LPBYTE = POINTER(BYTE) |
michael@0 | 43 | LPDWORD = POINTER(DWORD) |
michael@0 | 44 | LPBOOL = POINTER(BOOL) |
michael@0 | 45 | LPULONG = POINTER(c_ulong) |
michael@0 | 46 | |
michael@0 | 47 | def ErrCheckBool(result, func, args): |
michael@0 | 48 | """errcheck function for Windows functions that return a BOOL True |
michael@0 | 49 | on success""" |
michael@0 | 50 | if not result: |
michael@0 | 51 | raise WinError() |
michael@0 | 52 | return args |
michael@0 | 53 | |
michael@0 | 54 | |
michael@0 | 55 | # AutoHANDLE |
michael@0 | 56 | |
michael@0 | 57 | class AutoHANDLE(HANDLE): |
michael@0 | 58 | """Subclass of HANDLE which will call CloseHandle() on deletion.""" |
michael@0 | 59 | |
michael@0 | 60 | CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE) |
michael@0 | 61 | CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32)) |
michael@0 | 62 | CloseHandle.errcheck = ErrCheckBool |
michael@0 | 63 | |
michael@0 | 64 | def Close(self): |
michael@0 | 65 | if self.value and self.value != HANDLE(-1).value: |
michael@0 | 66 | self.CloseHandle(self) |
michael@0 | 67 | self.value = 0 |
michael@0 | 68 | |
michael@0 | 69 | def __del__(self): |
michael@0 | 70 | self.Close() |
michael@0 | 71 | |
michael@0 | 72 | def __int__(self): |
michael@0 | 73 | return self.value |
michael@0 | 74 | |
michael@0 | 75 | def ErrCheckHandle(result, func, args): |
michael@0 | 76 | """errcheck function for Windows functions that return a HANDLE.""" |
michael@0 | 77 | if not result: |
michael@0 | 78 | raise WinError() |
michael@0 | 79 | return AutoHANDLE(result) |
michael@0 | 80 | |
michael@0 | 81 | # PROCESS_INFORMATION structure |
michael@0 | 82 | |
michael@0 | 83 | class PROCESS_INFORMATION(Structure): |
michael@0 | 84 | _fields_ = [("hProcess", HANDLE), |
michael@0 | 85 | ("hThread", HANDLE), |
michael@0 | 86 | ("dwProcessID", DWORD), |
michael@0 | 87 | ("dwThreadID", DWORD)] |
michael@0 | 88 | |
michael@0 | 89 | def __init__(self): |
michael@0 | 90 | Structure.__init__(self) |
michael@0 | 91 | |
michael@0 | 92 | self.cb = sizeof(self) |
michael@0 | 93 | |
michael@0 | 94 | LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION) |
michael@0 | 95 | |
michael@0 | 96 | # STARTUPINFO structure |
michael@0 | 97 | |
michael@0 | 98 | class STARTUPINFO(Structure): |
michael@0 | 99 | _fields_ = [("cb", DWORD), |
michael@0 | 100 | ("lpReserved", LPWSTR), |
michael@0 | 101 | ("lpDesktop", LPWSTR), |
michael@0 | 102 | ("lpTitle", LPWSTR), |
michael@0 | 103 | ("dwX", DWORD), |
michael@0 | 104 | ("dwY", DWORD), |
michael@0 | 105 | ("dwXSize", DWORD), |
michael@0 | 106 | ("dwYSize", DWORD), |
michael@0 | 107 | ("dwXCountChars", DWORD), |
michael@0 | 108 | ("dwYCountChars", DWORD), |
michael@0 | 109 | ("dwFillAttribute", DWORD), |
michael@0 | 110 | ("dwFlags", DWORD), |
michael@0 | 111 | ("wShowWindow", WORD), |
michael@0 | 112 | ("cbReserved2", WORD), |
michael@0 | 113 | ("lpReserved2", LPBYTE), |
michael@0 | 114 | ("hStdInput", HANDLE), |
michael@0 | 115 | ("hStdOutput", HANDLE), |
michael@0 | 116 | ("hStdError", HANDLE) |
michael@0 | 117 | ] |
michael@0 | 118 | LPSTARTUPINFO = POINTER(STARTUPINFO) |
michael@0 | 119 | |
michael@0 | 120 | SW_HIDE = 0 |
michael@0 | 121 | |
michael@0 | 122 | STARTF_USESHOWWINDOW = 0x01 |
michael@0 | 123 | STARTF_USESIZE = 0x02 |
michael@0 | 124 | STARTF_USEPOSITION = 0x04 |
michael@0 | 125 | STARTF_USECOUNTCHARS = 0x08 |
michael@0 | 126 | STARTF_USEFILLATTRIBUTE = 0x10 |
michael@0 | 127 | STARTF_RUNFULLSCREEN = 0x20 |
michael@0 | 128 | STARTF_FORCEONFEEDBACK = 0x40 |
michael@0 | 129 | STARTF_FORCEOFFFEEDBACK = 0x80 |
michael@0 | 130 | STARTF_USESTDHANDLES = 0x100 |
michael@0 | 131 | |
michael@0 | 132 | # EnvironmentBlock |
michael@0 | 133 | |
michael@0 | 134 | class EnvironmentBlock: |
michael@0 | 135 | """An object which can be passed as the lpEnv parameter of CreateProcess. |
michael@0 | 136 | It is initialized with a dictionary.""" |
michael@0 | 137 | |
michael@0 | 138 | def __init__(self, dict): |
michael@0 | 139 | if not dict: |
michael@0 | 140 | self._as_parameter_ = None |
michael@0 | 141 | else: |
michael@0 | 142 | values = ["%s=%s" % (key, value) |
michael@0 | 143 | for (key, value) in dict.iteritems()] |
michael@0 | 144 | values.append("") |
michael@0 | 145 | self._as_parameter_ = LPCWSTR("\0".join(values)) |
michael@0 | 146 | |
michael@0 | 147 | # Error Messages we need to watch for go here |
michael@0 | 148 | # See: http://msdn.microsoft.com/en-us/library/ms681388%28v=vs.85%29.aspx |
michael@0 | 149 | ERROR_ABANDONED_WAIT_0 = 735 |
michael@0 | 150 | |
michael@0 | 151 | # GetLastError() |
michael@0 | 152 | GetLastErrorProto = WINFUNCTYPE(DWORD # Return Type |
michael@0 | 153 | ) |
michael@0 | 154 | GetLastErrorFlags = () |
michael@0 | 155 | GetLastError = GetLastErrorProto(("GetLastError", windll.kernel32), GetLastErrorFlags) |
michael@0 | 156 | |
michael@0 | 157 | # CreateProcess() |
michael@0 | 158 | |
michael@0 | 159 | CreateProcessProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 160 | LPCWSTR, # lpApplicationName |
michael@0 | 161 | LPWSTR, # lpCommandLine |
michael@0 | 162 | LPVOID, # lpProcessAttributes |
michael@0 | 163 | LPVOID, # lpThreadAttributes |
michael@0 | 164 | BOOL, # bInheritHandles |
michael@0 | 165 | DWORD, # dwCreationFlags |
michael@0 | 166 | LPVOID, # lpEnvironment |
michael@0 | 167 | LPCWSTR, # lpCurrentDirectory |
michael@0 | 168 | LPSTARTUPINFO, # lpStartupInfo |
michael@0 | 169 | LPPROCESS_INFORMATION # lpProcessInformation |
michael@0 | 170 | ) |
michael@0 | 171 | |
michael@0 | 172 | CreateProcessFlags = ((1, "lpApplicationName", None), |
michael@0 | 173 | (1, "lpCommandLine"), |
michael@0 | 174 | (1, "lpProcessAttributes", None), |
michael@0 | 175 | (1, "lpThreadAttributes", None), |
michael@0 | 176 | (1, "bInheritHandles", True), |
michael@0 | 177 | (1, "dwCreationFlags", 0), |
michael@0 | 178 | (1, "lpEnvironment", None), |
michael@0 | 179 | (1, "lpCurrentDirectory", None), |
michael@0 | 180 | (1, "lpStartupInfo"), |
michael@0 | 181 | (2, "lpProcessInformation")) |
michael@0 | 182 | |
michael@0 | 183 | def ErrCheckCreateProcess(result, func, args): |
michael@0 | 184 | ErrCheckBool(result, func, args) |
michael@0 | 185 | # return a tuple (hProcess, hThread, dwProcessID, dwThreadID) |
michael@0 | 186 | pi = args[9] |
michael@0 | 187 | return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.dwThreadID |
michael@0 | 188 | |
michael@0 | 189 | CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32), |
michael@0 | 190 | CreateProcessFlags) |
michael@0 | 191 | CreateProcess.errcheck = ErrCheckCreateProcess |
michael@0 | 192 | |
michael@0 | 193 | # flags for CreateProcess |
michael@0 | 194 | CREATE_BREAKAWAY_FROM_JOB = 0x01000000 |
michael@0 | 195 | CREATE_DEFAULT_ERROR_MODE = 0x04000000 |
michael@0 | 196 | CREATE_NEW_CONSOLE = 0x00000010 |
michael@0 | 197 | CREATE_NEW_PROCESS_GROUP = 0x00000200 |
michael@0 | 198 | CREATE_NO_WINDOW = 0x08000000 |
michael@0 | 199 | CREATE_SUSPENDED = 0x00000004 |
michael@0 | 200 | CREATE_UNICODE_ENVIRONMENT = 0x00000400 |
michael@0 | 201 | |
michael@0 | 202 | # Flags for IOCompletion ports (some of these would probably be defined if |
michael@0 | 203 | # we used the win32 extensions for python, but we don't want to do that if we |
michael@0 | 204 | # can help it. |
michael@0 | 205 | INVALID_HANDLE_VALUE = HANDLE(-1) # From winbase.h |
michael@0 | 206 | |
michael@0 | 207 | # Self Defined Constants for IOPort <--> Job Object communication |
michael@0 | 208 | COMPKEY_TERMINATE = c_ulong(0) |
michael@0 | 209 | COMPKEY_JOBOBJECT = c_ulong(1) |
michael@0 | 210 | |
michael@0 | 211 | # flags for job limit information |
michael@0 | 212 | # see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx |
michael@0 | 213 | JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800 |
michael@0 | 214 | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000 |
michael@0 | 215 | |
michael@0 | 216 | # Flags for Job Object Completion Port Message IDs from winnt.h |
michael@0 | 217 | # See also: http://msdn.microsoft.com/en-us/library/ms684141%28v=vs.85%29.aspx |
michael@0 | 218 | JOB_OBJECT_MSG_END_OF_JOB_TIME = 1 |
michael@0 | 219 | JOB_OBJECT_MSG_END_OF_PROCESS_TIME = 2 |
michael@0 | 220 | JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT = 3 |
michael@0 | 221 | JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO = 4 |
michael@0 | 222 | JOB_OBJECT_MSG_NEW_PROCESS = 6 |
michael@0 | 223 | JOB_OBJECT_MSG_EXIT_PROCESS = 7 |
michael@0 | 224 | JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS = 8 |
michael@0 | 225 | JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT = 9 |
michael@0 | 226 | JOB_OBJECT_MSG_JOB_MEMORY_LIMIT = 10 |
michael@0 | 227 | |
michael@0 | 228 | # See winbase.h |
michael@0 | 229 | DEBUG_ONLY_THIS_PROCESS = 0x00000002 |
michael@0 | 230 | DEBUG_PROCESS = 0x00000001 |
michael@0 | 231 | DETACHED_PROCESS = 0x00000008 |
michael@0 | 232 | |
michael@0 | 233 | # GetQueuedCompletionPortStatus - http://msdn.microsoft.com/en-us/library/aa364986%28v=vs.85%29.aspx |
michael@0 | 234 | GetQueuedCompletionStatusProto = WINFUNCTYPE(BOOL, # Return Type |
michael@0 | 235 | HANDLE, # Completion Port |
michael@0 | 236 | LPDWORD, # Msg ID |
michael@0 | 237 | LPULONG, # Completion Key |
michael@0 | 238 | LPULONG, # PID Returned from the call (may be null) |
michael@0 | 239 | DWORD) # milliseconds to wait |
michael@0 | 240 | GetQueuedCompletionStatusFlags = ((1, "CompletionPort", INVALID_HANDLE_VALUE), |
michael@0 | 241 | (1, "lpNumberOfBytes", None), |
michael@0 | 242 | (1, "lpCompletionKey", None), |
michael@0 | 243 | (1, "lpPID", None), |
michael@0 | 244 | (1, "dwMilliseconds", 0)) |
michael@0 | 245 | GetQueuedCompletionStatus = GetQueuedCompletionStatusProto(("GetQueuedCompletionStatus", |
michael@0 | 246 | windll.kernel32), |
michael@0 | 247 | GetQueuedCompletionStatusFlags) |
michael@0 | 248 | |
michael@0 | 249 | # CreateIOCompletionPort |
michael@0 | 250 | # Note that the completion key is just a number, not a pointer. |
michael@0 | 251 | CreateIoCompletionPortProto = WINFUNCTYPE(HANDLE, # Return Type |
michael@0 | 252 | HANDLE, # File Handle |
michael@0 | 253 | HANDLE, # Existing Completion Port |
michael@0 | 254 | c_ulong, # Completion Key |
michael@0 | 255 | DWORD # Number of Threads |
michael@0 | 256 | ) |
michael@0 | 257 | CreateIoCompletionPortFlags = ((1, "FileHandle", INVALID_HANDLE_VALUE), |
michael@0 | 258 | (1, "ExistingCompletionPort", 0), |
michael@0 | 259 | (1, "CompletionKey", c_ulong(0)), |
michael@0 | 260 | (1, "NumberOfConcurrentThreads", 0)) |
michael@0 | 261 | CreateIoCompletionPort = CreateIoCompletionPortProto(("CreateIoCompletionPort", |
michael@0 | 262 | windll.kernel32), |
michael@0 | 263 | CreateIoCompletionPortFlags) |
michael@0 | 264 | CreateIoCompletionPort.errcheck = ErrCheckHandle |
michael@0 | 265 | |
michael@0 | 266 | # SetInformationJobObject |
michael@0 | 267 | SetInformationJobObjectProto = WINFUNCTYPE(BOOL, # Return Type |
michael@0 | 268 | HANDLE, # Job Handle |
michael@0 | 269 | DWORD, # Type of Class next param is |
michael@0 | 270 | LPVOID, # Job Object Class |
michael@0 | 271 | DWORD # Job Object Class Length |
michael@0 | 272 | ) |
michael@0 | 273 | SetInformationJobObjectProtoFlags = ((1, "hJob", None), |
michael@0 | 274 | (1, "JobObjectInfoClass", None), |
michael@0 | 275 | (1, "lpJobObjectInfo", None), |
michael@0 | 276 | (1, "cbJobObjectInfoLength", 0)) |
michael@0 | 277 | SetInformationJobObject = SetInformationJobObjectProto(("SetInformationJobObject", |
michael@0 | 278 | windll.kernel32), |
michael@0 | 279 | SetInformationJobObjectProtoFlags) |
michael@0 | 280 | SetInformationJobObject.errcheck = ErrCheckBool |
michael@0 | 281 | |
michael@0 | 282 | # CreateJobObject() |
michael@0 | 283 | CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type |
michael@0 | 284 | LPVOID, # lpJobAttributes |
michael@0 | 285 | LPCWSTR # lpName |
michael@0 | 286 | ) |
michael@0 | 287 | |
michael@0 | 288 | CreateJobObjectFlags = ((1, "lpJobAttributes", None), |
michael@0 | 289 | (1, "lpName", None)) |
michael@0 | 290 | |
michael@0 | 291 | CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32), |
michael@0 | 292 | CreateJobObjectFlags) |
michael@0 | 293 | CreateJobObject.errcheck = ErrCheckHandle |
michael@0 | 294 | |
michael@0 | 295 | # AssignProcessToJobObject() |
michael@0 | 296 | |
michael@0 | 297 | AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 298 | HANDLE, # hJob |
michael@0 | 299 | HANDLE # hProcess |
michael@0 | 300 | ) |
michael@0 | 301 | AssignProcessToJobObjectFlags = ((1, "hJob"), |
michael@0 | 302 | (1, "hProcess")) |
michael@0 | 303 | AssignProcessToJobObject = AssignProcessToJobObjectProto( |
michael@0 | 304 | ("AssignProcessToJobObject", windll.kernel32), |
michael@0 | 305 | AssignProcessToJobObjectFlags) |
michael@0 | 306 | AssignProcessToJobObject.errcheck = ErrCheckBool |
michael@0 | 307 | |
michael@0 | 308 | # GetCurrentProcess() |
michael@0 | 309 | # because os.getPid() is way too easy |
michael@0 | 310 | GetCurrentProcessProto = WINFUNCTYPE(HANDLE # Return type |
michael@0 | 311 | ) |
michael@0 | 312 | GetCurrentProcessFlags = () |
michael@0 | 313 | GetCurrentProcess = GetCurrentProcessProto( |
michael@0 | 314 | ("GetCurrentProcess", windll.kernel32), |
michael@0 | 315 | GetCurrentProcessFlags) |
michael@0 | 316 | GetCurrentProcess.errcheck = ErrCheckHandle |
michael@0 | 317 | |
michael@0 | 318 | # IsProcessInJob() |
michael@0 | 319 | try: |
michael@0 | 320 | IsProcessInJobProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 321 | HANDLE, # Process Handle |
michael@0 | 322 | HANDLE, # Job Handle |
michael@0 | 323 | LPBOOL # Result |
michael@0 | 324 | ) |
michael@0 | 325 | IsProcessInJobFlags = ((1, "ProcessHandle"), |
michael@0 | 326 | (1, "JobHandle", HANDLE(0)), |
michael@0 | 327 | (2, "Result")) |
michael@0 | 328 | IsProcessInJob = IsProcessInJobProto( |
michael@0 | 329 | ("IsProcessInJob", windll.kernel32), |
michael@0 | 330 | IsProcessInJobFlags) |
michael@0 | 331 | IsProcessInJob.errcheck = ErrCheckBool |
michael@0 | 332 | except AttributeError: |
michael@0 | 333 | # windows 2k doesn't have this API |
michael@0 | 334 | def IsProcessInJob(process): |
michael@0 | 335 | return False |
michael@0 | 336 | |
michael@0 | 337 | |
michael@0 | 338 | # ResumeThread() |
michael@0 | 339 | |
michael@0 | 340 | def ErrCheckResumeThread(result, func, args): |
michael@0 | 341 | if result == -1: |
michael@0 | 342 | raise WinError() |
michael@0 | 343 | |
michael@0 | 344 | return args |
michael@0 | 345 | |
michael@0 | 346 | ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type |
michael@0 | 347 | HANDLE # hThread |
michael@0 | 348 | ) |
michael@0 | 349 | ResumeThreadFlags = ((1, "hThread"),) |
michael@0 | 350 | ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32), |
michael@0 | 351 | ResumeThreadFlags) |
michael@0 | 352 | ResumeThread.errcheck = ErrCheckResumeThread |
michael@0 | 353 | |
michael@0 | 354 | # TerminateProcess() |
michael@0 | 355 | |
michael@0 | 356 | TerminateProcessProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 357 | HANDLE, # hProcess |
michael@0 | 358 | UINT # uExitCode |
michael@0 | 359 | ) |
michael@0 | 360 | TerminateProcessFlags = ((1, "hProcess"), |
michael@0 | 361 | (1, "uExitCode", 127)) |
michael@0 | 362 | TerminateProcess = TerminateProcessProto( |
michael@0 | 363 | ("TerminateProcess", windll.kernel32), |
michael@0 | 364 | TerminateProcessFlags) |
michael@0 | 365 | TerminateProcess.errcheck = ErrCheckBool |
michael@0 | 366 | |
michael@0 | 367 | # TerminateJobObject() |
michael@0 | 368 | |
michael@0 | 369 | TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 370 | HANDLE, # hJob |
michael@0 | 371 | UINT # uExitCode |
michael@0 | 372 | ) |
michael@0 | 373 | TerminateJobObjectFlags = ((1, "hJob"), |
michael@0 | 374 | (1, "uExitCode", 127)) |
michael@0 | 375 | TerminateJobObject = TerminateJobObjectProto( |
michael@0 | 376 | ("TerminateJobObject", windll.kernel32), |
michael@0 | 377 | TerminateJobObjectFlags) |
michael@0 | 378 | TerminateJobObject.errcheck = ErrCheckBool |
michael@0 | 379 | |
michael@0 | 380 | # WaitForSingleObject() |
michael@0 | 381 | |
michael@0 | 382 | WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type |
michael@0 | 383 | HANDLE, # hHandle |
michael@0 | 384 | DWORD, # dwMilliseconds |
michael@0 | 385 | ) |
michael@0 | 386 | WaitForSingleObjectFlags = ((1, "hHandle"), |
michael@0 | 387 | (1, "dwMilliseconds", -1)) |
michael@0 | 388 | WaitForSingleObject = WaitForSingleObjectProto( |
michael@0 | 389 | ("WaitForSingleObject", windll.kernel32), |
michael@0 | 390 | WaitForSingleObjectFlags) |
michael@0 | 391 | |
michael@0 | 392 | # http://msdn.microsoft.com/en-us/library/ms681381%28v=vs.85%29.aspx |
michael@0 | 393 | INFINITE = -1 |
michael@0 | 394 | WAIT_TIMEOUT = 0x0102 |
michael@0 | 395 | WAIT_OBJECT_0 = 0x0 |
michael@0 | 396 | WAIT_ABANDONED = 0x0080 |
michael@0 | 397 | |
michael@0 | 398 | # http://msdn.microsoft.com/en-us/library/ms683189%28VS.85%29.aspx |
michael@0 | 399 | STILL_ACTIVE = 259 |
michael@0 | 400 | |
michael@0 | 401 | # Used when we terminate a process. |
michael@0 | 402 | ERROR_CONTROL_C_EXIT = 0x23c |
michael@0 | 403 | |
michael@0 | 404 | # GetExitCodeProcess() |
michael@0 | 405 | |
michael@0 | 406 | GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type |
michael@0 | 407 | HANDLE, # hProcess |
michael@0 | 408 | LPDWORD, # lpExitCode |
michael@0 | 409 | ) |
michael@0 | 410 | GetExitCodeProcessFlags = ((1, "hProcess"), |
michael@0 | 411 | (2, "lpExitCode")) |
michael@0 | 412 | GetExitCodeProcess = GetExitCodeProcessProto( |
michael@0 | 413 | ("GetExitCodeProcess", windll.kernel32), |
michael@0 | 414 | GetExitCodeProcessFlags) |
michael@0 | 415 | GetExitCodeProcess.errcheck = ErrCheckBool |
michael@0 | 416 | |
michael@0 | 417 | def CanCreateJobObject(): |
michael@0 | 418 | currentProc = GetCurrentProcess() |
michael@0 | 419 | if IsProcessInJob(currentProc): |
michael@0 | 420 | jobinfo = QueryInformationJobObject(HANDLE(0), 'JobObjectExtendedLimitInformation') |
michael@0 | 421 | limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] |
michael@0 | 422 | return bool(limitflags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) or bool(limitflags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK) |
michael@0 | 423 | else: |
michael@0 | 424 | return True |
michael@0 | 425 | |
michael@0 | 426 | ### testing functions |
michael@0 | 427 | |
michael@0 | 428 | def parent(): |
michael@0 | 429 | print 'Starting parent' |
michael@0 | 430 | currentProc = GetCurrentProcess() |
michael@0 | 431 | if IsProcessInJob(currentProc): |
michael@0 | 432 | print >> sys.stderr, "You should not be in a job object to test" |
michael@0 | 433 | sys.exit(1) |
michael@0 | 434 | assert CanCreateJobObject() |
michael@0 | 435 | print 'File: %s' % __file__ |
michael@0 | 436 | command = [sys.executable, __file__, '-child'] |
michael@0 | 437 | print 'Running command: %s' % command |
michael@0 | 438 | process = Popen(command) |
michael@0 | 439 | process.kill() |
michael@0 | 440 | code = process.returncode |
michael@0 | 441 | print 'Child code: %s' % code |
michael@0 | 442 | assert code == 127 |
michael@0 | 443 | |
michael@0 | 444 | def child(): |
michael@0 | 445 | print 'Starting child' |
michael@0 | 446 | currentProc = GetCurrentProcess() |
michael@0 | 447 | injob = IsProcessInJob(currentProc) |
michael@0 | 448 | print "Is in a job?: %s" % injob |
michael@0 | 449 | can_create = CanCreateJobObject() |
michael@0 | 450 | print 'Can create job?: %s' % can_create |
michael@0 | 451 | process = Popen('c:\\windows\\notepad.exe') |
michael@0 | 452 | assert process._job |
michael@0 | 453 | jobinfo = QueryInformationJobObject(process._job, 'JobObjectExtendedLimitInformation') |
michael@0 | 454 | print 'Job info: %s' % jobinfo |
michael@0 | 455 | limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] |
michael@0 | 456 | print 'LimitFlags: %s' % limitflags |
michael@0 | 457 | process.kill() |