| OLD | NEW |
| (Empty) |
| 1 from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFU
NCTYPE, addressof, c_size_t, c_ulong | |
| 2 from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LARGE_INTEGER | |
| 3 | |
| 4 LPVOID = c_void_p | |
| 5 LPDWORD = POINTER(DWORD) | |
| 6 SIZE_T = c_size_t | |
| 7 ULONG_PTR = POINTER(c_ulong) | |
| 8 | |
| 9 # A ULONGLONG is a 64-bit unsigned integer. | |
| 10 # Thus there are 8 bytes in a ULONGLONG. | |
| 11 # XXX why not import c_ulonglong ? | |
| 12 ULONGLONG = BYTE * 8 | |
| 13 | |
| 14 class IO_COUNTERS(Structure): | |
| 15 # The IO_COUNTERS struct is 6 ULONGLONGs. | |
| 16 # TODO: Replace with non-dummy fields. | |
| 17 _fields_ = [('dummy', ULONGLONG * 6)] | |
| 18 | |
| 19 class JOBOBJECT_BASIC_ACCOUNTING_INFORMATION(Structure): | |
| 20 _fields_ = [('TotalUserTime', LARGE_INTEGER), | |
| 21 ('TotalKernelTime', LARGE_INTEGER), | |
| 22 ('ThisPeriodTotalUserTime', LARGE_INTEGER), | |
| 23 ('ThisPeriodTotalKernelTime', LARGE_INTEGER), | |
| 24 ('TotalPageFaultCount', DWORD), | |
| 25 ('TotalProcesses', DWORD), | |
| 26 ('ActiveProcesses', DWORD), | |
| 27 ('TotalTerminatedProcesses', DWORD)] | |
| 28 | |
| 29 class JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION(Structure): | |
| 30 _fields_ = [('BasicInfo', JOBOBJECT_BASIC_ACCOUNTING_INFORMATION), | |
| 31 ('IoInfo', IO_COUNTERS)] | |
| 32 | |
| 33 # see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx | |
| 34 class JOBOBJECT_BASIC_LIMIT_INFORMATION(Structure): | |
| 35 _fields_ = [('PerProcessUserTimeLimit', LARGE_INTEGER), | |
| 36 ('PerJobUserTimeLimit', LARGE_INTEGER), | |
| 37 ('LimitFlags', DWORD), | |
| 38 ('MinimumWorkingSetSize', SIZE_T), | |
| 39 ('MaximumWorkingSetSize', SIZE_T), | |
| 40 ('ActiveProcessLimit', DWORD), | |
| 41 ('Affinity', ULONG_PTR), | |
| 42 ('PriorityClass', DWORD), | |
| 43 ('SchedulingClass', DWORD) | |
| 44 ] | |
| 45 | |
| 46 # see http://msdn.microsoft.com/en-us/library/ms684156%28VS.85%29.aspx | |
| 47 class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(Structure): | |
| 48 _fields_ = [('BasicLimitInformation', JOBOBJECT_BASIC_LIMIT_INFORMATION), | |
| 49 ('IoInfo', IO_COUNTERS), | |
| 50 ('ProcessMemoryLimit', SIZE_T), | |
| 51 ('JobMemoryLimit', SIZE_T), | |
| 52 ('PeakProcessMemoryUsed', SIZE_T), | |
| 53 ('PeakJobMemoryUsed', SIZE_T)] | |
| 54 | |
| 55 # XXX Magical numbers like 8 should be documented | |
| 56 JobObjectBasicAndIoAccountingInformation = 8 | |
| 57 | |
| 58 # ...like magical number 9 comes from | |
| 59 # http://community.flexerasoftware.com/archive/index.php?t-181670.html | |
| 60 # I wish I had a more canonical source | |
| 61 JobObjectExtendedLimitInformation = 9 | |
| 62 | |
| 63 class JobObjectInfo(object): | |
| 64 mapping = { 'JobObjectBasicAndIoAccountingInformation': 8, | |
| 65 'JobObjectExtendedLimitInformation': 9 | |
| 66 } | |
| 67 structures = { 8: JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION, | |
| 68 9: JOBOBJECT_EXTENDED_LIMIT_INFORMATION | |
| 69 } | |
| 70 def __init__(self, _class): | |
| 71 if isinstance(_class, basestring): | |
| 72 assert _class in self.mapping, 'Class should be one of %s; you gave
%s' % (self.mapping, _class) | |
| 73 _class = self.mapping[_class] | |
| 74 assert _class in self.structures, 'Class should be one of %s; you gave %
s' % (self.structures, _class) | |
| 75 self.code = _class | |
| 76 self.info = self.structures[_class]() | |
| 77 | |
| 78 | |
| 79 QueryInformationJobObjectProto = WINFUNCTYPE( | |
| 80 BOOL, # Return type | |
| 81 HANDLE, # hJob | |
| 82 DWORD, # JobObjectInfoClass | |
| 83 LPVOID, # lpJobObjectInfo | |
| 84 DWORD, # cbJobObjectInfoLength | |
| 85 LPDWORD # lpReturnLength | |
| 86 ) | |
| 87 | |
| 88 QueryInformationJobObjectFlags = ( | |
| 89 (1, 'hJob'), | |
| 90 (1, 'JobObjectInfoClass'), | |
| 91 (1, 'lpJobObjectInfo'), | |
| 92 (1, 'cbJobObjectInfoLength'), | |
| 93 (1, 'lpReturnLength', None) | |
| 94 ) | |
| 95 | |
| 96 _QueryInformationJobObject = QueryInformationJobObjectProto( | |
| 97 ('QueryInformationJobObject', windll.kernel32), | |
| 98 QueryInformationJobObjectFlags | |
| 99 ) | |
| 100 | |
| 101 class SubscriptableReadOnlyStruct(object): | |
| 102 def __init__(self, struct): | |
| 103 self._struct = struct | |
| 104 | |
| 105 def _delegate(self, name): | |
| 106 result = getattr(self._struct, name) | |
| 107 if isinstance(result, Structure): | |
| 108 return SubscriptableReadOnlyStruct(result) | |
| 109 return result | |
| 110 | |
| 111 def __getitem__(self, name): | |
| 112 match = [fname for fname, ftype in self._struct._fields_ | |
| 113 if fname == name] | |
| 114 if match: | |
| 115 return self._delegate(name) | |
| 116 raise KeyError(name) | |
| 117 | |
| 118 def __getattr__(self, name): | |
| 119 return self._delegate(name) | |
| 120 | |
| 121 def QueryInformationJobObject(hJob, JobObjectInfoClass): | |
| 122 jobinfo = JobObjectInfo(JobObjectInfoClass) | |
| 123 result = _QueryInformationJobObject( | |
| 124 hJob=hJob, | |
| 125 JobObjectInfoClass=jobinfo.code, | |
| 126 lpJobObjectInfo=addressof(jobinfo.info), | |
| 127 cbJobObjectInfoLength=sizeof(jobinfo.info) | |
| 128 ) | |
| 129 if not result: | |
| 130 raise WinError() | |
| 131 return SubscriptableReadOnlyStruct(jobinfo.info) | |
| 132 | |
| 133 def test_qijo(): | |
| 134 from killableprocess import Popen | |
| 135 | |
| 136 popen = Popen('c:\\windows\\notepad.exe') | |
| 137 | |
| 138 try: | |
| 139 result = QueryInformationJobObject(0, 8) | |
| 140 raise AssertionError('throw should occur') | |
| 141 except WindowsError, e: | |
| 142 pass | |
| 143 | |
| 144 try: | |
| 145 result = QueryInformationJobObject(0, 1) | |
| 146 raise AssertionError('throw should occur') | |
| 147 except NotImplementedError, e: | |
| 148 pass | |
| 149 | |
| 150 result = QueryInformationJobObject(popen._job, 8) | |
| 151 if result['BasicInfo']['ActiveProcesses'] != 1: | |
| 152 raise AssertionError('expected ActiveProcesses to be 1') | |
| 153 popen.kill() | |
| 154 | |
| 155 result = QueryInformationJobObject(popen._job, 8) | |
| 156 if result.BasicInfo.ActiveProcesses != 0: | |
| 157 raise AssertionError('expected ActiveProcesses to be 0') | |
| 158 | |
| 159 if __name__ == '__main__': | |
| 160 print "testing." | |
| 161 test_qijo() | |
| 162 print "success!" | |
| OLD | NEW |