| OLD | NEW | 
| (Empty) |  | 
 |    1 # A module to expose various thread/process/job related structures and | 
 |    2 # methods from kernel32 | 
 |    3 # | 
 |    4 # The MIT License | 
 |    5 # | 
 |    6 # Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se> | 
 |    7 # | 
 |    8 # Additions and modifications written by Benjamin Smedberg | 
 |    9 # <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation | 
 |   10 # <http://www.mozilla.org/> | 
 |   11 # | 
 |   12 # More Modifications | 
 |   13 # Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com> | 
 |   14 # Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com> | 
 |   15 # | 
 |   16 # By obtaining, using, and/or copying this software and/or its | 
 |   17 # associated documentation, you agree that you have read, understood, | 
 |   18 # and will comply with the following terms and conditions: | 
 |   19 # | 
 |   20 # Permission to use, copy, modify, and distribute this software and | 
 |   21 # its associated documentation for any purpose and without fee is | 
 |   22 # hereby granted, provided that the above copyright notice appears in | 
 |   23 # all copies, and that both that copyright notice and this permission | 
 |   24 # notice appear in supporting documentation, and that the name of the | 
 |   25 # author not be used in advertising or publicity pertaining to | 
 |   26 # distribution of the software without specific, written prior | 
 |   27 # permission. | 
 |   28 # | 
 |   29 # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | 
 |   30 # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. | 
 |   31 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR | 
 |   32 # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS | 
 |   33 # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, | 
 |   34 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION | 
 |   35 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
 |   36  | 
 |   37 from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFU
     NCTYPE | 
 |   38 from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WO
     RD | 
 |   39 from qijo import QueryInformationJobObject | 
 |   40  | 
 |   41 LPVOID = c_void_p | 
 |   42 LPBYTE = POINTER(BYTE) | 
 |   43 LPDWORD = POINTER(DWORD) | 
 |   44 LPBOOL = POINTER(BOOL) | 
 |   45  | 
 |   46 def ErrCheckBool(result, func, args): | 
 |   47     """errcheck function for Windows functions that return a BOOL True | 
 |   48     on success""" | 
 |   49     if not result: | 
 |   50         raise WinError() | 
 |   51     return args | 
 |   52  | 
 |   53  | 
 |   54 # AutoHANDLE | 
 |   55  | 
 |   56 class AutoHANDLE(HANDLE): | 
 |   57     """Subclass of HANDLE which will call CloseHandle() on deletion.""" | 
 |   58      | 
 |   59     CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE) | 
 |   60     CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32)) | 
 |   61     CloseHandle.errcheck = ErrCheckBool | 
 |   62      | 
 |   63     def Close(self): | 
 |   64         if self.value and self.value != HANDLE(-1).value: | 
 |   65             self.CloseHandle(self) | 
 |   66             self.value = 0 | 
 |   67      | 
 |   68     def __del__(self): | 
 |   69         self.Close() | 
 |   70  | 
 |   71     def __int__(self): | 
 |   72         return self.value | 
 |   73  | 
 |   74 def ErrCheckHandle(result, func, args): | 
 |   75     """errcheck function for Windows functions that return a HANDLE.""" | 
 |   76     if not result: | 
 |   77         raise WinError() | 
 |   78     return AutoHANDLE(result) | 
 |   79  | 
 |   80 # PROCESS_INFORMATION structure | 
 |   81  | 
 |   82 class PROCESS_INFORMATION(Structure): | 
 |   83     _fields_ = [("hProcess", HANDLE), | 
 |   84                 ("hThread", HANDLE), | 
 |   85                 ("dwProcessID", DWORD), | 
 |   86                 ("dwThreadID", DWORD)] | 
 |   87  | 
 |   88     def __init__(self): | 
 |   89         Structure.__init__(self) | 
 |   90          | 
 |   91         self.cb = sizeof(self) | 
 |   92  | 
 |   93 LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION) | 
 |   94  | 
 |   95 # STARTUPINFO structure | 
 |   96  | 
 |   97 class STARTUPINFO(Structure): | 
 |   98     _fields_ = [("cb", DWORD), | 
 |   99                 ("lpReserved", LPWSTR), | 
 |  100                 ("lpDesktop", LPWSTR), | 
 |  101                 ("lpTitle", LPWSTR), | 
 |  102                 ("dwX", DWORD), | 
 |  103                 ("dwY", DWORD), | 
 |  104                 ("dwXSize", DWORD), | 
 |  105                 ("dwYSize", DWORD), | 
 |  106                 ("dwXCountChars", DWORD), | 
 |  107                 ("dwYCountChars", DWORD), | 
 |  108                 ("dwFillAttribute", DWORD), | 
 |  109                 ("dwFlags", DWORD), | 
 |  110                 ("wShowWindow", WORD), | 
 |  111                 ("cbReserved2", WORD), | 
 |  112                 ("lpReserved2", LPBYTE), | 
 |  113                 ("hStdInput", HANDLE), | 
 |  114                 ("hStdOutput", HANDLE), | 
 |  115                 ("hStdError", HANDLE) | 
 |  116                 ] | 
 |  117 LPSTARTUPINFO = POINTER(STARTUPINFO) | 
 |  118  | 
 |  119 SW_HIDE                 = 0 | 
 |  120  | 
 |  121 STARTF_USESHOWWINDOW    = 0x01 | 
 |  122 STARTF_USESIZE          = 0x02 | 
 |  123 STARTF_USEPOSITION      = 0x04 | 
 |  124 STARTF_USECOUNTCHARS    = 0x08 | 
 |  125 STARTF_USEFILLATTRIBUTE = 0x10 | 
 |  126 STARTF_RUNFULLSCREEN    = 0x20 | 
 |  127 STARTF_FORCEONFEEDBACK  = 0x40 | 
 |  128 STARTF_FORCEOFFFEEDBACK = 0x80 | 
 |  129 STARTF_USESTDHANDLES    = 0x100 | 
 |  130  | 
 |  131 # EnvironmentBlock | 
 |  132  | 
 |  133 class EnvironmentBlock: | 
 |  134     """An object which can be passed as the lpEnv parameter of CreateProcess. | 
 |  135     It is initialized with a dictionary.""" | 
 |  136  | 
 |  137     def __init__(self, dict): | 
 |  138         if not dict: | 
 |  139             self._as_parameter_ = None | 
 |  140         else: | 
 |  141             values = ["%s=%s" % (key, value) | 
 |  142                       for (key, value) in dict.iteritems()] | 
 |  143             values.append("") | 
 |  144             self._as_parameter_ = LPCWSTR("\0".join(values)) | 
 |  145          | 
 |  146 # CreateProcess() | 
 |  147  | 
 |  148 CreateProcessProto = WINFUNCTYPE(BOOL,                  # Return type | 
 |  149                                  LPCWSTR,               # lpApplicationName | 
 |  150                                  LPWSTR,                # lpCommandLine | 
 |  151                                  LPVOID,                # lpProcessAttributes | 
 |  152                                  LPVOID,                # lpThreadAttributes | 
 |  153                                  BOOL,                  # bInheritHandles | 
 |  154                                  DWORD,                 # dwCreationFlags | 
 |  155                                  LPVOID,                # lpEnvironment | 
 |  156                                  LPCWSTR,               # lpCurrentDirectory | 
 |  157                                  LPSTARTUPINFO,         # lpStartupInfo | 
 |  158                                  LPPROCESS_INFORMATION  # lpProcessInformation | 
 |  159                                  ) | 
 |  160  | 
 |  161 CreateProcessFlags = ((1, "lpApplicationName", None), | 
 |  162                       (1, "lpCommandLine"), | 
 |  163                       (1, "lpProcessAttributes", None), | 
 |  164                       (1, "lpThreadAttributes", None), | 
 |  165                       (1, "bInheritHandles", True), | 
 |  166                       (1, "dwCreationFlags", 0), | 
 |  167                       (1, "lpEnvironment", None), | 
 |  168                       (1, "lpCurrentDirectory", None), | 
 |  169                       (1, "lpStartupInfo"), | 
 |  170                       (2, "lpProcessInformation")) | 
 |  171  | 
 |  172 def ErrCheckCreateProcess(result, func, args): | 
 |  173     ErrCheckBool(result, func, args) | 
 |  174     # return a tuple (hProcess, hThread, dwProcessID, dwThreadID) | 
 |  175     pi = args[9] | 
 |  176     return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.d
     wThreadID | 
 |  177  | 
 |  178 CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32), | 
 |  179                                    CreateProcessFlags) | 
 |  180 CreateProcess.errcheck = ErrCheckCreateProcess | 
 |  181  | 
 |  182 # flags for CreateProcess | 
 |  183 CREATE_BREAKAWAY_FROM_JOB = 0x01000000 | 
 |  184 CREATE_DEFAULT_ERROR_MODE = 0x04000000 | 
 |  185 CREATE_NEW_CONSOLE = 0x00000010 | 
 |  186 CREATE_NEW_PROCESS_GROUP = 0x00000200 | 
 |  187 CREATE_NO_WINDOW = 0x08000000 | 
 |  188 CREATE_SUSPENDED = 0x00000004 | 
 |  189 CREATE_UNICODE_ENVIRONMENT = 0x00000400 | 
 |  190  | 
 |  191 # flags for job limit information | 
 |  192 # see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx | 
 |  193 JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800 | 
 |  194 JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000 | 
 |  195  | 
 |  196 # XXX these flags should be documented | 
 |  197 DEBUG_ONLY_THIS_PROCESS = 0x00000002 | 
 |  198 DEBUG_PROCESS = 0x00000001 | 
 |  199 DETACHED_PROCESS = 0x00000008 | 
 |  200  | 
 |  201 # CreateJobObject() | 
 |  202  | 
 |  203 CreateJobObjectProto = WINFUNCTYPE(HANDLE,             # Return type | 
 |  204                                    LPVOID,             # lpJobAttributes | 
 |  205                                    LPCWSTR             # lpName | 
 |  206                                    ) | 
 |  207  | 
 |  208 CreateJobObjectFlags = ((1, "lpJobAttributes", None), | 
 |  209                         (1, "lpName", None)) | 
 |  210  | 
 |  211 CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32), | 
 |  212                                        CreateJobObjectFlags) | 
 |  213 CreateJobObject.errcheck = ErrCheckHandle | 
 |  214  | 
 |  215 # AssignProcessToJobObject() | 
 |  216  | 
 |  217 AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL,      # Return type | 
 |  218                                             HANDLE,    # hJob | 
 |  219                                             HANDLE     # hProcess | 
 |  220                                             ) | 
 |  221 AssignProcessToJobObjectFlags = ((1, "hJob"), | 
 |  222                                  (1, "hProcess")) | 
 |  223 AssignProcessToJobObject = AssignProcessToJobObjectProto( | 
 |  224     ("AssignProcessToJobObject", windll.kernel32), | 
 |  225     AssignProcessToJobObjectFlags) | 
 |  226 AssignProcessToJobObject.errcheck = ErrCheckBool | 
 |  227  | 
 |  228 # GetCurrentProcess() | 
 |  229 # because os.getPid() is way too easy | 
 |  230 GetCurrentProcessProto = WINFUNCTYPE(HANDLE    # Return type | 
 |  231                                      ) | 
 |  232 GetCurrentProcessFlags = () | 
 |  233 GetCurrentProcess = GetCurrentProcessProto( | 
 |  234     ("GetCurrentProcess", windll.kernel32), | 
 |  235     GetCurrentProcessFlags) | 
 |  236 GetCurrentProcess.errcheck = ErrCheckHandle | 
 |  237  | 
 |  238 # IsProcessInJob() | 
 |  239 try: | 
 |  240     IsProcessInJobProto = WINFUNCTYPE(BOOL,     # Return type | 
 |  241                                       HANDLE,   # Process Handle | 
 |  242                                       HANDLE,   # Job Handle | 
 |  243                                       LPBOOL      # Result | 
 |  244                                       ) | 
 |  245     IsProcessInJobFlags = ((1, "ProcessHandle"), | 
 |  246                            (1, "JobHandle", HANDLE(0)), | 
 |  247                            (2, "Result")) | 
 |  248     IsProcessInJob = IsProcessInJobProto( | 
 |  249         ("IsProcessInJob", windll.kernel32), | 
 |  250         IsProcessInJobFlags) | 
 |  251     IsProcessInJob.errcheck = ErrCheckBool  | 
 |  252 except AttributeError: | 
 |  253     # windows 2k doesn't have this API | 
 |  254     def IsProcessInJob(process): | 
 |  255         return False | 
 |  256  | 
 |  257  | 
 |  258 # ResumeThread() | 
 |  259  | 
 |  260 def ErrCheckResumeThread(result, func, args): | 
 |  261     if result == -1: | 
 |  262         raise WinError() | 
 |  263  | 
 |  264     return args | 
 |  265  | 
 |  266 ResumeThreadProto = WINFUNCTYPE(DWORD,      # Return type | 
 |  267                                 HANDLE      # hThread | 
 |  268                                 ) | 
 |  269 ResumeThreadFlags = ((1, "hThread"),) | 
 |  270 ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32), | 
 |  271                                  ResumeThreadFlags) | 
 |  272 ResumeThread.errcheck = ErrCheckResumeThread | 
 |  273  | 
 |  274 # TerminateProcess() | 
 |  275  | 
 |  276 TerminateProcessProto = WINFUNCTYPE(BOOL,   # Return type | 
 |  277                                     HANDLE, # hProcess | 
 |  278                                     UINT    # uExitCode | 
 |  279                                     ) | 
 |  280 TerminateProcessFlags = ((1, "hProcess"), | 
 |  281                          (1, "uExitCode", 127)) | 
 |  282 TerminateProcess = TerminateProcessProto( | 
 |  283     ("TerminateProcess", windll.kernel32), | 
 |  284     TerminateProcessFlags) | 
 |  285 TerminateProcess.errcheck = ErrCheckBool | 
 |  286  | 
 |  287 # TerminateJobObject() | 
 |  288  | 
 |  289 TerminateJobObjectProto = WINFUNCTYPE(BOOL,   # Return type | 
 |  290                                       HANDLE, # hJob | 
 |  291                                       UINT    # uExitCode | 
 |  292                                       ) | 
 |  293 TerminateJobObjectFlags = ((1, "hJob"), | 
 |  294                            (1, "uExitCode", 127)) | 
 |  295 TerminateJobObject = TerminateJobObjectProto( | 
 |  296     ("TerminateJobObject", windll.kernel32), | 
 |  297     TerminateJobObjectFlags) | 
 |  298 TerminateJobObject.errcheck = ErrCheckBool | 
 |  299  | 
 |  300 # WaitForSingleObject() | 
 |  301  | 
 |  302 WaitForSingleObjectProto = WINFUNCTYPE(DWORD,  # Return type | 
 |  303                                        HANDLE, # hHandle | 
 |  304                                        DWORD,  # dwMilliseconds | 
 |  305                                        ) | 
 |  306 WaitForSingleObjectFlags = ((1, "hHandle"), | 
 |  307                             (1, "dwMilliseconds", -1)) | 
 |  308 WaitForSingleObject = WaitForSingleObjectProto( | 
 |  309     ("WaitForSingleObject", windll.kernel32), | 
 |  310     WaitForSingleObjectFlags) | 
 |  311  | 
 |  312 INFINITE = -1 | 
 |  313 WAIT_TIMEOUT = 0x0102 | 
 |  314 WAIT_OBJECT_0 = 0x0 | 
 |  315 WAIT_ABANDONED = 0x0080 | 
 |  316  | 
 |  317 # GetExitCodeProcess() | 
 |  318  | 
 |  319 GetExitCodeProcessProto = WINFUNCTYPE(BOOL,    # Return type | 
 |  320                                       HANDLE,  # hProcess | 
 |  321                                       LPDWORD, # lpExitCode | 
 |  322                                       ) | 
 |  323 GetExitCodeProcessFlags = ((1, "hProcess"), | 
 |  324                            (2, "lpExitCode")) | 
 |  325 GetExitCodeProcess = GetExitCodeProcessProto( | 
 |  326     ("GetExitCodeProcess", windll.kernel32), | 
 |  327     GetExitCodeProcessFlags) | 
 |  328 GetExitCodeProcess.errcheck = ErrCheckBool | 
 |  329  | 
 |  330 def CanCreateJobObject(): | 
 |  331     currentProc = GetCurrentProcess() | 
 |  332     if IsProcessInJob(currentProc): | 
 |  333         jobinfo = QueryInformationJobObject(HANDLE(0), 'JobObjectExtendedLimitIn
     formation') | 
 |  334         limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] | 
 |  335         return bool(limitflags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) or bool(limitfla
     gs & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK) | 
 |  336     else: | 
 |  337         return True | 
 |  338  | 
 |  339 ### testing functions | 
 |  340  | 
 |  341 def parent(): | 
 |  342     print 'Starting parent' | 
 |  343     currentProc = GetCurrentProcess() | 
 |  344     if IsProcessInJob(currentProc): | 
 |  345         print >> sys.stderr, "You should not be in a job object to test" | 
 |  346         sys.exit(1) | 
 |  347     assert CanCreateJobObject() | 
 |  348     print 'File: %s' % __file__ | 
 |  349     command = [sys.executable, __file__, '-child'] | 
 |  350     print 'Running command: %s' % command | 
 |  351     process = Popen(command) | 
 |  352     process.kill() | 
 |  353     code = process.returncode | 
 |  354     print 'Child code: %s' % code | 
 |  355     assert code == 127 | 
 |  356          | 
 |  357 def child(): | 
 |  358     print 'Starting child' | 
 |  359     currentProc = GetCurrentProcess() | 
 |  360     injob = IsProcessInJob(currentProc) | 
 |  361     print "Is in a job?: %s" % injob | 
 |  362     can_create = CanCreateJobObject() | 
 |  363     print 'Can create job?: %s' % can_create | 
 |  364     process = Popen('c:\\windows\\notepad.exe') | 
 |  365     assert process._job | 
 |  366     jobinfo = QueryInformationJobObject(process._job, 'JobObjectExtendedLimitInf
     ormation') | 
 |  367     print 'Job info: %s' % jobinfo | 
 |  368     limitflags = jobinfo['BasicLimitInformation']['LimitFlags'] | 
 |  369     print 'LimitFlags: %s' % limitflags | 
 |  370     process.kill() | 
 |  371  | 
 |  372 if __name__ == '__main__': | 
 |  373     import sys | 
 |  374     from killableprocess import Popen | 
 |  375     nargs = len(sys.argv[1:]) | 
 |  376     if nargs: | 
 |  377         if nargs != 1 or sys.argv[1] != '-child': | 
 |  378             raise AssertionError('Wrong flags; run like `python /path/to/winproc
     ess.py`') | 
 |  379         child() | 
 |  380     else: | 
 |  381         parent() | 
| OLD | NEW |