Windows, Meet Linux. Linux, Meet RemoteApp!

It has been forever and a dream for me to run Windows apps and Linux apps seamlessly on one Linux box. I have dreamed up almost every imaginable scenario. That day has now arrived. Here were some failed ideas:

  • Wine has always been about 50 steps behind Windows in terms of the API. It doesn't run the apps I need it to. In fact, outside of World of Warcraft via Wine, which was a difficult configuration, I've never really been able to get anything to work like it should. That especially goes for my important applications, like Microsoft Office and Adobe Fireworks.

  • Virtualization works well but gets a bit in the way sometimes on the local machine. Virtualization is an optional part of the solution, though I recommend using a dedicated virtualization server or locally-hosted private compute cloud. Well, that is unless you have much more RAM in your notebook than I do.

  • Seamless virtualization with VirtualBox seemed almost promising with the advent of separate desktops via virtualization. For the same reasons, it was in the way when running locally, which was the only time that seamless mode ever seemed to work for me at all. Still there was the awkward minimizable taskbar and I wanted a full integration so that I could easily launch my Windows apps from my GNOME menus by simply running the application.

  • VNC works for single apps, so it would follow that it could be modified to push certain apps out by using a server launcher. The key problem with VNC is that two remoted apps must share a single desktop workspace, and that real-estate is limited by the physical screen resolution and the number of actual monitors. That won't work for running many remote apps simultaneously.

  • Windows Remote Desktop, RDP, always seemed like the closest solution to the problem. But, then you're stuck with the whole workspace again. Along came RemoteApp for Windows Server, but there was no Linux support for RemoteApp via the established rdesktop RDP client. Until now…

Meet FreeRDP, a coming-of-age RDP client for Linux destined to replace rdesktop as the popular client. It is a shiny new drop-in replacement for rdesktop. Let us first talk about setup.

You are going to need:

  1. Windows Server 2008 or 2008 R2 with Terminal Services and RemoteApp enabled and properly licensed.
  2. Any number of Linux clients (I'm sure there is a practical limit somewhere.)
  3. Experience compiling your own software.

I have Ubuntu 11.10 64-bit (amd64) for my clients. For now my Windows Server 2008 R2 is running on bare-metal hardware, but I am picturing moving that to a virtual server in the very near future.

My instructions will be for Ubuntu 11.10 amd64. You can modify them as you will to fit your distribution.

NOTICE: I have written some scripts to automate this part of the installation. Read the article to see how this process is simplified further. Linux RemoteApp Just Got Way Easier

1. Install FreeRDP

Basically, here are the steps I took:

sudo apt-get install build-essential git cmake libssl-dev libx11-dev libxext-dev libxinerama-dev libxcursor-dev libxdamage-dev libxv-dev libxkbfile-dev libasound2-dev libcups2-dev

sudo apt-get install libcunit1-dev libdirectfb-dev xmlto doxygen

mkdir ~/src
cd ~/src

git clone https://github.com/FreeRDP/FreeRDP.git
cd FreeRDP

cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_SSE2=ON .
make
sudo make install

Then create the file:

sudo nano /etc/ld.so.conf.d/freerdp.conf

With the contents:

/usr/local/lib/freerdp

Save, then run:

sudo ldconfig

2. Create integration files

Create integration files used to run any of my hosted RemoteApp programs. For this example, my server is hosted at 192.168.1.100 and my user name is david and my password is password (it isn't really, but you get the idea).

Init these files:

mkdir ~/bin
touch ~/bin/{remoteapp,convert_local_to_remoteapp}
chmod +x ~/bin/{remoteapp,convert_local_to_remoteapp}

Create this file:

nano ~/bin/remoteapp

With the contents:

#!/bin/bash
#Description: Launches RemoteApp with command line arguments.
#Syntax: remoteapp <exename> <args>
#Depends: convert_local_to_remoteapp, Python 2.x, pwd, and POSIX $HOME.
#Notice: Should work on just about any Ubuntu.
#Author: David Ball, www.daball.me
REMOTE_HOST=192.168.1.100
REMOTE_USER=david
REMOTE_PASS=password

if [ "$#" == "0" ]; then
echo "$0 <exename> <args>"
echo "Use --convert-linux-path followed by a local path to convert it to a remote path."
echo "Example: remoteapp '||winword' /f --convert-linux-path $HOME/myDoc.docx"
exit 1
fi

REMOTE_COMMAND=$1;shift
REMOTE_COMMAND_ARGS=
CWD=`pwd`
REMOTE_WORKING_DIR=`convert_local_to_remoteapp $CWD`

while (($#)); do
if [ "$1" == "--convert-linux-path" ]; then
shift
REMOTE_COMMAND_ARGS="$REMOTE_COMMAND_ARGS`convert_local_to_remoteapp $1` "
else
REMOTE_COMMAND_ARGS="$REMOTE_COMMAND_ARGS$1 "
fi
shift
done
xfreerdp -z -a 32 -x l --app -u "$REMOTE_USER" -p "$REMOTE_PASS" --plugin rdpsnd --plugin rdpdr --data disk:home:"$HOME" disk:vfsroot:/ -- --plugin "$HOME"/src/FreeRDP/channels/rail/rail.so --data "$REMOTE_COMMAND":"$REMOTE_WORKING_DIR":"$REMOTE_COMMAND_ARGS" -- "$REMOTE_HOST"

Create the file:

nano ~/bin/convert_local_to_remoteapp

With the contents:

#!/usr/bin/python
#Converts local / paths to \\\\tsclient\\\\vfsroot for remoteapp consumption
#Converts local $HOME paths to \\\\tsclient\\\\home for remoteapp consumption
#HACK: When %U (used in GNOME menus/file associations) is found, deliberately
#      return empty string to avoid opening a non-existent path
#Author: David Ball, www.daball.me
import sys, os
server = 'tsclient'
share = 'vfsroot'
#get all of cmd line as a single path, regardless of '' or "" quotations used
path = ' '.join(sys.argv[1:])
#trim all whitespace around edges
path = path.strip()
#trim all quotations around edges, NOTE: has potential problem of ignoring
#a file name that ends with a quotation mark, however rare it would seem to be
path = path.strip('\'\"')
#expand ~/ user paths
path = os.path.expanduser(path)
#get the absolute path, converting ./ and ../ and so on
path = os.path.realpath(path)
#check whether it falls under the \\tsclient\home or \\tsclient\vfsroot scope
if path.find(os.environ['HOME']) != -1:
share = 'home'
path = path[len(os.environ['HOME']):]
#trim leading /
if len(path) > 0 and path[0] == '/': path = path[1:]
#join the base path to the share name to the server to the root
path = '/' + os.path.join('/', server, share, path)
#trim trailing /
if path[-1] == '/': path = path[0:-1]
#convert from unix style to windows-style slashes
path = path.replace('/', '\\\\')
#if the word %U shows up, don't return anything, otherwise print the path
if path != '%U':
print path

Be sure that your ~/bin folder is included in your PATH. If you aren't sure, edit your $HOME/.profile with the command:

nano ~/.profile

Make sure it has this:

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$PATH"
fi

Reload your profile by either logging out and logging back in, or use the command:

source ~/.profile

Install Python 2.7.

sudo apt-get install python2.7

You may want to log out and log back in your window manager (if you had to create ~/bin). And, now you can run any hosted RemoteApp using a command such as these:

remoteapp '||winword'
remoteapp '||iexplore'
remoteapp '||excel'

The syntax of the command is like this:

remoteapp '||shared-app-name' command-line-args

You may also add --convert-linux-path to convert a local path to a RemoteApp path on \\tsclient.

An elaborate, useful example would be one for Microsoft Office applications for GNOME integration:

#example command lines useful for GNOME Menus
remoteapp '||winword' /f --convert-linux-path "%U"
remoteapp '||excel' /f --convert-linux-path "%U"
remoteapp '||powerpnt' /O --convert-linux-path "%U"
remoteapp '||msaccess' --convert-linux-path "%U"

This is the exact integration I have used on my Ubuntu box so that I can load it from the menu launcher as well as by double-clicking files in the File Manager (after setting the default program associations). (Microsoft Access does not work since file locking doesn't currently work across the \\tsclient.)

3. Create .desktop files

Create .desktop files where appropriate. You can try various methods to grab out the icons from the program executable files on the remote end and then convert them to PNG files. I would just recommend looking online first. I think these look better anyways:

#download example Microsoft Office icons
cd /usr/share/icons
sudo wget http://icons.iconarchive.com/icons/benjigarner/softdimension/256/MS-Word-2-icon.png
sudo wget http://icons.iconarchive.com/icons/benjigarner/softdimension/256/Excel-icon.png
sudo wget http://icons.iconarchive.com/icons/benjigarner/softdimension/256/PowerPoint-icon.png
sudo wget http://icons.iconarchive.com/icons/benjigarner/softdimension/256/Access-icon.png

Download alacarte.

sudo apt-get install alacarte

Use it like this: How to Create Custom Application Launchers in GNOME 3. Just remember to put your icons in /usr/share/icons as root user.

Enjoy your one-click access to freedom. Have a look at what I've got going on here:

Microsoft Office on Ubuntu 11.10 with RemoteApp and FreeRDP

Buyer beware: I've had to manually log in to my server and reset connections when they don't exit properly. For some reason, it doesn't work after 3 concurrent connections; perhaps a licensing glitch on my end. Maximize doesn't render on my screen. The clipboard integration package doesn't work. And, I haven't quite figured out the RemoteFX capabilities on neither Windows Server 2008 R2 nor FreeRDP.

Despite these limitations of the pre-release version, this is perhaps one of the best integration successes I have had to date. Way to go FreeRDP team!