Re: finding PID through handle with Ctypes

eryk sun <[email protected]> Mon, 25 Sep 2017 02:48:02 -0500
Newsgroups gmane.comp.python.ctypes
Message-ID <CACL+1avw-CjyPKUXtOrKxRGoafRBz+WV90RJEQdzPQ6uN+WvJg@mail.gmail.com>
On Sun, Sep 24, 2017 at 3:08 PM, Michael C
<[email protected]> wrote:
> I tried this, but it returns zero, is it because I need to initiate the
> variable, PID, in DWORD, somehow?
>
> [...]
>
> PID = 0
> print(User32.GetWindowThreadProcessId(handle, PID))
> print(PID)

The second argument, lpdwProcessId, should be a pointer to a DWORD. For example:

    pid = wintypes.DWORD()
    tid = user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
    print(pid.value)

lpdwProcessId is optional, however, and can be NULL if you don't need
the process ID. The ctypes equivalent to NULL is passing None. Passing
the integer 0, as you did, is undefined behavior, especially with
64-bit Python. The default conversion is to a 32-bit C int, and ctypes
doesn't guarantee that the value will be cast to uintptr_t, though
recent versions of Python 3 do implement this. There's a chance in
Python 2 that the high DWORD will be random garbage, which may cause
your program to fail with an access violation.

Note that GetWindowThreadProcessId [1] tells a white lie for console
applications. A console keeps track of the process and thread ID of
every application that's attached to it. You can get the list of
attached processes by calling GetConsoleProcessList [2]. Additionally
a console stores the process and thread identifiers of its nominal
owner as window data at offsets 0 and 4, respectively, which you can
query by calling GetWindowLongW.

When the window manager sees that you're asking for the owner of a
console window, instead of returning the window's *true* owner (i.e.
conhost.exe), it returns the console's *nominal* owner. For example:

    >>> hwnd = kernel32.GetConsoleWindow()
    >>> con_pid = user32.GetWindowLongW(hwnd, 0)
    >>> con_tid = user32.GetWindowLongW(hwnd, 4)
    >>> con_pid, con_tid
    (3556, 3948)

    >>> tid = user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
    >>> (pid.value, tid)
    (3556, 3948)

Process ID 3556 is the instance of CMD from which I ran Python. In
this case, the console only has two attached processes. If I kill CMD,
the console will make Python its new nominal owner. For example:

Terminate CMD:

    >>> hp = kernel32.OpenProcess(1, 0, 3556)
    >>> kernel32.TerminateProcess(hp, 1)
    1

Verify that the console promoted my current Python process as its owner:

    >>> py_pid = kernel32.GetCurrentProcessId()
    >>> py_tid = kernel32.GetCurrentThreadId()
    >>> py_pid, py_tid
    (4888, 3008)

    >>> con_pid = user32.GetWindowLongW(hwnd, 0)
    >>> con_tid = user32.GetWindowLongW(hwnd, 4)
    >>> con_pid, con_tid
    (4888, 3008)

    >>> tid = user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
    >>> (pid.value, tid)
    (4888, 3008)

[1]: https://msdn.microsoft.com/en-us/library/ms633522
[2]: https://docs.microsoft.com/en-us/windows/console/getconsoleprocesslist

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot