Objeto WMI para obter as sessões atuais com o nome do cliente?

4

Eu quero poder criar um script powershell que me diga, para todas as sessões RDP atualmente ativas em uma máquina, quem é o usuário e qual é o nome do cliente (nome da máquina).

Eu posso usar uma combinação de win32_loggedonnuser e win32_logonsession para obter as informações de nome de usuário, mas não consigo encontrar o nome do cliente nesses objetos (enumerações?).

PS C:\> $logons = gwmi win32_loggedonuser; $lstring = ""; foreach($l in $logons) { $lstring +=$l;} $lstring -match "cephalopod";
False
PS C:\> $sessions = gwmi win32_logonsession; $sstring = ""; foreach($s in $sessions) { $sstring +=$s;} $sstring -match "cephalopod";
False

(cefalópode é o nome da minha máquina, a máquina que está logada na caixa do servidor)

.

Eu posso ver que HKCU:\Volatile Environment tem o nome do cliente, e a temp tem o nome de usuário dentro dele, mas não consigo estabelecer a partir das chaves sozinhas se a sessão estiver ativa no momento.

Estou perdendo uma chamada da API que me fornecerá todas essas informações em um só lugar?

Requisito básico: acesse o Gerenciador de tarefas > Usuários que listam o nome do usuário e do cliente, onde o status está ativo.

    
por glasnt 19.12.2011 / 23:07

4 respostas

8

Não há interface WMI para isso que eu saiba.

Am I missing an API call that will get me all this information in one place?

Sim. Você pode obter os dados da API do Win32. De wtsapi32.dll, para ser específico. Você pode escrever um programa em C, ou você pode P / invocá-lo a partir de C # ou mesmo Powershell.

Como você provavelmente quer o Powershell, escrevi isso para você esta manhã:

# QuerySessionInformation.ps1
# Written by Ryan Ries, Jan. 2013, with help from MSDN and Stackoverflow.

$Code = @'
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
public class RDPInfo
{
    [DllImport("wtsapi32.dll")]
    static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName);

    [DllImport("wtsapi32.dll")]
    static extern void WTSCloseServer(IntPtr hServer);

    [DllImport("wtsapi32.dll")]
    static extern Int32 WTSEnumerateSessions(
        IntPtr hServer,
        [MarshalAs(UnmanagedType.U4)] Int32 Reserved,
        [MarshalAs(UnmanagedType.U4)] Int32 Version,
        ref IntPtr ppSessionInfo,
        [MarshalAs(UnmanagedType.U4)] ref Int32 pCount);

    [DllImport("wtsapi32.dll")]
    static extern void WTSFreeMemory(IntPtr pMemory);

    [DllImport("Wtsapi32.dll")]
    static extern bool WTSQuerySessionInformation(System.IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out uint pBytesReturned);

    [StructLayout(LayoutKind.Sequential)]
    private struct WTS_SESSION_INFO
    {
        public Int32 SessionID;
        [MarshalAs(UnmanagedType.LPStr)]
        public String pWinStationName;
        public WTS_CONNECTSTATE_CLASS State;
    }

    public enum WTS_INFO_CLASS
    {
        WTSInitialProgram,
        WTSApplicationName,
        WTSWorkingDirectory,
        WTSOEMId,
        WTSSessionId,
        WTSUserName,
        WTSWinStationName,
        WTSDomainName,
        WTSConnectState,
        WTSClientBuildNumber,
        WTSClientName,
        WTSClientDirectory,
        WTSClientProductId,
        WTSClientHardwareId,
        WTSClientAddress,
        WTSClientDisplay,
        WTSClientProtocolType
    }

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

    public static IntPtr OpenServer(String Name)
    {
        IntPtr server = WTSOpenServer(Name);
        return server;
    }

    public static void CloseServer(IntPtr ServerHandle)
    {
        WTSCloseServer(ServerHandle);
    }

    public static void ListUsers(String ServerName)
    {
        IntPtr serverHandle = IntPtr.Zero;
        List<String> resultList = new List<string>();
        serverHandle = OpenServer(ServerName);

        try
        {
            IntPtr SessionInfoPtr = IntPtr.Zero;
            IntPtr userPtr = IntPtr.Zero;
            IntPtr domainPtr = IntPtr.Zero;
            IntPtr clientNamePtr = IntPtr.Zero;
            Int32 sessionCount = 0;
            Int32 retVal = WTSEnumerateSessions(serverHandle, 0, 1, ref SessionInfoPtr, ref sessionCount);
            Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
            Int32 currentSession = (int)SessionInfoPtr;
            uint bytes = 0;
            if (retVal != 0)
            {
                for (int i = 0; i < sessionCount; i++)
                {
                    WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)currentSession, typeof(WTS_SESSION_INFO));
                    currentSession += dataSize;

                    WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSUserName, out userPtr, out bytes);
                    WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSDomainName, out domainPtr, out bytes);
                    WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSClientName, out clientNamePtr, out bytes);

                    if(Marshal.PtrToStringAnsi(domainPtr).Length > 0 && Marshal.PtrToStringAnsi(userPtr).Length > 0)
                    {
                        if(Marshal.PtrToStringAnsi(clientNamePtr).Length < 1)                       
                            Console.WriteLine(Marshal.PtrToStringAnsi(domainPtr) + "\" + Marshal.PtrToStringAnsi(userPtr) + "\tSessionID: " + si.SessionID + "\tClientName: n/a");
                        else
                            Console.WriteLine(Marshal.PtrToStringAnsi(domainPtr) + "\" + Marshal.PtrToStringAnsi(userPtr) + "\tSessionID: " + si.SessionID + "\tClientName: " + Marshal.PtrToStringAnsi(clientNamePtr));
                    }
                    WTSFreeMemory(clientNamePtr);
                    WTSFreeMemory(userPtr);
                    WTSFreeMemory(domainPtr);
                }
                WTSFreeMemory(SessionInfoPtr);
            }
        }
        catch(Exception ex)
        {
            Console.WriteLine("Exception: " + ex.Message);
        }
        finally
        {
            CloseServer(serverHandle);
        }
    }
}
'@

Add-Type $Code

Copie tudo isso em um arquivo chamado QuerySessionInformation.ps1. Agora, abra a versão de 32 bits do Powershell em C: \ Windows \ SysWOW64 \ WindowsPowershell \ v1.0. O código acima usa ponteiros que não funcionam em um ambiente nativo de 64 bits.

Agora, execute o script. Se você nunca executou a versão de 32 bits do Powershell nesse servidor antes, será necessário modificar a política de execução de scripts com Set-ExecutionPolicy, já que o Powershell de 32 bits e 64 bits tem políticas de execução separadas. Observe que não deve haver saída do próprio script, pois tudo o que ele está fazendo é compilar o código .NET e adicioná-lo ao ambiente atual. Observe também que, depois que um tipo é adicionado ao Add-Type, você não pode descarregá-lo sem sair da sessão do Powershell ... AFAIK. Isso torna a depuração desse tipo de coisa realmente irritante, já que você precisa reiniciar o Powershell toda vez que modificar o código.

Agora que o código está carregado, digite:

PS C:\> [RDPInfo]::ListUsers("REMOTESERVER")

Se houver alguma sessão de usuário ativa no REMOTESERVER, a saída ficará assim:

DOMAIN\UserName  SessionID: 2    ClientName: RYAN-PC

Isso funcionará em computadores remotos, bem como no computador local, mas lembre-se de que, se o usuário que estiver executando não tiver permissões suficientes para o computador remoto, ele falhará silenciosamente (sem saída).

Edit: Existem outros bits de informação em WTS_INFO_CLASS que podem ser do seu interesse, como WTSConnectState e WTSClientAddress. Tudo o que você precisa fazer é consultá-los.

Edit: Eu também converti esta solução para o código nativo (C) para uso na linha de comando:

link

    
por 13.01.2013 / 20:58
1

Os PS Terminal Services (para o Powershell) fazem o truque? Eu uso isso o tempo todo em nossos 10 servidores de terminal.

  1. Faça o download e instale a partir deste link
  2. PS > Import-Module PSTerminalServices
  3. PS > Get-tssession -computername {name}

Esta é uma utilidade maravilhosa.

    
por 18.01.2013 / 00:49
1

Execute o quser / server: [o nome dos servidores] > [o caminho para o arquivo de texto] .txt

Lista todas as informações, direciona-as para um arquivo de texto delimitado por espaço para que possa ser facilmente importado & dissecado. Funciona muito bem e evita qualquer complexidade de chamar APIs nativas dependentes de 32 ou 64 bits. Pode ser feito tudo em código gerenciado se for um aplicativo focado em .Net.

    
por 06.11.2015 / 11:21
0

link

pode ser mais útil para você:)

    
por 26.12.2011 / 13:28