Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Den aktiven Benutzer bzw Status des Benutzers bekommen (https://www.delphipraxis.net/57952-den-aktiven-benutzer-bzw-status-des-benutzers-bekommen.html)

Scorpion3000 29. Nov 2005 11:03


Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Hallo Leute!

Hab in diesem Forum leider noch nichts zu diesem Thema gefunden. In mühevoller Kleinarbeit hab ich mir den Code selber zusammen gebaut:

Delphi-Quellcode:
const
  WTS_CURRENT_SERVER_HANDLE = 0;

type
  PTOKEN_USER = ^TOKEN_USER;
  _TOKEN_USER = record
    User: TSidAndAttributes;
  end;
  TOKEN_USER = _TOKEN_USER;

  USHORT = word;
   
  _LSA_UNICODE_STRING = record
    Length: USHORT;
    MaximumLength: USHORT;
    Buffer: LPWSTR;
  end;
  LSA_UNICODE_STRING = _LSA_UNICODE_STRING;

  PLuid = ^LUID;
  _LUID = record
    LowPart: DWORD;
    HighPart: LongInt;
  end;
  LUID = _LUID;
 
  _SECURITY_LOGON_TYPE = (
    seltFiller0, seltFiller1,
    Interactive,
    Network,
    Batch,
    Service,
    Proxy,
    Unlock,
    NetworkCleartext,
    NewCredentials,
    RemoteInteractive,
    CachedInteractive,
    CachedRemoteInteractive);
  SECURITY_LOGON_TYPE = _SECURITY_LOGON_TYPE;

  PSECURITY_LOGON_SESSION_DATA = ^SECURITY_LOGON_SESSION_DATA;
  _SECURITY_LOGON_SESSION_DATA = record
    Size: ULONG;
    LogonId: LUID;
    UserName: LSA_UNICODE_STRING;
    LogonDomain: LSA_UNICODE_STRING;
    AuthenticationPackage: LSA_UNICODE_STRING;
    LogonType: SECURITY_LOGON_TYPE;
    Session: ULONG;
    Sid: PSID;
    LogonTime: LARGE_INTEGER;
    LogonServer: LSA_UNICODE_STRING;
    DnsDomainName: LSA_UNICODE_STRING;
    Upn: LSA_UNICODE_STRING;
  end;
  SECURITY_LOGON_SESSION_DATA = _SECURITY_LOGON_SESSION_DATA;

  _WTS_INFO_CLASS = (
    WTSInitialProgram,
    WTSApplicationName,
    WTSWorkingDirectory,
    WTSOEMId,
    WTSSessionId,
    WTSUserName,
    WTSWinStationName,
    WTSDomainName,
    WTSConnectState,
    WTSClientBuildNumber,
    WTSClientName,
    WTSClientDirectory,
    WTSClientProductId,
    WTSClientHardwareId,
    WTSClientAddress,
    WTSClientDisplay,
    WTSClientProtocolType);
  WTS_INFO_CLASS = _WTS_INFO_CLASS;

  _WTS_CONNECTSTATE_CLASS = (
    WTSActive,             // User logged on to WinStation
    WTSConnected,          // WinStation connected to client
    WTSConnectQuery,       // In the process of connecting to client
    WTSShadow,             // Shadowing another WinStation
    WTSDisconnected,       // WinStation logged on without client
    WTSIdle,               // Waiting for client to connect
    WTSListen,             // WinStation is listening for connection
    WTSReset,              // WinStation is being reset
    WTSDown,               // WinStation is down due to error
    WTSInit);              // WinStation in initialization
  WTS_CONNECTSTATE_CLASS = _WTS_CONNECTSTATE_CLASS;

  function LsaGetLogonSessionData(LogonId: PLUID;
     var ppLogonSessionData: PSECURITY_LOGON_SESSION_DATA): LongInt; stdcall;
     external 'Secur32.dll';

  function LsaNtStatusToWinError(Status: cardinal): ULONG; stdcall;
     external 'Advapi32.dll';

  function LsaEnumerateLogonSessions(Count: PULONG; List: PLUID): LongInt;
     stdcall; external 'Secur32.dll';

  function WTSQuerySessionInformationA(hServer: THandle; SessionId: DWORD;
     WTSInfoClass: WTS_INFO_CLASS; var pBuffer: Pointer;
     var pBytesReturned: DWORD): BOOL; stdcall; external 'Wtsapi32.dll';

implementation

{$R *.dfm}

function GetActiveUserName: string;
var
   Count: cardinal;
   List: PLUID;
   sessionData: PSECURITY_LOGON_SESSION_DATA;
   i1: integer;
   SizeNeeded, SizeNeeded2: DWORD;
   OwnerName, DomainName: PChar;
   OwnerType: SID_NAME_USE;
   pBuffer: Pointer;
   pBytesreturned: DWord;
begin
   result:= nil;
   //Auflisten der LogOnSessions
   i1:= lsaNtStatusToWinError(LsaEnumerateLogonSessions(@Count, @List));
   if i1 = 0 then begin
      i1:= -1;
      if Count > 0 then begin
         repeat
             inc(i1);
             LsaGetLogonSessionData(List, sessionData);
             //Wenn es sich um einen interaktive LogOnSession handelt
             if sessionData.LogonType = Interactive then begin
                //Prufen ob es sich um einen Benutzer handelt
                SizeNeeded := MAX_PATH;
                SizeNeeded2:= MAX_PATH;
                GetMem(OwnerName, MAX_PATH);
                GetMem(DomainName, MAX_PATH);
                if LookupAccountSID(nil, sessionData.SID, OwnerName,
                   SizeNeeded, DomainName, SizeNeeded2, OwnerType) then begin
                   if OwnerType = 1 then begin
                      //Wenn Benutzer verbunden
                      if WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE,
                         sessionData.Session, WTSConnectState, pBuffer,
                         pBytesreturned) then begin
                         if WTS_CONNECTSTATE_CLASS(pBuffer^) = WTSActive then
                            result:= sessionData.UserName.Buffer;
                      end;
                   end;
                end;
             end;
             inc(List);
         until (i1 = Count-1) or (result <> nil);
      end;
   end;
end;
Vielleicht entdeckt noch irgend jemand einen Fehler bzw kennt eine bessere Methode.
Ich hoff ich kann jemandem damit helfen!

Mfg Scorpion 3000

Christian Seehase 29. Nov 2005 15:25

Re: Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Moin Scorpion,

hast Du Dir schon mal die Funktion GetUserName angeschaut?

Zitat:

Zitat von PSDK
The GetUserName function retrieves the user name of the current thread. This is the name of the user currently logged onto the system.


Scorpion3000 30. Nov 2005 14:56

Re: Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Sorry! Hab noch etwas vergessen: Diese Quelltext ist nur für Multi-User Betriebssysteme (Win Xp aufwärts).

Die genannte Methode "GetUserName" von Christian Seehase ist unter Win Xp anwendbar. Allerdings liefert die Funktion "GetUserName" nur den Benutzernamen, der mit dem aufrufend Thread/Process verbunden ist bzw unter dem das Prog läuft. Dieser Benutzer muss NICHT automatisch jener Benutzer sein der aktiv ist. (zB. Werft einmal einen Blick in den Taskmanager unter Benutzer. In der Spalte "Status" kann man feststellen, dass (wenn ein oder mehrere Benutzer angemeldet sind) nur einer aktiv ist (sofern kein Benutzer sich Remote anmeldet). Ihr könnt ja kurz einmal ein kleines Prog schreiben, das in einem gewissen Intervall mittels "GetUserName" den momentanen Benutzernamen in einer Listbox ausgibt. Wechselt (nicht ausloggen) dann den Benutzer für kurze Zeit zu einem anderen Benutzer. Dann wieder zurück zu dem Benutzer unter dem ihr das Prog gestartet habt. Das Programm hat brav den Benutzernamen ausgegeben, allerdings halt den, unter dem das Programm läuft und nicht den, zu dem ihr kurz gewechselt habt!)

Interessant ist der obige Quelltext vor allem für Dienste, die Programme unter dem gerade aktiven Benutzer ausführen wollen (Falls jemand interesse hat kann ich bei Zeiten einmal den Quelltext posten).

Mfg Scorpion3000

Olli 6. Dez 2005 20:20

Re: Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Zitat:

Zitat von Scorpion3000
Sorry! Hab noch etwas vergessen: Diese Quelltext ist nur für Multi-User Betriebssysteme (Win Xp aufwärts).

Kleiner Tip: es heißt nicht "Multi-User Betriebssysteme", denn das sind NT 3.51 und 4.0 und Windows 2000 auch - was du meinst sind installierte und laufende Terminal Services (TS).

Zitat:

Zitat von Scorpion3000
Interessant ist der obige Quelltext vor allem für Dienste, die Programme unter dem gerade aktiven Benutzer ausführen wollen (Falls jemand interesse hat kann ich bei Zeiten einmal den Quelltext posten).

Man kann es eben nicht ermitteln. 1.) es gibt auch für XP (Pro/Home) Erweiterungen, die TS voll freischalten und 2.) kann auf einem Server-OS (NT 4.0 - 2003 Server) mehr als ein Benutzer auf einmal an jeweils einem Terminal aktiv sein.

Also stimmt entweder am Code etwas nicht, oder du machst Annahmen, die nicht gerechtfertigt sind (i.e. nur einer kann aktiv sein).

Scorpion3000 7. Dez 2005 00:00

Re: Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Hallo Olli!

Zitat:

Kleiner Tip: es heißt nicht "Multi-User Betriebssysteme", denn das sind NT 3.51 und 4.0 und Windows 2000 auch - was du meinst sind installierte und laufende Terminal Services (TS).
Das ist richtig. Es müssen Betriebssysteme mit installierten Terminal-Services sein. Dh NT 4.0 aufwärts mit installierten Terminal Services. Ab Windows Xp imho sind diese von Haus aus dabei.


Zitat:

2.) kann auf einem Server-OS (NT 4.0 - 2003 Server) mehr als ein Benutzer auf einmal an jeweils einem Terminal aktiv sein
Zuerst muss ich mal dazu sagen, dass ich unter "aktiv" den Benutzer meine, der gerade "lokal", also an der Konsole angemeldet ist.
Habe einen sehr wichtige Abfrage vergessen! Hier ist die neue Funktion mit der Abfrage:

Delphi-Quellcode:
function WTSGetActiveConsoleSessionId: DWORD; external 'Kernel32.dll';

function GetActiveUserName: string;
var
   Count: cardinal;
   List: PLUID;
   sessionData: PSECURITY_LOGON_SESSION_DATA;
   i1: integer;
   SizeNeeded, SizeNeeded2: DWORD;
   OwnerName, DomainName: PChar;
   OwnerType: SID_NAME_USE;
   pBuffer: Pointer;
   pBytesreturned: DWord;
begin
   result:= nil;
   //Auflisten der LogOnSessions
   i1:= lsaNtStatusToWinError(LsaEnumerateLogonSessions(@Count, @List));
   if i1 = 0 then begin
      i1:= -1;
      if Count > 0 then begin
         repeat
             inc(i1);
             LsaGetLogonSessionData(List, sessionData);
             //Wenn es sich um einen interaktive LogOnSession handelt
             if sessionData.LogonType = Interactive then begin
                //Prufen ob es sich um einen Benutzer handelt
                SizeNeeded := MAX_PATH;
                SizeNeeded2:= MAX_PATH;
                GetMem(OwnerName, MAX_PATH);
                GetMem(DomainName, MAX_PATH);
                if LookupAccountSID(nil, sessionData.SID, OwnerName,
                   SizeNeeded, DomainName, SizeNeeded2, OwnerType) then begin
                   if OwnerType = 1 then begin
                      //Wenn Benutzer an lokaler console angemeldet
                      //unter Windows NT 4.0 und Windows 2000 ist die SessionID der Konsole immer "0" dh.:
                      //if sessionData.Session = 0 then
                      if sessionData.Session = WTSGetActiveConsoleSessionId then //<- Hab ich vergessen
                      begin
                         //Wenn Benutzer aktiv
                         if WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE,
                            sessionData.Session, WTSConnectState, pBuffer,
                            pBytesreturned) then begin
                            if WTS_CONNECTSTATE_CLASS(pBuffer^) =
                               WTSActive then
                               result:= sessionData.UserName.Buffer;
                         end;
                      end;
                   end;
                end;
                FreeMem(OwnerName);
                FreeMem(DomainName);
             end;
             inc(List);
         until (i1 = Count-1) or (result <> nil);
      end;
   end;
end;
Danke für dein Feedback. Hätte das fast übersehen. So sollte es passen.

Mfg Scorpion3000

Olli 7. Dez 2005 09:49

Re: Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Stimmt, so sollte es funktionieren. Wenn man keine TS hat, ist man leicht gelackmeiert. Das Problem ist dabei, daß man "verbundene" Benutzer oder Benutzer die "interaktiv" über eine Konsole (zB über SSH oder PSEXEC von Sysinternals) laufen nicht vom Rest unterscheiden kann. Die einzige Unterscheidung sind die laufenden Prozesse. Und genau dabei kann es zu ziemlichen Ungenauigkeiten kommen.

Ach ja, die TS sind ab XP Home dabei (deswegen ja "schnelle Benutzerumschaltung").

Manzoni 11. Apr 2007 16:41

Re: Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Hi!

Ich schreibe grade einen Dienst, der den obigen Quelltest verwendet. Leider steigt der Speicherbedarf meines Dienstes pro Aufruf der Prozedur um 8KB. Da er die Funktion jede Sekunde aufruft, ist der Speicher schnell voll! An welcher Stelle muss gegebenenfalls Speicher wieder freigegeben werden?

hoika 11. Apr 2007 17:32

Re: Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Hallo,

ein try finally um das GetMem wäre das eine.
Ich denke aber sessionData wird nicht freigegeben (ist doch nen Pointer ?),
Freigabe erfolgt über LSAFreeReturnBuffer

Dazu fällt mir das noch ein

http://support.microsoft.com/kb/331970


Heiko

Manzoni 11. Apr 2007 21:55

Re: Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Hallo hoika,

Danke erstmal für Deine Hilfe! Die Freigabe für sessionData habe ich eingebaut, aber nach dem folgenden if Aufruf steigt der Speicher um 4KB und wird nicht mehr freigegeben:

Delphi-Quellcode:
if LookupAccountSID(nil, sessionData.SID, OwnerName,
                   SizeNeeded, DomainName, SizeNeeded2, OwnerType) then
begin
...
end;
Den Teil im if-Block habe ich probehalber auskommentiert, daran kann es nicht liegen. Auch habe ich alle optionalen Parameter mit nil angegeben, trotzdem ändert sich nichts!

Woran kann das liegen?

hoika 12. Apr 2007 07:24

Re: Den aktiven Benutzer bzw Status des Benutzers bekommen
 
Hallo,

dann schau dir mal per msdn die einzelnen Aurufe an.
Das mit der Freigabe habe ich auch nur dort gefunden.

werden jetzt pro Aufruf 4 kB verbraten oder einmalig ?

Ich würde jetzt mal memcheck (google) druaf ansetzen.


Heiko


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:20 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz