[Tutor] How to log process handles and GDI objects in python

eryksun eryksun at gmail.com
Mon Feb 11 12:23:49 CET 2013


On Sun, Feb 10, 2013 at 11:25 PM, Pai, Yogesh M
<Yogesh.M.Pai at tektronix.com> wrote:
> I want to log the process threads and GDI objects (the one you see in
> the Windows task manager) in my python code- The idea is to use this
> data to generate a plot to check if the application leaks memory in a
> long run. Although I can use a third-party app to log this, is it
> possible to import certain library in python and log these windows
> attributes during the execution?

psutil can query the number of handles and threads for a Windows
process, but not GDI objects.

http://code.google.com/p/psutil

Getting the number of handles and gdi/user objects via the Win32 API
isn't too hard. Required functions:

OpenProcess
http://msdn.microsoft.com/en-us/library/ms684320

CloseHandle
http://msdn.microsoft.com/en-us/library/ms724211

GetProcessHandleCount
http://msdn.microsoft.com/en-us/library/ms683214

GetGuiResources
http://msdn.microsoft.com/en-us/library/ms683192

Here's a basic example with ctypes. In practice, set argtypes for each
function call (a TypeError is better than a segfault) and error check
the return value per the above docs. You can map the last error (i.e.
GetLastError) to an exception using ctypes.WinError.

(I haven't checked, but all of this is probably simple using pywin32.
Just search around the net for examples.)

    >>> import os
    >>> from ctypes import *
    >>> from ctypes.wintypes import *

    >>> PQI = 0x400 # PROCESS_QUERY_INFORMATION
    >>> pid = os.getpid()
    >>> h = windll.kernel32.OpenProcess(PQI, 0, pid)
    >>> hndcnt = DWORD()
    >>> windll.kernel32.GetProcessHandleCount(h, byref(hndcnt))
    1
    >>> hndcnt.value
    72L
    >>> GR_GDIOBJECTS, GR_USEROBJECTS = 0, 1
    >>> windll.user32.GetGuiResources(h, GR_GDIOBJECTS)
    4
    >>> windll.user32.GetGuiResources(h, GR_USEROBJECTS)
    1
    >>> windll.kernel32.CloseHandle(h)
    1

The number of threads can be found by iterating over a system snapshot
up to the desired PID. Here are the required functions and data
struct:

CreateToolhelp32Snapshot
http://msdn.microsoft.com/en-us/library/ms682489

Process32First[W]
http://msdn.microsoft.com/en-us/library/ms684834

Process32Next[W]
http://msdn.microsoft.com/en-us/library/ms684836

PROCESSENTRTY32
http://msdn.microsoft.com/en-us/library/ms684839

    class PROCESSENTRY32(Structure):
        _fields_ = [
          ('dwSize', DWORD),
          ('cntUsage', DWORD),
          ('th32ProcessID', DWORD),
          ('th32DefaultHeapID', c_void_p),
          ('th32ModuleID', DWORD),
          ('cntThreads', DWORD),
          ('th32ParentProcessID', DWORD),
          ('pcPriClassBase', c_long),
          ('dwFlags', DWORD),
          ('szExeFile', WCHAR * MAX_PATH),
        ]

        def __init__(self, *args, **kwds):
            Structure.__init__(self, *args, **kwds)
            self.dwSize = sizeof(self)

    TH32CS_SNAPPROCESS = 2
    th32cs = windll.kernel32.CreateToolhelp32Snapshot

    def get_num_threads(pid):
        num_threads = 0
        hsnap = th32cs(TH32CS_SNAPPROCESS, 0)
        pe = PROCESSENTRY32()
        r = windll.kernel32.Process32FirstW(hsnap, byref(pe))
        while r:
            if pe.th32ProcessID == pid:
                num_threads = int(pe.cntThreads)
                break
            r = windll.kernel32.Process32NextW(hsnap, byref(pe))
        windll.kernel32.CloseHandle(hsnap)
        return num_threads

Example:

    >>> from threading import Timer
    >>> pid = os.getpid()
    >>> get_num_threads(pid)
    1
    >>> for i in range(30): Timer(30, lambda: None).start()
    >>> get_num_threads(pid)
    31


More information about the Tutor mailing list