Add support for Windows 8.1 and 7.

This commit is contained in:
Jared Goodwin 2020-02-22 21:53:42 -08:00
parent a811f7e04f
commit b712874eeb
8 changed files with 211 additions and 105 deletions

View File

@ -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<bool>("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);
}

View File

@ -43,15 +43,18 @@
@if (User.Identity.IsAuthenticated)
{
<div class="col-sm-6 mb-3">
<h6>Windows 10 (64-Bit and 32-Bit)</h6>
<h6>Windows 10 / 8.1 / 7 (64-Bit and 32-Bit)</h6>
<p>
<small>Note: GPU-accelerated screen capture and PowerShell Core is unavailable on Windows 7.</small>
</p>
<p>
<strong>Download:</strong>
<br />
<a href="/API/ClientDownloads/Windows">Windows 10 Installer (x64/x86)</a>
<a href="/API/ClientDownloads/Windows">Windows Installer (x64/x86)</a>
<br />
<a href="~/Downloads/Remotely-Win10-x64.zip">Windows 10 x64 Files Only</a>
<a href="~/Downloads/Remotely-Win10-x64.zip">Windows x64 Files Only</a>
<br />
<a href="~/Downloads/Remotely-Win10-x86.zip">Windows 10 x86 Files Only</a>
<a href="~/Downloads/Remotely-Win10-x86.zip">Windows x86 Files Only</a>
</p>
<p>

View File

@ -51,70 +51,7 @@ namespace Remotely.Shared.Models
public double TotalMemory { get; set; }
public double TotalStorage { get; set; }
public static async Task<Device> 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;
}
}
}

View File

@ -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; }
}
}

View File

@ -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<Device> 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<double> 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);
}

View File

@ -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();
/// <summary>
/// contains information about the current state of both physical and virtual memory, including extended memory
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MEMORYSTATUSEX
{
/// <summary>
/// Size of the structure, in bytes. You must set this member before calling GlobalMemoryStatusEx.
/// </summary>
public uint dwLength;
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetCommandLine();
/// <summary>
/// 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).
/// </summary>
public uint dwMemoryLoad;
#endregion
/// <summary>
/// Total size of physical memory, in bytes.
/// </summary>
public ulong ullTotalPhys;
/// <summary>
/// Size of physical memory available, in bytes.
/// </summary>
public ulong ullAvailPhys;
/// <summary>
/// Size of the committed memory limit, in bytes. This is physical memory plus the size of the page file, minus a small overhead.
/// </summary>
public ulong ullTotalPageFile;
/// <summary>
/// Size of available memory to commit, in bytes. The limit is ullTotalPageFile.
/// </summary>
public ulong ullAvailPageFile;
/// <summary>
/// Total size of the user mode portion of the virtual address space of the calling process, in bytes.
/// </summary>
public ulong ullTotalVirtual;
/// <summary>
/// Size of unreserved and uncommitted memory in the user mode portion of the virtual address space of the calling process, in bytes.
/// </summary>
public ulong ullAvailVirtual;
/// <summary>
/// Size of unreserved and uncommitted memory in the extended portion of the virtual address space of the calling process, in bytes.
/// </summary>
public ulong ullAvailExtendedVirtual;
/// <summary>
/// Initializes a new instance of the <see cref="T:MEMORYSTATUSEX"/> class.
/// </summary>
public MEMORYSTATUSEX()
{
this.dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
}
}
}
}

View File

@ -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;
}
}
}

View File

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