We have a winner! Huge thanks from me even though I wasn’t the OP because I've wanted to know how to do this for a while and just started to get my feet wet in this arena!!! I now have a VERY GOOD EXAMPLE of how to start looking at doing a complex task like this with all the nested references. UGH!
In [16]: get_idle_time(2) # session id = 2 Out[16]: 0.0156271 Please note that line wraps (and or a missing newline) in the previous email may cause copy/paste issues in Eryk's code around these lines: winsta.WinStationQueryInformationW.restype = wintypes.BOOLEAN # this needs to be a newline winsta.WinStationQueryInformationW.argtypes = ( wintypes.HANDLE, # ServerHandle wintypes.ULONG, # SessionId ctypes.c_long, # WinStationInformationClass wintypes.LPVOID, # pWinStationInformation wintypes.ULONG, # WinStationInformationLength wintypes.PULONG, # pReturnLength ) I HOPE THIS HELPS AND THANK YOU VERY MUCH! Steven -----Original Message----- From: Eryk Sun <eryk...@gmail.com> Sent: Wednesday, June 22, 2022 1:28 PM To: Steven Manross <ste...@manross.net> Cc: python-win32@python.org Subject: Re: [python-win32] Need a value from pywin32 On 6/21/22, Steven Manross <ste...@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()). _______________________________________________ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32