Friday, 20 January 2012

Opening files under VirtualBox guest from the Linux host


Updated: 08/06/2015

I have been using VirtualBox to run windows software on my linux box for a while. I always wondered how nice it would be to simply click on a windows file (.doc, .xls, .exe, etc.) under linux to open it with the appropriate application (winword, excel, etc.) under the virtual machine instead of saving the file and looking it up under windows. I did not find any solution for this so I looked around and invented my own one. The operation is based on the VBoxManage guestcontrol utility provided by VirtualBox. This command allows you to initiate program execution inside the guest from the host. A small bash script handles this on the host by converting the filename to windows format and opening the file inside the guest Windows machine.

The steps for setting all these up:

Add the / directory to the windows guest

Install VirtualBox guest additions if it is not installed already. I do not want to describe how to install guest additions, it is well documented elsewhere. Briefly under Arch Linux you have to install the virtualbox-additions AUR package, add /usr/lib/virtualbox/additions/VBoxGuestAdditions.iso to your VirtualBox storage and install the guest additions under the guest.

Create a shared folder under VirtualBox settings and add your linux host's root (/) filesystem. Now let's say your linux / direcrory is the f: drive under the windows guest.



As a result a file named /home/<your_username>/document.doc under linux looks like f:\home\<your_username>\document.doc under the guest.



Install Quiet.exe under the windows guest

UPDATE: Under my latest Win7 guest installation cmd.exe works just fine without bringing up a command window and without the need of setting up environment variables. If this is not the case you can use Quiet.exe instead.

To open files remotely you can use cmd.exe or the start command but both opens a console window besides the program that you want to run which I find quite annoying. To hide it you have to use additional software: a hand-made vbs script or a third-party tool like hstart, cmdow or Quiet. I will follow the last one, this simple tool works fine.

Download Quiet from their website and copy the Quiet.exe anywhere you like under your windows guest.

Let's say it will be here:
c:\Users\<your_windows_username>\Quiet.exe

Enable passwordless guest control under the windows guest

If you do not use password in your windows guest (like me), the guest's group policy must be changed under the guest OS. To do so, open the group policy editor on the command line by typing gpedit.msc, open the key Computer Configuration \ Windows Settings \ Security Settings \ Local Policies \ Security Options and change the value of Accounts: Limit local account use of blank passwords to console logon only to Disabled.

Without this step you will get an error when trying use VboxManage:
VBoxManage: error: The specified user was not able to logon on guest.

Install detox for cleaning up filenames

Not only me but VboxManage also hates special characters and spaces in filenames. Detox is a handy tool for swiping such garbages out of filenames. It will replace spaces to underline sign, convert characters with accents for the non-accent version and remove special characters. You can install it from AUR then use it in your scripts (like I did below).

Create the winopen.sh script

UPDATE: since Virtualbox 5.0.0 the execute subcommand of VBoxManage guestcontrol is deprecated. Use run instead with the --exe option (see the script below)

UPDATE: since Virtualbox 5.0.0 instead of --environment use the --putenv option in your "VBoxManage guestcontrol ... run" command (see the script below)

UPDATE: since Virtualbox 5.1.x instead of run use start! run gives session problems, commands stuck after termination. run works for me. Oracle's documentation sucks by the way.
 
Create a winopen.sh bash script under the linux host. This will be the command for opening windows files within the guest.

#!/bin/bash

# stdout and errors go here:
exec 2>>"/home/<your_linux_username>/.xlog<or_anything_you_like>"
exec 1>&2

script="$(basename "$0")"
vmname="<the_name_of_your_windows_guest_under_Virtuabox>"
driveletter="f:"  #your ROOT folder's drive letter comes here 

# Start vm if not running already
if [ $(VBoxManage list runningvms | wc -l) = 0 ]; then
   VBoxManage startvm $vmname
   echo "Please wait, starting virtual PC..."
   sleep 5s
fi

until VBoxManage showvminfo $vmname | grep '"Graphics Mode": active/running' > /dev/null
 do
  sleep 0.5s
 done

# Set the absolute full path of the file in the linux host
for i in "$@"
do
 if echo $i | grep '^/' > /dev/null
   then targetfullpath=$i
   else targetfullpath=$PWD'/'$i
 fi

#Cleanup filename, you need detox installed! 

cleanpath=$(detox -vs utf_8 "$targetfullpath" | tail -1 | awk '{print $NF}')

# The same filepath under the windows guest machine
winfullpath=$(echo $driveletter$cleanpath | sed 's/\//\\/g')

# Open the file under windows
echo $script": opening "$targetfullpath" ("$winfullpath") in Virtualbox"
#sleep 1   # uncomment and increase this if you see empty Winword windows

#In newer Windows installations try this:
VBoxManage guestcontrol "$vmname" start --exe "cmd.exe" --username 'cuh' --putenv "USERPROFILE=C:\Users\<your_windows_username>" --putenv "APPDATA=C:\Users\<your_windows_username>\AppData\Roaming" -- "cmd" "/c" $winfullpath &
# For older installations:
#VBoxManage guestcontrol "$vmname" execute --image "cmd.exe" --username "<your_windows_username>" -- "cmd" "/c" $winfullpath & 
# If you want to use quiet.exe or have problems with environment variables, try this:
#VBoxManage guestcontrol "$vmname" execute --image "C:\Users\<your_windows_username>\Quiet.exe" --wait-exit --username "<your_windows_username>" --environment "USERPROFILE=C:\Users\<your_windows_username>" --environment "APPDATA=C:\Users\<your_windows_username>\AppData\Roaming" -- "cmd" "/c" $winfullpath &

done


Make your script executable, i.e.:
chmod 755 winopen.sh

Optionally place winopen.sh to your path, (i.e. /home/<your_username>/bin/winopen.sh and put export PATH=$PATH:~/bin under your .bashrc).

Important notices on environment variables:
Without the --environment "USERPROFILE=C:\Users\<your_windows_username>" attribute in your winopen.sh script your guest OS sometimes does not get this environment variable and cannot open your files. You get errors like this:
"your AutoCorrect file, MSO1038.acl could not be saved." or "Microsoft Excel cannot access the file ... There are several possible reasons: The file name or path does not exist..."

The lack of the --environment "APPDATA=C:\Users\<your_windows_username>\AppData\Roaming" attribute can slow down Word document spell checking.

In general all the environment variables should be the same as in the Windows guest environment but I was just lazy to write all the environment set to the VboxManage command.


Usage

Invoking the winopen.sh script with one or more filenames will start your Virtualbox (if it was not running already), clean up your filename(s) (if there were special characters in them) and open the file(s) under the windows guest. For example:

winopen.sh /home/<your_username>/document.doc
or simply (with relative pathname):
winopen.sh document.doc

You can configure Thunderbird, Firefox, file managers, etc. to use winopen.sh when opening windows files (.doc, .xls, ppt, .exe, etc.), so you simply need to click on an e-mail document attachment or an excel link and it will open automatically under windows.





Any improvements are welcome!

6 comments:

  1. Thank you for this great info! For VirtualBox 4.3.14 r95030 and having a user with password authentification within Windows 7 I had to adapt the second last line to the following to make this work:

    VBoxManage guestcontrol "$vmname" exec --image "c:\Users\Admin\Quiet.exe" --wait-exit --username "Admin" --password --environment "USERPROFILE=C:\Users\Admin" --environment "APPDATA=C:\Users\Admin\AppData\Roaming" -- "cmd" "/c" $winfullpath &

    ReplyDelete
  2. ...after --password fill in your Windows user account password of course...

    ReplyDelete
  3. "VBoxManage guestcontrol exec --image..." is deprecated. Use "VBoxManage guestcontrol run --exe..." instead. I updated te blog post above to reflect these changes.

    ReplyDelete
  4. Since Virtualbox 5.0.0 instead of --environment use the --putenv option in your "VBoxManage guestcontrol ... run" command. I updated te blog post above to reflect these changes.

    ReplyDelete
  5. Thank you!!

    How close VirtualBox in the same script, when you close the application (eg. Excel)?

    --wait-stdout and VBoxManage controlvm: does not work

    ReplyDelete