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 |