[python-win32] Need a value from pywin32

Eryk Sun eryksun at gmail.com
Wed Jun 22 16:27:59 EDT 2022


On 6/21/22, Steven Manross <steven at manross.net> wrote:
>
> class WinStationInformation(ctypes.Structure):
>     __fields__ = [
>         ('ConnectState', ctypes.c_long),
>         ('WinStationName', ctypes.wintypes.WCHAR),
>         ('LogonId', ctypes.c_ulong),
>         ('ConnectTime', ctypes.wintypes.LARGE_INTEGER),
>         ('DisconnectTime', ctypes.wintypes.LARGE_INTEGER),
>         ('LastInputTime', ctypes.wintypes.LARGE_INTEGER),
>         ('LogonTime', ctypes.wintypes.LARGE_INTEGER),
>         ('Status', ctypes.c_int()),
>         ('Domain', ctypes.wintypes.WCHAR * (17 + 1)),
>         ('UserName', ctypes.wintypes.WCHAR * (20 + 1)),
>         ('CurrentTime', ctypes.wintypes.LARGE_INTEGER),
>     ]

The above definition is incorrect for `WinStationName` and `Status`.
Defining the PROTOCOLSTATUS type for `Status` is tedious. Note also
that the ctypes attribute to set is `_fields_`, not `__fields__`, so
the above struct is actually defined with no fields (i.e. zero size).

Here's an example that defines a get_idle_time() function, based on
the difference between the current time and the last input time in the
session.

import ctypes
from ctypes import wintypes

winsta = ctypes.WinDLL('winsta', use_last_error=True)

WINSTATIONNAME_LENGTH = 32
DOMAIN_LENGTH = 17
USERNAME_LENGTH = 20
MAX_THINWIRECACHE = 4

SERVERNAME_CURRENT = None
LOGONID_CURRENT = -1

# WINSTATIONINFOCLASS
WinStationInformation = 8

# WINSTATIONSTATECLASS
State_Active = 0
State_Connected = 1
State_ConnectQuery = 2
State_Shadow = 3
State_Disconnected = 4
State_Idle = 5
State_Listen = 6
State_Reset = 7
State_Down = 8
State_Init = 9

class TSHARE_COUNTERS(ctypes.Structure):
    __slots__ = ()
    _fields_ = (
        ('Reserved', wintypes.ULONG),
    )

class PROTOCOLCOUNTERS(ctypes.Structure):
    __slots__ = ()
    class SPECIFIC(ctypes.Union):
        __slots__ = ()
        _fields_ = (
            ('TShareCounters', TSHARE_COUNTERS),
            ('Reserved', wintypes.ULONG * 100),
        )
    _fields_ = (
        ('WdBytes', wintypes.ULONG),
        ('WdFrames', wintypes.ULONG),
        ('WaitForOutBuf', wintypes.ULONG),
        ('Frames', wintypes.ULONG),
        ('Bytes', wintypes.ULONG),
        ('CompressedBytes', wintypes.ULONG),
        ('CompressFlushes', wintypes.ULONG),
        ('Errors', wintypes.ULONG),
        ('Timeouts', wintypes.ULONG),
        ('AsyncFramingError', wintypes.ULONG),
        ('AsyncOverrunError', wintypes.ULONG),
        ('AsyncOverflowError', wintypes.ULONG),
        ('AsyncParityError', wintypes.ULONG),
        ('TdErrors', wintypes.ULONG),
        ('ProtocolType', wintypes.USHORT),
        ('Length', wintypes.USHORT),
        ('Specific', SPECIFIC),
    )


class THINWIRECACHE (ctypes.Structure):
    __slots__ = ()
    _fields_ = (
        ('CacheReads', wintypes.ULONG),
        ('CacheHits', wintypes.ULONG),
    )


class RESERVED_CACHE(ctypes.Structure):
    __slots__ = ()
    _fields_ = (
        ('ThinWireCache[', THINWIRECACHE * MAX_THINWIRECACHE),
    )


class CACHE_STATISTICS(ctypes.Structure):
    __slots__ = ()
    class SPECIFIC(ctypes.Union):
        __slots__ = ()
        _fields_ = (
            ('ReservedCacheStats', RESERVED_CACHE),
            ('TShareCacheStats', wintypes.ULONG),
            ('Reserved', wintypes.ULONG * 20),
        )
    _fields_ = (
        ('ProtocolType', wintypes.USHORT),
        ('Length', wintypes.USHORT),
        ('Specific', SPECIFIC),
    )


class PROTOCOLSTATUS(ctypes.Structure):
    __slots__ = ()
    _fields_ = (
        ('Output', PROTOCOLCOUNTERS),
        ('Input', PROTOCOLCOUNTERS),
        ('Cache', CACHE_STATISTICS),
        ('AsyncSignal', wintypes.ULONG),
        ('AsyncSignalMask', wintypes.ULONG),
    )


class WINSTATIONINFORMATION(ctypes.Structure):
    __slots__ = ()
    _fields_ = (
        ('ConnectState', ctypes.c_long),
        ('WinStationName', wintypes.WCHAR * (WINSTATIONNAME_LENGTH + 1)),
        ('LogonId', wintypes.ULONG),
        ('ConnectTime', wintypes.LARGE_INTEGER),
        ('DisconnectTime', wintypes.LARGE_INTEGER),
        ('LastInputTime', wintypes.LARGE_INTEGER),
        ('LogonTime', wintypes.LARGE_INTEGER),
        ('Status', PROTOCOLSTATUS),
        ('Domain', wintypes.WCHAR * (DOMAIN_LENGTH + 1)),
        ('UserName', wintypes.WCHAR * (USERNAME_LENGTH + 1)),
        ('CurrentTime', wintypes.LARGE_INTEGER)
    )


winsta.WinStationQueryInformationW.restype = wintypes.BOOLEAN
winsta.WinStationQueryInformationW.argtypes = (
    wintypes.HANDLE, # ServerHandle
    wintypes.ULONG,  # SessionId
    ctypes.c_long,   # WinStationInformationClass
    wintypes.LPVOID, # pWinStationInformation
    wintypes.ULONG,  # WinStationInformationLength
    wintypes.PULONG, # pReturnLength
)


def get_idle_time(session_id=LOGONID_CURRENT,
                  server_handle=SERVERNAME_CURRENT):
    info = WINSTATIONINFORMATION()
    rlen = wintypes.ULONG()
    if not winsta.WinStationQueryInformationW(
                server_handle, session_id, WinStationInformation,
                ctypes.byref(info), ctypes.sizeof(info), ctypes.byref(rlen)):
        raise ctypes.WinError(ctypes.get_last_error())
    if info.ConnectState == State_Disconnected:
        last_input_time = info.DisconnectTime
    else:
        last_input_time = info.LastInputTime
    if not last_input_time:
        return None
    return (info.CurrentTime - last_input_time) / 1e7

---

Note that the `winsta` WinDLL instance is configured to capture the
last error value (i.e. use_last_error=True). Thus if
WinStationQueryInformationW() fails, the relevant OSError exception is
ctypes.WinError(ctypes.get_last_error()).


More information about the python-win32 mailing list