我希望能够创build一个powershell脚本来告诉我,对于当前机器上的所有RDP会话,用户是谁,以及他们的客户机名(机器名)是什么。
我可以使用win32_loggedonnuser和win32_logonsession的组合来获取用户名信息,但是我无法在这些对象(枚举?)中find客户端名称。
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
(头足类是我的机器名称,login到服务器的机器)
。
我可以看到HKCU:\Volatile Environment确实有客户端名称, temp密钥中有用户名,但是如果会话当前处于活动状态,我无法单独build立密钥。
我是否错过了一个API调用,可以在一个地方获得所有这些信息?
基本要求:grep出任务pipe理器>用户列表的用户名和客户端,其中状态处于活动状态。
没有我知道的这个WMI接口。
我是否错过了一个API调用,可以在一个地方获得所有这些信息?
是的。 您可以从Win32 API获取数据。 从wtsapi32.dll,具体。 你可以编写一个C程序,或者你可以从C#甚至Powershell中调用它。
既然你可能需要Powershell,我今天早上为你写了这个:
# 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
将所有这些复制到名为QuerySessionInformation.ps1的文件中。 现在在C:\ Windows \ SysWOW64 \ WindowsPowershell \ v1.0中启动32位版本的Powershell 。 上面的代码使用指针,将无法在本机64位环境中工作。
现在运行脚本。 如果您以前从未在该服务器上运行32位版本的Powershell,则需要使用Set-ExecutionPolicy修改脚本执行策略,因为32位和64位Powershell具有单独的执行策略。 请注意,脚本本身不应该有输出,因为它正在编译.NET代码并将其添加到当前环境中。 另外请注意,一旦types添加了添加types,您不能卸载它,而不必退出该Powershell会话… AFAIK。 这使得debugging这种东西真的很烦人,因为每次修改代码时必须重新启动Powershell。
现在代码已经加载了,input:
PS C:\> [RDPInfo]::ListUsers("REMOTESERVER")
如果REMOTESERVER上有任何活动的用户会话,则输出如下所示:
DOMAIN\UserName SessionID: 2 ClientName: RYAN-PC
这可以在远程计算机以及本地计算机上运行,但要注意,如果运行此程序的用户对远程计算机没有足够的权限,它将会自动失败(无输出)。
编辑:WTS_INFO_CLASS中还有其他一些信息可能会引起您的兴趣,例如WTSConnectState和WTSClientAddress。 你所要做的就是查询它们。
编辑:我也已经将此解决scheme转换为本机代码(C)以便在命令行上使用:
http://www.myotherpcisacloud.com/post/2013/01/16/Usersexe-v1003.aspx
PSterminal服务 (为Powershell)做的伎俩? 我一直在我们的10台terminal服务器上使用它。
PS > Import-Module PSTerminalServices PS > Get-tssession -computername {name} 这是一个很棒的工具。
执行quser / server:[服务器名称]> [文本文件的path] .txt
它列出所有的信息,将其pipe理到一个空格分隔的文本文件,以便可以轻松导入和parsing。 很好的工作,并避免调用32位或64位依赖的本地API的任何复杂性。 如果它是一个.Net专注的应用程序,可以在托pipe代码中完成。