Hacking VMConnect

I started out using VMWare Workstation before Hyper-V even existed. There were some simple things that I was really surprised were missing from Hyper-V V1 when it came out. Most importantly, copy and paste and smart sizing. Now we are on v3 and we still don’t have these abilities. One alternative is to just RDP into the target machine (or use PowerShell…I know, I know ;)). Well I stumbled onto the fact today that VMConnect is a managed assembly. For some reason I didn’t think this was the case. After cracking it open with Reflector, I realized that the connect functionality is just a hosted RDP OCX. The control just connects to an alternate RDP port. I’m not quite sure if it’s really RDPing to the VM or really just RDPing to an internal component of Hyper-V and they are virtualizing the display. The former seems more likely because the port is accessible on Linux machines and when a VM is booting up. RDP wouldn’t be available in either case. This may be why these particular functionalities haven’t made it into the product, even in the third version.

So, naturally, I ripped open VMConnect and made a PowerShell script that can add Smart Sizing. I’m still working on Clipboard support.

The first step is to load up the VMConnect assemblies and then create a helper class for calling the ConnectionHelper class. This was done to get around an ambigious overload failure.

$ClientAssemblyPath = Split-Path -Path $VMConnectPath -Parent
$ClientAssemblyPath = Join-Path $ClientAssemblyPath "Microsoft.Virtualization.Client.dll"
$ManagementAssemblyPath = Join-Path $ClientAssemblyPath "Microsoft.Virtualization.Client.Management.dll"
 
Add-Type -Path $ClientAssemblyPath
 
[Reflection.Assembly]::LoadFile($VMConnectPath) | Out-Null
Add-Type "
    using System;
    using Microsoft.Virtualization.Client;
    using Microsoft.Virtualization.Client.Management;
    public class ConnectHelperEx 
    {
        public static IVMComputerSystem TryGetVirtualMachine(string server, string vmName, out Exception ex)
        {
            IVMComputerSystem vm;
            ConnectionHelper.TryGetVirtualMachine(server, vmName, out vm, out ex);
            return vm;
        }
    }
" -ReferencedAssemblies $ClientAssemblyPath,$ManagementAssemblyPath

Next, I got a hold of the Server and Virtual Machine objects, the VMConnect form and the RDP controls hosted within it. Then I added the private fields using some reflection and Add-Member.

[Exception]$exception = $null
$VM = [ConnectHelperEx]::TryGetVirtualMachine($Server, [string]$VMName, [ref] $exception)
 
$TS = [Microsoft.Virtualization.Client.Management.ObjectLocator]::GetTerminalService($vm.Server)
 
$RdpConnectionInfo = New-Object Microsoft.Virtualization.Client.InteractiveSession.RdpConnectionInfo -ArgumentList $Server,$VM,$TS.ListenerPort
 
$InteractiveSessionForm = New-Object Microsoft.Virtualization.Client.InteractiveSession.InteractiveSessionForm -ArgumentList $RdpConnectionInfo
 
$InteractiveSessionForm = $InteractiveSessionForm | Add-PrivateField
$RDPViewer = $InteractiveSessionForm.m_RdpViewer | Add-PrivateField
$RdpControl = $RDPViewer.m_RdpClient  | Add-PrivateField

Finally, I setup a couple event handlers and some other settings to get around issues I encountered. The full screen hack doesn’t actually work. The VM won’t take up the full screen. The Resize hack does work. When you pull the handles of the VMConnect window you won’t get scroll bars anymore; the VM’s resolution will smart size. I had to enter my credentials to prevent a dialog from appearing when I connected to the machine.

#Doesn't work
$RDPViewer.add_FullScreenChanged({
    if ($RDPControl.FullScreen)
    {
		$RDpControl.Size = New-Object System.Drawing.Size -ArgumentList ([System.Windows.Forms.SystemInformation]::VirtualScreen.Width),([System.Windows.Forms.SystemInformation]::VirtualScreen.Height)
    }
})
#Works!
$RDPViewer.add_Resize([EventHandler]{ 
    $RDpControl.Size = New-Object System.Drawing.Size -ArgumentList $RDPViewer.Width,$RDPViewer.Height
    $RDpControl.Location = New-Object System.Drawing.Point -ArgumentList 0,0
})
 
#So I don't get asked for creds
$RDPControl.UserName = "-" #<-- Actual creds here!
$RDPControl.Domain = "-"
$RDPControl.AdvancedSettings2.ClearTextPassword= "-"
 
#Works!
$RDpControl.AdvancedSettings2.SmartSizing = $true
 
#Doesn't work :(
$RDpControl.AdvancedSettings2.RedirectDrives = $true
$RDpControl.AdvancedSettings5.RedirectClipboard = $true

It’s a pretty neat hack but I wish I could get the clipboard to work.

small

Small

stretchedfunny

Squished

 

I’m hoping some one in the community could help get to the bottom of this or that someone from Microsoft will show me why this won’t work. One interesting thing I noticed is the RDP port that VMConnect is “connecting” to is 2179. Using MSTSC to try and connect to that port fails. There is some trickery going on here but I haven’t gotten to the bottom of it…

If anything I think it would be possible to start RDPClip and communicate over a virtual channel manually. I imagine that would be some serious work though.

FYI: I”ve tried starting and restarting RDPClip from within the VM and it doesn’t fix anything.

Download the script here.

You can leave a response, or trackback from your own site.

10 Responses to “Hacking VMConnect”

  1. Thanks for sharing Adam! :) Haven’t tried it yet, but both issues you mentioned (resizing, clipboard) are real PITA to me. All in all: I usually end up with network interface dedicated to connect from host to VM and use RDP client anyways. If someone would fix clipboard – I could avoid it in scenarios where I want to test something isolated from my host… :)

    • adamdriscoll says:

      You’re welcome. This is what I was trying to fix as well. RDP takes extra steps sometimes that make it a bit more difficult to use. We will see about clipboard support. :D

  2. Stefa says:

    Hi Adam,

    If you choose to use RDP there is another great feature you can use and that’s Smart Sizing. You can enable this by right-clicking on the the RDP Icon when you have RDP-ed on to a machine.

    Now you will be able to Resize your RDP screen automatically :-)

    /Stefan

  3. Bahus says:

    Hello, Adam.
    Thank you for this script! I’t exactly what I search. But I have trouble to run this when use RDP to HyperV-server:

    Exception when calling “.ctor” with “1″ argument: “Creating an instance of the control ActiveX” d2ea46a
    7-c2bf-426b-af24-e19c44456399 “impossible: the current thread is not in single-threaded container.”
    C:\Users\Administrator\Desktop\VMConnect.ps1:61 знак:37
    + $InteractiveSessionForm = New-Object <<<< Microsoft.Virtualization.Client.InteractiveSession.InteractiveSessionForm
    -ArgumentList $RdpConnectionInfo
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

    PS: Sorry for my bad english.

  4. Thanks Adam for all your help, He and I wrote a powershell script that leverages freeRDP’s wfreerdp.exe to replace vmconnect.exe and mstsc.exe for the Free Core Server 2012. I have published this a project at vmconnect.codeplex.com. Where you can download and install the solution.

  5. eli says:

    Hi I had a question about vmconnect and ports. I know this was posted a while ago but hoping im in the right place considering the work that was done here. Is It possible to tell vmconnect to connect on a different port. In my case if we are able to get the server listening on a different port. and all we need is the client to connect on a different port, is there a way to do this. either using a config/xml/host file or a method like VNC with HOSTIP:[port]

    any info on this would be great thanks.

Leave a Reply

In an effort to prevent automatic filling, you should perform a task displayed below.



− four = 5