From b712874eeb6c512afcb8f34234ef6467ceb372f4 Mon Sep 17 00:00:00 2001 From: Jared Goodwin Date: Sat, 22 Feb 2020 21:53:42 -0800 Subject: [PATCH] Add support for Windows 8.1 and 7. --- Agent/Services/DeviceSocket.cs | 4 +- Server/Pages/Agents.cshtml | 11 ++-- Shared/Models/Device.cs | 65 +--------------------- Shared/Models/WindowsSession.cs | 1 + Shared/Services/DeviceInformation.cs | 82 +++++++++++++++++++++++----- Shared/Win32/Kernel32.cs | 73 ++++++++++++++++++++++--- Shared/Win32/WTSAPI32.cs | 59 ++++++++++++++++---- Shared/Win32/Win32Interop.cs | 21 ++++++- 8 files changed, 211 insertions(+), 105 deletions(-) diff --git a/Agent/Services/DeviceSocket.cs b/Agent/Services/DeviceSocket.cs index c03b7364..e9847a62 100644 --- a/Agent/Services/DeviceSocket.cs +++ b/Agent/Services/DeviceSocket.cs @@ -60,7 +60,7 @@ namespace Remotely.Agent.Services await HubConnection.StartAsync(); - var device = await Device.Create(ConnectionInfo); + var device = await DeviceInformation.Create(ConnectionInfo); var result = await HubConnection.InvokeAsync("DeviceCameOnline", device); @@ -98,7 +98,7 @@ namespace Remotely.Agent.Services public async Task SendHeartbeat() { - var currentInfo = await Device.Create(ConnectionInfo); + var currentInfo = await DeviceInformation.Create(ConnectionInfo); await HubConnection.InvokeAsync("DeviceHeartbeat", currentInfo); } diff --git a/Server/Pages/Agents.cshtml b/Server/Pages/Agents.cshtml index 335e65ec..efcad895 100644 --- a/Server/Pages/Agents.cshtml +++ b/Server/Pages/Agents.cshtml @@ -43,15 +43,18 @@ @if (User.Identity.IsAuthenticated) {
-
Windows 10 (64-Bit and 32-Bit)
+
Windows 10 / 8.1 / 7 (64-Bit and 32-Bit)
+

+ Note: GPU-accelerated screen capture and PowerShell Core is unavailable on Windows 7. +

Download:
- Windows 10 Installer (x64/x86) + Windows Installer (x64/x86)
- Windows 10 x64 Files Only + Windows x64 Files Only
- Windows 10 x86 Files Only + Windows x86 Files Only

diff --git a/Shared/Models/Device.cs b/Shared/Models/Device.cs index 76c84c35..144d7c45 100644 --- a/Shared/Models/Device.cs +++ b/Shared/Models/Device.cs @@ -51,70 +51,7 @@ namespace Remotely.Shared.Models public double TotalMemory { get; set; } public double TotalStorage { get; set; } - public static async Task Create(ConnectionInfo connectionInfo) - { - OSPlatform platform = OSUtils.GetPlatform(); - DriveInfo systemDrive; - - if (!string.IsNullOrWhiteSpace(Environment.SystemDirectory)) - { - systemDrive = DriveInfo.GetDrives() - .Where(x=>x.IsReady) - .FirstOrDefault(x => - x.RootDirectory.FullName.Contains(Path.GetPathRoot(Environment.SystemDirectory ?? Environment.CurrentDirectory)) - ); - } - else - { - systemDrive = DriveInfo.GetDrives().FirstOrDefault(x => - x.IsReady && - x.RootDirectory.FullName == Path.GetPathRoot(Environment.CurrentDirectory)); - } - - var device = new Device() - { - ID = connectionInfo.DeviceID, - DeviceName = Environment.MachineName, - Platform = platform.ToString(), - ProcessorCount = Environment.ProcessorCount, - OSArchitecture = RuntimeInformation.OSArchitecture, - OSDescription = RuntimeInformation.OSDescription, - Is64Bit = Environment.Is64BitOperatingSystem, - IsOnline = true, - Drives = DriveInfo.GetDrives().Where(x => x.IsReady).Select(x => new Drive() - { - DriveFormat = x.DriveFormat, - DriveType = x.DriveType, - Name = x.Name, - RootDirectory = x.RootDirectory.FullName, - FreeSpace = x.TotalSize > 0 ? x.TotalFreeSpace / x.TotalSize : 0, - TotalSize = x.TotalSize > 0 ? Math.Round((double)(x.TotalSize / 1024 / 1024 / 1024), 2) : 0, - VolumeLabel = x.VolumeLabel - }).ToList(), - OrganizationID = connectionInfo.OrganizationID, - CurrentUser = DeviceInformation.GetCurrentUser() - }; - - if (systemDrive != null && systemDrive.TotalSize > 0 && systemDrive.TotalFreeSpace > 0) - { - device.TotalStorage = Math.Round((double)(systemDrive.TotalSize / 1024 / 1024 / 1024), 2); - device.UsedStorage = Math.Round((double)((systemDrive.TotalSize - systemDrive.TotalFreeSpace) / 1024 / 1024 / 1024), 2); - } - - - var (usedMemory, totalMemory) = DeviceInformation.GetMemoryInGB(); - device.UsedMemory = usedMemory; - device.TotalMemory = totalMemory; - - device.CpuUtilization = await DeviceInformation.GetCpuUtilization(); - - if (File.Exists("Remotely_Agent.dll")) - { - device.AgentVersion = FileVersionInfo.GetVersionInfo("Remotely_Agent.dll")?.FileVersion?.ToString()?.Trim(); - } - - return device; - } + } } \ No newline at end of file diff --git a/Shared/Models/WindowsSession.cs b/Shared/Models/WindowsSession.cs index 46545f1a..9495124e 100644 --- a/Shared/Models/WindowsSession.cs +++ b/Shared/Models/WindowsSession.cs @@ -14,5 +14,6 @@ namespace Remotely.Shared.Models public uint ID { get; internal set; } public string Name { get; internal set; } public SessionType Type { get; internal set; } + public string Username { get; internal set; } } } diff --git a/Shared/Services/DeviceInformation.cs b/Shared/Services/DeviceInformation.cs index e8f22c9f..94c6d880 100644 --- a/Shared/Services/DeviceInformation.cs +++ b/Shared/Services/DeviceInformation.cs @@ -1,9 +1,12 @@ -using Microsoft.Management.Infrastructure; +using Remotely.Shared.Models; using Remotely.Shared.Services; +using Remotely.Shared.Win32; using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; @@ -11,6 +14,59 @@ namespace Remotely.Shared.Services { public class DeviceInformation { + public static async Task Create(ConnectionInfo connectionInfo) + { + OSPlatform platform = OSUtils.GetPlatform(); + + var systemDrive = DriveInfo.GetDrives().FirstOrDefault(x => + x.IsReady && + x.RootDirectory.FullName.Contains(Path.GetPathRoot(Environment.SystemDirectory ?? Environment.CurrentDirectory))); + + var device = new Device() + { + ID = connectionInfo.DeviceID, + DeviceName = Environment.MachineName, + Platform = platform.ToString(), + ProcessorCount = Environment.ProcessorCount, + OSArchitecture = RuntimeInformation.OSArchitecture, + OSDescription = RuntimeInformation.OSDescription, + Is64Bit = Environment.Is64BitOperatingSystem, + IsOnline = true, + Drives = DriveInfo.GetDrives().Where(x => x.IsReady).Select(x => new Drive() + { + DriveFormat = x.DriveFormat, + DriveType = x.DriveType, + Name = x.Name, + RootDirectory = x.RootDirectory.FullName, + FreeSpace = x.TotalSize > 0 ? x.TotalFreeSpace / x.TotalSize : 0, + TotalSize = x.TotalSize > 0 ? Math.Round((double)(x.TotalSize / 1024 / 1024 / 1024), 2) : 0, + VolumeLabel = x.VolumeLabel + }).ToList(), + OrganizationID = connectionInfo.OrganizationID, + CurrentUser = DeviceInformation.GetCurrentUser() + }; + + if (systemDrive != null && systemDrive.TotalSize > 0 && systemDrive.TotalFreeSpace > 0) + { + device.TotalStorage = Math.Round((double)(systemDrive.TotalSize / 1024 / 1024 / 1024), 2); + device.UsedStorage = Math.Round((double)((systemDrive.TotalSize - systemDrive.TotalFreeSpace) / 1024 / 1024 / 1024), 2); + } + + + var (usedMemory, totalMemory) = DeviceInformation.GetMemoryInGB(); + device.UsedMemory = usedMemory; + device.TotalMemory = totalMemory; + + device.CpuUtilization = await DeviceInformation.GetCpuUtilization(); + + if (File.Exists("Remotely_Agent.dll")) + { + device.AgentVersion = FileVersionInfo.GetVersionInfo("Remotely_Agent.dll")?.FileVersion?.ToString()?.Trim(); + } + + return device; + } + public static async Task GetCpuUtilization() { double totalUtilization = 0; @@ -62,16 +118,12 @@ namespace Remotely.Shared.Services { if (OSUtils.IsWindows) { - var session = CimSession.Create(null); - var computerSystem = session.EnumerateInstances("root\\cimv2", "CIM_ComputerSystem"); - var username = computerSystem.FirstOrDefault().CimInstanceProperties["UserName"].Value ?? ""; - return username as string; + return Win32Interop.GetActiveSessions().LastOrDefault()?.Username; } else if (OSUtils.IsLinux) { var users = OSUtils.StartProcessWithResults("users", ""); - var username = users?.Split()?.FirstOrDefault()?.Trim(); - return $"{Environment.UserDomainName}\\{username}"; + return users?.Split()?.FirstOrDefault()?.Trim(); } throw new Exception("Unsupported operating system."); } @@ -96,7 +148,6 @@ namespace Remotely.Shared.Services return (0, 0); } } - private static (double, double) GetLinxMemoryInGB() { try @@ -136,12 +187,15 @@ namespace Remotely.Shared.Services { try { - var session = CimSession.Create(null); - var cimOS = session.EnumerateInstances("root\\cimv2", "CIM_OperatingSystem"); - var free = (ulong)(cimOS.FirstOrDefault()?.CimInstanceProperties["FreePhysicalMemory"]?.Value ?? 0); - var freeGB = Math.Round(((double)free / 1024 / 1024), 2); - var total = (ulong)(cimOS.FirstOrDefault()?.CimInstanceProperties["TotalVisibleMemorySize"]?.Value ?? 0); - var totalGB = Math.Round(((double)total / 1024 / 1024), 2); + var memoryStatus = new Kernel32.MEMORYSTATUSEX(); + double totalGB = 0; + double freeGB = 0; + + if (Kernel32.GlobalMemoryStatusEx(memoryStatus)) + { + freeGB = Math.Round(((double)memoryStatus.ullAvailPhys / 1024 / 1024 / 1024), 2); + totalGB = Math.Round(((double)memoryStatus.ullTotalPhys / 1024 / 1024 / 1024), 2); + } return (totalGB - freeGB, totalGB); } diff --git a/Shared/Win32/Kernel32.cs b/Shared/Win32/Kernel32.cs index b13b6884..f7ee6f0c 100644 --- a/Shared/Win32/Kernel32.cs +++ b/Shared/Win32/Kernel32.cs @@ -5,23 +5,82 @@ namespace Remotely.Shared.Win32 { public static class Kernel32 { - #region DLL Imports - [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr hSnapshot); + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr GetCommandLine(); + + [return: MarshalAs(UnmanagedType.Bool)] + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer); + [DllImport("kernel32.dll")] - public static extern uint WTSGetActiveConsoleSessionId(); + public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); [DllImport("kernel32.dll")] public static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId); [DllImport("kernel32.dll")] - public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); + public static extern uint WTSGetActiveConsoleSessionId(); + ///

+ /// contains information about the current state of both physical and virtual memory, including extended memory + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class MEMORYSTATUSEX + { + /// + /// Size of the structure, in bytes. You must set this member before calling GlobalMemoryStatusEx. + /// + public uint dwLength; - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - public static extern IntPtr GetCommandLine(); + /// + /// Number between 0 and 100 that specifies the approximate percentage of physical memory that is in use (0 indicates no memory use and 100 indicates full memory use). + /// + public uint dwMemoryLoad; - #endregion + /// + /// Total size of physical memory, in bytes. + /// + public ulong ullTotalPhys; + + /// + /// Size of physical memory available, in bytes. + /// + public ulong ullAvailPhys; + + /// + /// Size of the committed memory limit, in bytes. This is physical memory plus the size of the page file, minus a small overhead. + /// + public ulong ullTotalPageFile; + + /// + /// Size of available memory to commit, in bytes. The limit is ullTotalPageFile. + /// + public ulong ullAvailPageFile; + + /// + /// Total size of the user mode portion of the virtual address space of the calling process, in bytes. + /// + public ulong ullTotalVirtual; + + /// + /// Size of unreserved and uncommitted memory in the user mode portion of the virtual address space of the calling process, in bytes. + /// + public ulong ullAvailVirtual; + + /// + /// Size of unreserved and uncommitted memory in the extended portion of the virtual address space of the calling process, in bytes. + /// + public ulong ullAvailExtendedVirtual; + + /// + /// Initializes a new instance of the class. + /// + public MEMORYSTATUSEX() + { + this.dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX)); + } + } } } diff --git a/Shared/Win32/WTSAPI32.cs b/Shared/Win32/WTSAPI32.cs index bf7300da..0ee90368 100644 --- a/Shared/Win32/WTSAPI32.cs +++ b/Shared/Win32/WTSAPI32.cs @@ -5,14 +5,8 @@ namespace Remotely.Shared.Win32 { public static class WTSAPI32 { - [StructLayout(LayoutKind.Sequential)] - public struct WTS_SESSION_INFO - { - public uint SessionID; - [MarshalAs(UnmanagedType.LPStr)] - public string pWinStationName; - public WTS_CONNECTSTATE_CLASS State; - } + public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; + public enum WTS_CONNECTSTATE_CLASS { WTSActive, @@ -26,10 +20,36 @@ namespace Remotely.Shared.Win32 WTSDown, WTSInit } - public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; - [DllImport("wtsapi32.dll", SetLastError = true)] - static extern IntPtr WTSOpenServer(string pServerName); + public enum WTS_INFO_CLASS + { + WTSInitialProgram, + WTSApplicationName, + WTSWorkingDirectory, + WTSOEMId, + WTSSessionId, + WTSUserName, + WTSWinStationName, + WTSDomainName, + WTSConnectState, + WTSClientBuildNumber, + WTSClientName, + WTSClientDirectory, + WTSClientProductId, + WTSClientHardwareId, + WTSClientAddress, + WTSClientDisplay, + WTSClientProtocolType, + WTSIdleTime, + WTSLogonTime, + WTSIncomingBytes, + WTSOutgoingBytes, + WTSIncomingFrames, + WTSOutgoingFrames, + WTSClientInfo, + WTSSessionInfo + } + [DllImport("wtsapi32.dll", SetLastError = true)] public static extern int WTSEnumerateSessions( @@ -39,5 +59,22 @@ namespace Remotely.Shared.Win32 ref System.IntPtr ppSessionInfo, ref int pCount); + [DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)] + public static extern void WTSFreeMemory(IntPtr memory); + + [DllImport("Wtsapi32.dll")] + public static extern bool WTSQuerySessionInformation(IntPtr hServer, uint sessionId, WTS_INFO_CLASS wtsInfoClass, out IntPtr ppBuffer, out uint pBytesReturned); + + [DllImport("wtsapi32.dll", SetLastError = true)] + static extern IntPtr WTSOpenServer(string pServerName); + + [StructLayout(LayoutKind.Sequential)] + public struct WTS_SESSION_INFO + { + public uint SessionID; + [MarshalAs(UnmanagedType.LPStr)] + public string pWinStationName; + public WTS_CONNECTSTATE_CLASS State; + } } } diff --git a/Shared/Win32/Win32Interop.cs b/Shared/Win32/Win32Interop.cs index cdc5987b..a94fd621 100644 --- a/Shared/Win32/Win32Interop.cs +++ b/Shared/Win32/Win32Interop.cs @@ -21,7 +21,8 @@ namespace Remotely.Shared.Win32 { ID = consoleSessionId, Type = SessionType.Console, - Name = "Console" + Name = "Console", + Username = GetUsernameFromSessionId(consoleSessionId) }); IntPtr ppSessionInfo = IntPtr.Zero; @@ -43,7 +44,8 @@ namespace Remotely.Shared.Win32 { ID = sessionInfo.SessionID, Name = sessionInfo.pWinStationName, - Type = SessionType.RDP + Type = SessionType.RDP, + Username = GetUsernameFromSessionId(sessionInfo.SessionID) }); } } @@ -79,11 +81,24 @@ namespace Remotely.Shared.Win32 CloseDesktop(inputDesktop); } } + + public static string GetUsernameFromSessionId(uint sessionId) + { + var username = string.Empty; + + if (WTSAPI32.WTSQuerySessionInformation(IntPtr.Zero, sessionId, WTSAPI32.WTS_INFO_CLASS.WTSUserName, out var buffer, out var strLen) && strLen > 1) + { + username = Marshal.PtrToStringAnsi(buffer); + WTSAPI32.WTSFreeMemory(buffer); + } + + return username; + } + public static IntPtr OpenInputDesktop() { return User32.OpenInputDesktop(0, true, ACCESS_MASK.GENERIC_ALL); } - public static bool OpenInteractiveProcess(string applicationName, string desktopName, bool hiddenWindow, out PROCESS_INFORMATION procInfo) { uint winlogonPid = 0;