_Timer_GetIdleTime

  • Hallo!

    Ich stehe vor einer Aufgabe die sich als tückischer als am Anfang geglaubt habe heraus stellt: Es gibt immer (leider) Benutzer die sich einen PC teilen müssen und daher den "schnellen Benutzerwechsel" verwenden. Da ist jetzt noch nichts verwerfliches daran, aber wenn auf doch eher schwach ausgestatten Rechner 10 oder mehr Benutzer angemeldet sind wird das zum Problem.

    Jetzt soll ich einen automatischen Logout nach x Stunden Inaktivität machen. GPO gibt da keine (ist keine RDP-Sitztung), Boardmittel sind nicht vorhanden. Jetzt hatte ich die Idee ein kleines Programm dem Benutzer zu gegben der die _Timer_GetIdelTime auswertet und nach einer Zeitvorgabe ein Logoff durchführt.

    Jetzt kommt der Teil der leider nicht funktioniert: Das Programm läuft auch weiter wenn sich ein andere Benutzer anmeldet (mit Logfile wegschreiben getestet), nur das logoff sei es aus Autoit oder auch mit logoff.exe wird einfach nicht durchgeführt bzw. einfach ignoriert.

    Setzte ich ein logoff /sessionID mit erhöhten Rechten auf den auszuloggenden User ab funktioniert das. Nur aus dem Kontext heraus kann ich nicht die Idle-Time des Benutzer feststellen :(
    Quser.exe hat ein paar Bugs und zeigt nicht immer die richtige Idle-Time an...das hatte ich auch schon am Schirm!

    Nun zur AutoIT-Frage: gibt es eine Möglichkeit die Idle-Time für alle User abzufragen (mit erhöhten Rechten oder als System? Oder hat wer eine ganz andere Idee wie man das lösen kann?

    lg

    Racer

  • Nur eine Idee ohne jetzt weiter geforscht zu haben: Wird das umloggen nicht im System geloggt, ggf sogar mit mehr Informationen (z.B. Benutzername)?
    Wenn ja, könntest du das mit dem aktiven Nutzer auslesen und entsprechende Kommandos abschicken.

    Mir drängt sich aber irgendwie die Frage auf ob es nicht Sinnvoll ist sich Gedanken zu machen ob überhaupt eine spezifische Benutzeranmeldung notwendig ist (Shared Device).
    Bei 10 Benutzern kann doch nicht wirklich jeder aktiv gut darauf arbeiten zumal die Ummeldung auch jedes mal Zeit kostet (wir hatten das mal im Test nur mit 2 Nutzern und die Ummeldezeit war echt nicht schön).

  • Hallo Moombas!

    Das umloggen ist ein guter Ansatz, aber es ist auch nur ein "anmelden"....wird nicht weiter unterschieden! Der aktive User kann selbst keinen anderen User ausloggen, da fehlen die Rechte dazu.

    Ich habe nur die Möglichkeit das im betreffenden Userkontext laufen zu lassen und irgendwie ein loggoff zu ermöglichen oder ich lasse was am System laufen (mit hohen Rechten) der die Usersession auswertet und bei Bedarf dann ein Logoff auf den User/Session macht.

    Hätte ich fast vergessen: Das lauft immer nur auf den Client, also keine Auswertung übers Netz (:klatschen:)

    Bitte nicht nach den Sinn fragen - das habe ich schon vor langer Zeit aufgegeben. Ich habe einen Scherbenhaufen übergeben bekommen und muss jetzt sehen ich damit klar komme ohne den laufenden Betrieb zu behindern.

    Nur so das Du Dir den (Computer)-IQ von meinen Benutzer vorstellen kannst: Nach mehr als 10 Jahre Terminalserver wissen meine User immer noch nicht das man dort ausloggen soll. Alle machen nur das Fenster zu und gehen heim....und wenn man es ihnen sagt ist es ihnen auch egal....

    lg

    Racer

  • Ich hatte halt an sowas gedacht: https://www.manageengine.com/products/activ…%20ID%204624%20(viewed%20in,4625%20documents%20failed%20logon%20attempts.

    Ich weiß aber nicht ob dabei auch ein lock event beim anderen user generiert wird (event id 4800) den man ja bräuchte um dies zu berechnen/als Grundlage für den logoff zu nehmen.

    Und zum Rechte problem: Wenn dein Programm bei jedem user läuft, lass es doch einfach eine gemeinsame Datei nutzen, wo z.B. der Befehl zum logout für betreffende user eingetragen wird und dann vom lokal laufenden Programm ausgeführt wird. (Irgendwie habe ich gerade das Gefühl ich dneke zu kompliziert) :D

  • also, ich habe letztens rumexperimentiert mit Python, es war sehr einfach einen Windows Dienst zu erstellen.
    Der Dienst wurde unabhängig vom jeweiligen Benutzer laufen, leider weiß ich nicht ob du damit dann auch Userabmelden kannst.

    Wäre aber vielleicht ein Ansatz.

  • Hallo MojeB!
    Ob es ein Dienst ist oder ein Task der mit Systemrechte getriggert wird ist egal. Wichtig ist das ich diese Idle-Time vom Benutzer herausfinde...

    lg
    Racer

  • ich habe grad einige versuche gemacht mit Python, leider bekomme ich hier auf meinem Arbeitsrechner jedenfalls keine aktuellen Daten des Nutzers,
    mir wird angezeigt das ich seit 1970 angemeldet bin, und alle anderen Daten auch aus dem Jahr stammen sollen.

    Deswegen habe ich über die Windows API gegangen, um (Mausbewegung oder Tastatureingabe) als Aktivität zu erfassen.


    vielleicht kannst du daraus etwas adaptieren, aber es muss umsetzbar sein, ist meine Meinung,
    es kann auch sein das nun bei mir firmen intern, das mit 1970 angezeigt wird. möchte ich nicht ausschließen.


    Gruß

    Mojoe

  • Das hat mir ein Kollege gegben - verstehe ich leider genausoviel wie Dein Script, aber vielleicht kannst Du es mit Deinem kombinieren...

    param([string]$ComputerName=".")


    $cs=@'
    using System;
    using System.Runtime.InteropServices;

    public class WTSSessions
    {
    public enum WTS_TYPE_CLASS
    {
    WTSTypeProcessInfoLevel0,
    WTSTypeProcessInfoLevel1,
    WTSTypeSessionInfoLevel1
    }

    [DllImport("wtsapi32.dll")]
    public static extern void WTSFreeMemory(IntPtr ValuePointer);
    [DllImport("wtsapi32.dll")]
    public static extern void WTSFreeMemoryEx(WTS_TYPE_CLASS Type, IntPtr ValuePointer, int Count);
    [DllImport("wtsapi32.dll",SetLastError=true)]
    public static extern IntPtr WTSOpenServer(string ServerName);
    [DllImport("wtsapi32.dll", SetLastError = true)]
    public static extern IntPtr WTSOpenServerEx(string ServerName);
    [DllImport("wtsapi32.dll",SetLastError=true)]
    public static extern void WTSCloseServer(IntPtr ServerHandle);
    [DllImport("wtsapi32.dll", SetLastError = true)]
    public static extern bool WTSEnumerateSessions(IntPtr ServerHandle,UInt32 Reserved,UInt32 Version,ref IntPtr pBuffer,ref UInt32 Count);
    [DllImport("winsta.dll", SetLastError = true)]
    public static extern int WinStationQueryInformationW(IntPtr ServerHandle,uint SessionId,uint WinStationInformation,[Out] IntPtr pBuffer,uint BufferSize,ref uint BytesReturned);
    [DllImport("wtsapi32.dll", SetLastError = true)]
    public static extern bool WTSLogoffSession(IntPtr ServerHandle,uint SessionId,bool WaitForCompletion);

    private enum WINSTATIONINFOCLASS
    {
    WinStationInformation = 8
    }

    public enum WTS_CONNECTSTATE_CLASS
    {
    WTSActive,
    WTSConnected,
    WTSConnectQuery,
    WTSShadow,
    WTSDisconnected,
    WTSIdle,
    WTSListen,
    WTSReset,
    WTSDown,
    WTSInit
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct WINSTATIONINFORMATION
    {
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 72)]
    Byte[] Reserved1;
    public UInt32 SessionId;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    Byte[] Reserved2;
    public System.Runtime.InteropServices.ComTypes.FILETIME ConnectTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME DisconnectTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME LastInputTime;
    public System.Runtime.InteropServices.ComTypes.FILETIME LoginTime;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1096)]
    Byte[] Reserved3;
    public System.Runtime.InteropServices.ComTypes.FILETIME CurrentTime;
    }

    public struct WinStationTimestampInfo
    {
    public int SessionID;
    public DateTime Connect;
    public DateTime Disconnect;
    public DateTime LastInput;
    public DateTime Login;
    public DateTime Current;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct WTS_SESSION_INFO
    {
    public uint SessionID;
    public string WinStationName;
    public WTS_CONNECTSTATE_CLASS State;
    }

    public static WTS_SESSION_INFO[] GetSessions(IntPtr hServer)
    {
    IntPtr pBuffer = IntPtr.Zero;
    IntPtr pWork = IntPtr.Zero;
    UInt32 nCount = 0;
    WTS_SESSION_INFO[] oaSessionInfo = null;

    try
    {
    if (WTSEnumerateSessions(hServer, 0, 1, ref pBuffer, ref nCount))
    {
    oaSessionInfo = new WTS_SESSION_INFO[nCount];
    pWork = pBuffer;

    for (int nSession = 0; nSession < nCount; nSession++)
    {
    oaSessionInfo[nSession] = (WTS_SESSION_INFO)Marshal.PtrToStructure(pWork,typeof(WTS_SESSION_INFO));
    pWork = new IntPtr(pWork.ToInt64() + 3*IntPtr.Size);
    }
    WTSFreeMemory(pBuffer);

    return oaSessionInfo;
    }
    else
    throw new Exception("WTSSessions.GetSessions() failed (Error " + Marshal.GetLastWin32Error() + " in WTSEnumerateSessions)");
    }
    catch (Exception oExc)
    {
    if (pBuffer != IntPtr.Zero)
    WTSFreeMemory(pBuffer);
    throw oExc;
    }
    }

    public static DateTime COMFileTimeToDateTime(System.Runtime.InteropServices.ComTypes.FILETIME oFT)
    {
    long nFT = (((long)oFT.dwHighDateTime) << 32) + oFT.dwLowDateTime;
    return DateTime.FromFileTime(nFT);
    }

    public static WinStationTimestampInfo GetTimestamps(IntPtr hServer, uint nSession)
    {
    WINSTATIONINFORMATION oData = new WINSTATIONINFORMATION();
    IntPtr pBuffer;
    WinStationTimestampInfo oResult;
    uint nBytesReturned;
    int nStatus;

    pBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(oData));
    oResult = new WinStationTimestampInfo();
    nBytesReturned = 0;
    nStatus = WinStationQueryInformationW(hServer, nSession, (uint)WINSTATIONINFOCLASS.WinStationInformation, pBuffer, (uint)Marshal.SizeOf(typeof(WINSTATIONINFORMATION)), ref nBytesReturned);
    if (nStatus == 1)
    {
    oData = (WINSTATIONINFORMATION)Marshal.PtrToStructure(pBuffer, typeof(WINSTATIONINFORMATION));
    oResult.Connect = COMFileTimeToDateTime(oData.ConnectTime);
    oResult.Current = COMFileTimeToDateTime(oData.CurrentTime);
    oResult.Disconnect = COMFileTimeToDateTime(oData.DisconnectTime);
    oResult.LastInput = COMFileTimeToDateTime(oData.LastInputTime);
    oResult.Login = COMFileTimeToDateTime(oData.LoginTime);
    oResult.SessionID = (int)oData.SessionId;
    }
    Marshal.FreeHGlobal(pBuffer);

    return oResult;
    }

    }
    '@

    try { [WTSSessions] | Out-Null } catch { Add-Type -TypeDefinition $cs }

    $WTS=[WTSSessions]::WTSOpenServer($ComputerName)
    $sessions=[WTSSessions]::GetSessions($WTS)
    foreach ($session in $sessions)
    {
    if ($session.WinStationName -eq "Services" -or $session.State -eq [WTSSessions+WTS_CONNECTSTATE_CLASS]::WTSListen)
    {
    continue;
    }

    $timestamps=[WTSSessions]::GetTimestamps($WTS,$session.SessionID)
    if ($session.State -eq [WTSSessions+WTS_CONNECTSTATE_CLASS]::WTSDisconnected)
    {
    if ($timestamps.Login -eq [System.DateTime]::MinValue)
    {
    $idle=New-Object System.TimeSpan(0)
    }
    else
    {
    $idle=$timestamps.Current.Subtract($timestamps.Disconnect)
    }
    }
    elseif ($session.State -eq [WTSSessions+WTS_CONNECTSTATE_CLASS]::WTSActive)
    {
    if ($timestamps.Login -eq [System.DateTime]::MinValue)
    {
    $idle=New-Object System.TimeSpan(0)
    }
    else
    {
    $idle=$timestamps.Current.Subtract($timestamps.LastInput)
    }
    }
    else
    {
    $idle=New-Object System.TimeSpan(0);
    }

    Write-Host "$($session.WinStationName) $($session.State) $($idle)"

    if (([int]$idle.TotalHours) -gt 18)
    {
    Write-Host " ==> LOGOFF"
    [WTSSessions]::WTSLogoffSession($WTS,$session.SessionID,$true)
    }
    }
    [WTSSessions]::WTSCloseServer($WTS)

  • Das müsste PowerShell sein, was du da gepostet hast, und macht auch auf den ersten blick nichts anderes.

    1.WTSSessions enthält Informationen zum Abrufen von Infos (z.B. GetSessions und GetTimestamps).

    Die DLLs für die Windows-API, die genutzt werden: wtsapi32.dll und winsta.dll, sind normalerweise auch für die Verwaltung von Remote-Desktop-Sitzungen gedacht.

    2.Obwohl es für Remote-Desktop-Sitzungen vorgesehen ist, sollten auch normale Sitzungen funktionieren, was ja bereits mein Testskript zu mindestens für mich bestätigt hat.

    Korrigiert mich gerne wenn ich total falsch liege, ich weiß auch nicht alles, ich leite meistens einfach nur ab.

  • Da gibt es mehrere Ansätze bzw. fertige Software

    Lithnet Idle Logoff Group Policy Administrative Templates

    Auto logout for users - Microsoft Q&A
    Hi, I have a question because so far I have only found the lock screen solution.We often use multiple users on a PC in remote management. To save resources, we…
    learn.microsoft.com


    Releases · lithnet/idle-logoff
    A group-policy enabled utility for logging off idle windows user sessions - lithnet/idle-logoff
    github.com


    Gruß gmmg

  • Liebe AutoITler!

    tiefste Verbeugung vor Eurem Nowhow und Eurer Hilfsbereitschaft!

    MojoeB : Ja das hat mir meine "Mustererkennung" auch gesagt, aber mir fehlt da einfach Wissen über die Programmiersprachen um das zu verstehen. Das Powershellscript funktioniert nach den ersten Test auch nicht so wie es soll. Bei den Tests hat es mich gleich ausgeloggt - nur um 15 Stunden zu früh.

    gmmg : Volltreffer! Das ist genau das was ich gesucht habe. Opensource-Tool und gesteuert via GPO: was gibt es Besseres!

    Bericht folgt ob das so funktioniert wie beschrieben

    lg

    Racer

  • Test unter Windows 10 22H2 gesteuert mit einer Benutzer GPO: Der Test-User wurde wie definiert nach 10 Minuten ausgeloggt!
    Ich würde sagen voller Erfolg!

    vielen Danke nochmal an Alle!

    lg
    Racer