Обнаружение нескольких вошедших в систему пользователей через win32

С помощью стандартного API Win32, какой лучший способ обнаружить более одного пользователя вошли в систему? У меня есть обновление до нашего программного продукта, который не может быть запущен, когда в систему вошли более одного пользователя. (Я знаю, что этого следует избегать из-за его раздражающего фактора, но продукт очень сложный. Вам придется поверьте мне, когда я говорю, что действительно нет другого решения.) Спасибо.

15.12.2008 15:10:41
Что ж, я предполагаю, что вы хотите, чтобы это работало в Windows 2000, XP, Vista и Windows 7. Вам интересно знать, вошли ли пользователи через службы терминалов?
Mick 15.12.2008 15:41:06
3 ОТВЕТА
РЕШЕНИЕ

Чтобы одновременно войти в систему более одного пользователя, необходимо включить службы терминалов или быстрое переключение пользователей. Поскольку быстрое переключение пользователей реализовано с использованием служб терминалов, сначала необходимо выяснить, включена ли в ОС эта функция. Вы можете использовать GetVersionEx с OSVERSIONINFOEX . Проверьте наличие флагов VER_SUITE_SINGLEUSERTS и VER_SUITE_TERMINAL.

Если TS включен, вы можете использовать WTSEnumerateSessions, чтобы узнать, сколько пользователей вошли в систему. Это работает, только если запущена служба «Службы терминалов».

Если машина не поддерживает службы терминалов (или если служба не запущена), то в систему может войти только один пользователь.

4
15.12.2008 16:13:07
Это неправильно, при быстром переключении пользователей> 1 пользователь может одновременно запускать процессы.
Ana Betts 15.12.2008 16:08:41
@Paul Betts: если вы используете быстрое переключение пользователей, то WTSEnumerateSessions вернет все сеансы, вошедшие в данный момент в систему, независимо от того, активны они или нет.
Roger Lipscombe 15.12.2008 16:10:09

Это может быть окольным путем, но запустите список процессов и посмотрите, кто является владельцами процессов.

0
15.12.2008 16:06:41
На самом деле это хороший метод. Перечислите все процессы: msdn.microsoft.com/en-us/library/windows/desktop/… затем получите имя пользователя для каждого процесса: stackoverflow.com/questions/2686096/c-get-username-from-process/…
Itaypk 2.02.2014 11:53:32

Вот решение, которое работает в XP, Server 2003, Vista и Server 2008. Обратите внимание, что это не будет работать в Windows 2000, потому что « LsaEnumerateLogonSessions » не доступен в Windows 2000. Этот код изменен из сообщения Delphi-PRAXIS .

Чтобы скомпилировать это, создайте новое приложение VCL с TButton и TMemo в форме. Затем скопируйте и вставьте этот код, и он должен скомпилироваться. Я проверил на XP и Vista, и это работает хорошо. Вернутся интерактивные и удаленные пользователи.

unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

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 LsaFreeReturnBuffer(Buffer: pointer): Integer; stdcall;

  function WTSGetActiveConsoleSessionId: DWORD; external 'Kernel32.dll';

  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';

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function LsaFreeReturnBuffer; external 'secur32.dll' name 'LsaFreeReturnBuffer';

procedure GetActiveUserNames(var slUserList : TStringList);
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;
   sUser : string;
begin
   //result:= '';
   //Listing LogOnSessions
   i1:= lsaNtStatusToWinError(LsaEnumerateLogonSessions(@Count, @List));
   try
      if i1 = 0 then
      begin
          i1:= -1;
          if Count > 0 then
          begin
              repeat
                inc(i1);
                LsaGetLogonSessionData(List, sessionData);
                //Checks if it is an interactive session
                sUser := sessionData.UserName.Buffer;
                if (sessionData.LogonType = Interactive)
                  or (sessionData.LogonType = RemoteInteractive)
                  or (sessionData.LogonType = CachedInteractive)
                  or (sessionData.LogonType = CachedRemoteInteractive) then
                begin
                    //
                    SizeNeeded := MAX_PATH;
                    SizeNeeded2:= MAX_PATH;
                    GetMem(OwnerName, MAX_PATH);
                    GetMem(DomainName, MAX_PATH);
                    try
                    if LookupAccountSID(nil, sessionData.SID, OwnerName,
                                       SizeNeeded, DomainName,SizeNeeded2,
                                       OwnerType) then
                    begin
                      if OwnerType = 1 then  //This is a USER account SID (SidTypeUser=1)
                      begin
                        sUser := AnsiUpperCase(sessionData.LogonDomain.Buffer);
                        sUser := sUser + '\';
                        sUser := sUser + AnsiUpperCase(sessionData.UserName.Buffer);
                        slUserList.Add(sUser);
//                          if sessionData.Session = WTSGetActiveConsoleSessionId then
//                          begin
//                            //Wenn Benutzer aktiv
//                            try
//                                if WTSQuerySessionInformationA
//                                   (WTS_CURRENT_SERVER_HANDLE,
//                                    sessionData.Session, WTSConnectState,
//                                    pBuffer,
//                                    pBytesreturned) then
//                                begin
//                                    if WTS_CONNECTSTATE_CLASS(pBuffer^) = WTSActive then
//                                    begin
//                                      //result:= sessionData.UserName.Buffer;
//                                      slUserList.Add(sessionData.UserName.Buffer);
//                                    end;
//                                end;
//                            finally
//                              LSAFreeReturnBuffer(pBuffer);
//                            end;
                          //end;
                      end;
                    end;
                    finally
                    FreeMem(OwnerName);
                    FreeMem(DomainName);
                    end;
                end;
                inc(List);
                try
                    LSAFreeReturnBuffer(sessionData);
                except
                end;
            until (i1 = Count-1);// or (result <> '');
          end;
      end;
   finally
      LSAFreeReturnBuffer(List);
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  slUsers : TStringList;
begin
  slUsers := TStringList.Create;
  slUsers.Duplicates := dupIgnore;
  slUsers.Sorted := True;

  try
    GetActiveUserNames(slUsers);
    Memo1.Lines.AddStrings(slUsers);
  finally
    FreeAndNil(slUsers)
  end;
end;

end.
2
15.12.2008 16:43:16
Я должен добавить, что вы можете превратить этот код в библиотеку MSI DLL и использовать ее в качестве настраиваемого действия установщика Windows (MSI). У меня есть пример того, как сделать это с Delphi здесь: stackoverflow.com/questions/367365/… .
Mick 15.12.2008 16:48:59
Если у вас нет Delphi, весь этот код скомпилируется и будет отлично работать, используя бесплатный Turbo Delphi. Вы можете скачать это здесь: codegear.com/downloads/free/turbo
Mick 15.12.2008 16:51:22