通过win32检测多个登录用户

时间:2008-12-15 15:10:42

标签: winapi

使用标准的win32 api,检测多个用户的最佳方法是什么?我有一个升级到我们的软件产品,当多个用户登录时无法运行。(我知道这是因为它的烦恼因素而应该避免的,但产品非常复杂。你必须要当我说真的没有其他解决方案时,请相信我。)谢谢。

3 个答案:

答案 0 :(得分:4)

要同时登录多个用户,必须启用终端服务或快速用户切换。由于使用终端服务实现快速用户切换,因此首先需要确定操作系统是否已启用它。您可以将GetVersionExOSVERSIONINFOEX一起使用。检查VER_SUITE_SINGLEUSERTS和VER_SUITE_TERMINAL标志。

如果启用了TS,则可以使用WTSEnumerateSessions查看登录用户的数量。这仅在“终端服务”服务启动时有效。

如果机器不支持终端服务(或者服务未启动),则只能有一个用户登录。

答案 1 :(得分:2)

这是适用于XP,Server 2003,Vista和Server 2008的解决方案。请注意,这在Windows 2000上不起作用,因为“LsaEnumerateLogonSessions”在Windows 2000上不可用。此代码已修改来自Delphi-PRAXIS post

要编译它,请在表单上创建一个带有TButton和TMemo的新VCL应用程序。然后复制并粘贴此代码,它应该编译。我在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 :(得分:0)

这可能是一种迂回的方式,但是在流程列表中运行并查看流程所有者是谁。