Skip to content

Commit

Permalink
Merge pull request #37 from 0xf005ba11/rdp-connection
Browse files Browse the repository at this point in the history
remote desktop connections
  • Loading branch information
jxy-s authored Apr 13, 2023
2 parents 6cf47ed + 8984bc2 commit a563176
Show file tree
Hide file tree
Showing 10 changed files with 682 additions and 83 deletions.
150 changes: 123 additions & 27 deletions VMPlex/Rdp/RdpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

using HyperV;
using EasyWMI;
using System.Diagnostics;

namespace VMPlex
{
Expand Down Expand Up @@ -139,6 +140,79 @@ public void InitializeForLocalVmConnection(VirtualMachine vm, RdpOptions options
InitEvents();
}

public void InitializeForRemoteDesktopConnection(RdpOptions options)
{
AttachInterfaces();
m_ocx = (IMsRdpClient9)GetOcx();
m_options = options;

Domain = options.Domain;
Server = options.Server;
AdvancedSettings2.RDPPort = options.Port;
AdvancedSettings8.EnableCredSspSupport = true;

ColorDepth = m_options.ColorDepth;
DesktopWidth = options.DesktopWidth;
DesktopHeight = options.DesktopHeight;

int perfflags = 0;
if (options.EnhancedGraphics)
{
perfflags |= 0x10; // TF_PERF_ENABLE_ENHANCED_GRAPHICS
}
if (options.FontSmoothing)
{
perfflags |= 0x80; // TF_PERF_ENABLE_FONT_SMOOTHING
}
if (options.DesktopComposition)
{
perfflags |= 0x100; // TF_PERF_ENABLE_DESKTOP_COMPOSITION
}
AdvancedSettings2.PerformanceFlags = perfflags;
AdvancedSettings2.GrabFocusOnConnect = false;
AdvancedSettings2.minInputSendInterval = 20; // mouse input sent every 20 ms

AdvancedSettings3.EnableAutoReconnect = false;

AdvancedSettings6.SmartSizing = false;
AdvancedSettings6.RedirectClipboard = m_options.RedirectClipboard;
AdvancedSettings6.RedirectDrives = m_options.RedirectDrives;
AdvancedSettings6.RedirectDevices = m_options.RedirectDevices;
AdvancedSettings6.RedirectPorts = m_options.RedirectPorts;
AdvancedSettings6.RedirectSmartCards = m_options.RedirectSmartCards;
AdvancedSettings6.RedirectPOSDevices = false;
AdvancedSettings6.AudioRedirectionMode = options.AudioRedirectionMode;

AdvancedSettings7.RelativeMouseMode = true;

AdvancedSettings8.AudioCaptureRedirectionMode = options.AudioCaptureRedirectionMode;

SecuredSettings2.KeyboardHookMode = 1; // apply remotely (fixes windows key, alt-tab, etc)

SetExtendedProperty("DesktopScaleFactor", GetWindowScaleFactor());
SetExtendedProperty("DeviceScaleFactor", 100u);

if (options.HardwareAssist)
{
SetExtendedProperty("EnableHardwareMode", true);
}

DisableConnectionBar();
SetMaximumNetworkThroughput();

if (options.FrameBufferRedirection)
{
EnableFrameBufferRedirection();
}

if (options.MultiMonitor)
{
EnableMultiMon();
}

InitEvents();
}

public bool IsReady()
{
return m_eventsInitialized;
Expand All @@ -157,9 +231,12 @@ public void InitEvents()
OnEnterFullScreenMode += OnEnteredFullScreenMode;
OnLeaveFullScreenMode += OnLeftFullScreenMode;

m_watcher = VMManager.CreateMsvmWatcher(m_vm.Guid);
m_watcher.EventArrived += OnVMInstanceChange;
m_vm.PropertyChanged += OnVmPropertyChanged;
if (m_vm != null)
{
m_watcher = VMManager.CreateMsvmWatcher(m_vm.Guid);
m_watcher.EventArrived += OnVMInstanceChange;
m_vm.PropertyChanged += OnVmPropertyChanged;
}

m_eventsInitialized = true;
}
Expand All @@ -177,9 +254,12 @@ public void StopEvents()
OnEnterFullScreenMode -= OnEnteredFullScreenMode;
OnLeaveFullScreenMode -= OnLeftFullScreenMode;

m_watcher.Stop();
m_watcher = null;
m_vm.PropertyChanged -= OnVmPropertyChanged;
if (m_vm != null)
{
m_watcher.Stop();
m_watcher = null;
m_vm.PropertyChanged -= OnVmPropertyChanged;
}
}

public override void Connect()
Expand Down Expand Up @@ -224,33 +304,40 @@ private void ConnectInternal(bool EnableEnhancedMode)
return;
}

IMsvm_SecurityElement? security = m_vm.SecurityElement;
if (!EnableEnhancedMode && security != null && security.Shielded != null && security.Shielded.Value)
if (m_vm != null)
{
OnRdpError?.Invoke(this, RdpError.BasicSessionWithShieldedVm);
return;
}

AdvancedSettings7.PCB = m_vm.Guid;
Enhanced = false;
if (EnableEnhancedMode)
{
if (m_vm.EnhancedSessionModeState == IMsvm_ComputerSystem.EnhancedSessionMode.AllowedAndAvailable)
IMsvm_SecurityElement? security = m_vm.SecurityElement;
if (!EnableEnhancedMode && security != null && security.Shielded != null && security.Shielded.Value)
{
Enhanced = true;
AdvancedSettings7.PCB += ";EnhancedMode=1";
System.Diagnostics.Debug.Print("Enhanced mode set");
OnRdpError?.Invoke(this, RdpError.BasicSessionWithShieldedVm);
return;
}
else

AdvancedSettings7.PCB = m_vm.Guid;
Enhanced = false;
if (EnableEnhancedMode)
{
System.Diagnostics.Debug.Print("Enhanced requested but unavailable");
if (security != null && security.Shielded != null && security.Shielded.Value)
if (m_vm.EnhancedSessionModeState == IMsvm_ComputerSystem.EnhancedSessionMode.AllowedAndAvailable)
{
Enhanced = true;
AdvancedSettings7.PCB += ";EnhancedMode=1";
System.Diagnostics.Debug.Print("Enhanced mode set");
}
else
{
System.Diagnostics.Debug.Print("Shielded vm but enhanced mode net yet allowed");
return;
System.Diagnostics.Debug.Print("Enhanced requested but unavailable");
if (security != null && security.Shielded != null && security.Shielded.Value)
{
System.Diagnostics.Debug.Print("Shielded vm but enhanced mode net yet allowed");
return;
}
}
}
}
else
{
Enhanced = EnableEnhancedMode;
}

System.Diagnostics.Debug.Print("Connecting");
m_state = RdpState.Connecting;
Expand All @@ -273,6 +360,8 @@ private void OnVmPropertyChanged(object sender, System.ComponentModel.PropertyCh

protected void OnVMInstanceChange(object sender, WmiEvent<IMsvm_ComputerSystem> e)
{
Debug.Assert(m_vm != null);

IMsvm_ComputerSystem vm = VMManager.GetVMByGuid(e.TargetInstance.Name);

if (vm.EnabledState == m_vm.State && vm.EnhancedSessionModeState == m_vm.EnhancedSessionModeState)
Expand Down Expand Up @@ -340,7 +429,8 @@ protected void OnConnect(object sender, EventArgs e)
{
m_state = RdpState.Connected;

if (m_options.EnhancedSession && !Enhanced && m_vm.EnhancedSessionModeState == IMsvm_ComputerSystem.EnhancedSessionMode.AllowedAndAvailable)
if (m_options.EnhancedSession && !Enhanced &&
(m_vm == null || m_vm.EnhancedSessionModeState == IMsvm_ComputerSystem.EnhancedSessionMode.AllowedAndAvailable))
{
// reconnect in an enhanced session
}
Expand Down Expand Up @@ -397,7 +487,8 @@ protected void OnDisconnect(object sender, IMsTscAxEvents_OnDisconnectedEvent e)
ConnectInternal(m_options.EnhancedSession);
return;
}
else if (e.discReason == 4 && code == ExtendedDisconnectReasonCode.exDiscReasonNoInfo && m_vm.State == IMsvm_ComputerSystem.SystemState.Paused)
else if (e.discReason == 4 && code == ExtendedDisconnectReasonCode.exDiscReasonNoInfo &&
(m_vm == null || m_vm.State == IMsvm_ComputerSystem.SystemState.Paused))
{
ConnectInternal(m_options.EnhancedSession);
return;
Expand Down Expand Up @@ -449,6 +540,11 @@ protected override void OnNotifyMessage(Message m)

public bool IsVideoAvailable(VirtualMachine wmivm = null)
{
if (m_vm == null)
{
return true;
}

IMsvm_ComputerSystem.SystemState state = m_vm.State;
if (wmivm != null)
{
Expand Down
3 changes: 3 additions & 0 deletions VMPlex/Rdp/RdpOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using System.DirectoryServices.ActiveDirectory;
using System.Text;

namespace VMPlex
Expand All @@ -14,6 +15,7 @@ public RdpOptions()
{
// sensible defaults

Domain = "";
Server = "localhost";
Port = 2179;
DesktopWidth = 1024;
Expand All @@ -35,6 +37,7 @@ public RdpOptions()
AudioCaptureRedirectionMode = false;
}

public string Domain { get; set; }
public string Server { get; set; }
public int Port { get; set; }
public int DesktopWidth { get; set; }
Expand Down
6 changes: 6 additions & 0 deletions VMPlex/UI/ManagerPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@
<ui:AppBarSeparator IsCompact="True"/>
<ui:AppBarButton x:Name="vmAdd" Icon="Add" ToolTip="Create VM" Click="OnAddCommand" IsTabStop="False" IsCompact="True"/>
<ui:AppBarButton x:Name="vmDelete" Icon="Delete" ToolTip="Delete VM" Click="OnDeleteCommand" IsTabStop="False" IsCompact="True"/>
<ui:AppBarSeparator IsCompact="True"/>
<ui:AppBarButton x:Name="rdpConnect" ToolTip="Remote Desktop Connection" Click="OnRdpConnect" IsTabStop="False" IsCompact="True">
<ui:AppBarButton.Icon>
<ui:FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE8AF;"/>
</ui:AppBarButton.Icon>
</ui:AppBarButton>
</DockPanel>
</Grid>
<Grid Grid.Row="1">
Expand Down
78 changes: 77 additions & 1 deletion VMPlex/UI/ManagerPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,21 @@ private TabItem CreateVmTab(VirtualMachine vm)
return tab;
}

private TabItem CreateRdpTab(RdpSettings settings)
{
TabItem tab = new TabItem();
CloseableHeader hdr = new CloseableHeader();
hdr.Icon.Content = "\xE8AF";
hdr.Title.SetValue(Label.ContentProperty, settings.Server);
hdr.closeButton.Visibility = Visibility.Visible;
hdr.closeButton.Click += new RoutedEventHandler(Tab_OnCloseClicked);
hdr.closeButton.Tag = tab;
tab.Header = hdr;
tab.Content = new RdpPage(settings);

return tab;
}

private void VmListItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
VirtualMachine vm = (VirtualMachine)((e.Source as ListViewItem).DataContext);
Expand Down Expand Up @@ -156,6 +171,16 @@ private void Tab_OnCloseClicked(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
TabItem tab = (TabItem)button.Tag;
TabControl tc = (TabControl)(((TabItem)this.Parent).Parent);

if (tab.Content is RdpPage)
{
RdpPage rdp = (RdpPage)tab.Content;
rdp.Shutdown();
tc.Items.Remove(tab);
return;
}

VirtualMachine vm = (VirtualMachine)tab.DataContext;
button.Tag = null;

Expand All @@ -165,7 +190,6 @@ private void Tab_OnCloseClicked(object sender, RoutedEventArgs e)
return s;
});

TabControl tc = (TabControl)(((TabItem)this.Parent).Parent);
if (tab.Content != null && tab.Content is VmRdpPage)
{
VmRdpPage rdp = (VmRdpPage)tab.Content;
Expand Down Expand Up @@ -385,6 +409,58 @@ private void OnDeleteCommand(object sender, RoutedEventArgs e)
vm.DeleteFromServer();
}
}

private void OnRdpConnect(object sender, RoutedEventArgs e)
{
var settings = RdpConnectWindow.Show();
if (settings == null)
{
return;
}

if (settings.Server.Length == 0)
{
MessageBox.Show(
MessageBoxImage.Error,
"Remote Desktop Connection Failed",
"Please provide server to connect to.",
MessageBoxButton.OK);
return;
}

TabItem tab;
try
{
tab = CreateRdpTab(settings);
}
catch (Exception ex)
{
MessageBox.Show(
MessageBoxImage.Error,
"Remote Desktop Connection Failed",
ex.Message,
MessageBoxButton.OK);
return;
}

TabControl tc = (TabControl)(((TabItem)this.Parent).Parent);
int index = tc.Items.Add(tab);
Dispatcher.BeginInvoke((Action)(() => tc.SelectedIndex = index));

//
// Store the RDP connection for later.
//
UserSettings.Instance.Mutate(s =>
{
if (s.RdpConnections.Find(
c => (c.Domain == settings.Domain && c.Server == settings.Server)
) == null)
{
s.RdpConnections.Add(settings);
}
return s;
});
}
}

public class VMPidConverter : IValueConverter
Expand Down
18 changes: 18 additions & 0 deletions VMPlex/UI/RdpConnectWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Window x:Class="VMPlex.UI.RdpConnectWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.modernwpf.com/2019"
mc:Ignorable="d"
Title="VMPlex Workstation"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
ui:ThemeManager.IsThemeAware="True"
ui:WindowHelper.UseModernWindowStyle="True">
<ui:SimpleStackPanel Orientation="Horizontal" Margin="12 10 12 10" Spacing="20">
<ui:FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE8AF;"/>
<ComboBox Name="ConnectionBox" IsEditable="True" StaysOpenOnEdit="True" Width="250"/>
<Button Name="ConnectButton" Content="Connect" Click="ConnectButton_Click"/>
</ui:SimpleStackPanel>
</Window>
Loading

0 comments on commit a563176

Please sign in to comment.