Can I launch a program or web link on the host OS from within a VMware Guest VM?

1

1

I have some software which won't run on my machine (Windows Vista) so I run it in a VMware VM (Windows XP) using unity, to give me a seamless user experience.TM *8')

This generally works very well, but the software allows me to launch links in a web browser, which obviously launches the link in a new web browser within the VM, while I would like links to be launched such that the web page ends up on the web browser on my Host.

Does anyone know if this is possible and if so, how?

I can configure what command is run to launch a link in the application, so if it requires a specific command to be run on the guest, that should be no problem.

Thanks.

Mark Booth

Posted 2011-05-24T20:13:40.317

Reputation: 2 324

This is possible in VMware Fusion's global preferences, which is the Mac version of VMware. Check your VMware settings.

– Daniel Beck – 2011-05-29T13:28:15.700

Answers

3

I've been looking for a way to launch Windows applications (the host) from an Ubuntu virtual machine (under VMWare Player). I got a bit carried away and wrote the client and server scripts listed below. The guest OS is not Windows, so these will need some changes to work for a Windows guest. I used this setup to have Git (running on the Ubuntu guest) call KDiff3 on the host when merging.

The following Python script (host_run_server.py) acts as a server accepting commands from the guest. It expects the guest to provide a Samba share with name GUEST_ROOT_SHARE (set this at the top of the script) that exposes the root of the filesystem. This share is mapped to a drive GUEST_DRIVE. This is needed so that the host and guest can access the same files. In my case, I already had also mounted 'My Documents' to a folder on the guest to be able to use git on my host's files.

import asyncore, asynchat
import os
import socket
import shlex, subprocess
import threading

# make the root / of the guest accessible as a samba share and map
# this share in the host to GUEST_DRIVE

HOST_IP = '192.168.126.1'
GUEST_IP = '192.168.126.129'
GUEST_ROOT_SHARE = 'root'
GUEST_DRIVE = 'K:'

TCP_PORT = 5005
BUFFER_SIZE = 1024
ENCODING = 'utf-8'

# map network drive
try:
    import win32wnet
    import pywintypes
    from win32netcon import RESOURCETYPE_DISK

    network_path = r'\\{}\{}'.format(GUEST_IP, GUEST_ROOT_SHARE)
    try:
        win32wnet.WNetAddConnection2(RESOURCETYPE_DISK, GUEST_DRIVE, network_path)
    except pywintypes.error as e:
        if (e.args[0] != 85 or
            win32wnet.WNetGetUniversalName(GUEST_DRIVE) != network_path):
            raise
except ImportError:
    pass


# allow GUI applications to pop to front on Windows
try:
    import win32gui
    from win32con import SPI_SETFOREGROUNDLOCKTIMEOUT

    result = win32gui.SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0)
    if result is not None:
        print("Failed:", result)
except ImportError:
    pass


class Handler(asynchat.async_chat):    
    def __init__(self, sock, map=None):
        asynchat.async_chat.__init__(self, sock, map=map)
        self.remote_ip, self.remote_port = self.socket.getpeername()
        self.log('connected')
        self.set_terminator(b'\x00')        
        self.data = b''
        self.state = 'cwd'

    def handle_close(self):
        remote_ip, remote_port = self.socket.getpeername()
        self.log('disconnected')
        self.close()

    def collect_incoming_data(self, data):
        self.data += data

    def found_terminator(self):
        if self.state == 'cwd':
            self.cwd = self.data.decode(ENCODING)
            self.state = 'cmd'
            self.data = b''
        elif self.state == 'cmd':
            self.cmd = self.data.decode(ENCODING)
            self.reply()
            self.state = 'end'

    def prepare(self):
        cwd = GUEST_DRIVE + self.cwd.replace('/', '\\')
        self.log('in {}'.format(cwd))
        os.chdir(cwd)
        cmd_args = []
        for arg in shlex.split(self.cmd):
            if arg.startswith('[FILE]'):
                arg = arg[6:].replace('/', '\\')
                if arg.startswith('\\'):
                    arg = GUEST_DRIVE + arg
            cmd_args.append(arg)
        return cwd, cmd_args

    def run(self, cwd, cmd_args):
        self.log('executing: {}'.format(' '.join(cmd_args)))
        try:
            p = subprocess.Popen(cmd_args,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE,
                                 shell=True)
            out, err = p.communicate()
            rcode = p.returncode
        except WindowsError as e:
            out = b''
            err = '{}: {}\n'.format(e.__class__.__name__, e.args[1]).encode(ENCODING)
            rcode = -1
        return rcode, out, err

    def reply(self):
        cwd, cmd_args = self.prepare()
        rc, out, err = self.run(cwd, cmd_args)
        self.push(str(len(out)).encode(ENCODING) +  b'\x00')
        if len(out):
            self.push(out)
        self.push(str(len(err)).encode(ENCODING) +  b'\x00')
        if len(err):
            self.push(err)
        self.push(str(rc).encode(ENCODING) + b'\x00')

    def log(self, msg):
        print("[{}:{}]\t{}".format(self.remote_ip, self.remote_port, msg))


class HandlerThread(threading.Thread):
    def __init__(self, sock):
        super().__init__()
        self.sock = sock

    def run(self):
        handler = Handler(self.sock)
        asyncore.loop(map=handler._map)


class Server(asyncore.dispatcher):
    def __init__(self, host, port, guest_ip):
        asyncore.dispatcher.__init__(self, map={})
        self.guest_ip = guest_ip
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind((host, port))
        self.listen(5)
        print("Service started. Listening on {} port {}."
              .format(host, port))

    def handle_accepted(self, sock, addr):
        (guest_ip, guest_port) = addr
        if guest_ip == self.guest_ip:
            ht = HandlerThread(sock)
            ht.start()
        else:
            print("Ignoring request from {}".format(guest_ip))


server = Server(HOST_IP, TCP_PORT, GUEST_IP)
asyncore.loop(map=server._map)

Below is the script to invoke on the guest side (host_run.py).

#!/usr/bin/env python3

import asyncore, asynchat
import os
import socket
import sys
from optparse import OptionParser


HOST_IP = "192.168.126.1"
GUEST_IP = "192.168.126.129"
HOST_IS_WINDOWS = True

TCP_PORT = 5005
BUFFER_SIZE = 1024
ENCODING = 'utf-8'

STD_ENCODING = 'cp1252' if HOST_IS_WINDOWS else ENCODING


class HostRun(asynchat.async_chat):    
    def __init__(self, host, port):
        asynchat.async_chat.__init__(self)
        self.set_terminator(b'\x00')        
        self.data = b''
        self.state = 'stdout1'
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((host, port))

    def handle_connect(self):
        self.push(os.getcwd().encode(ENCODING) + b'\x00')
        self.push(command.encode(ENCODING) + b'\x00')

    def collect_incoming_data(self, data):
        self.data += data

    def found_terminator(self):
        if self.state == 'stdout1':
            stdout_len = int(self.data.decode(ENCODING))
            if stdout_len:
                self.set_terminator(stdout_len)
                self.state = 'stdout2'
            else:
                self.state = 'stderr1'
        elif self.state == 'stdout2':
            stdout = self.data.decode(STD_ENCODING)
            sys.stdout.write(stdout)
            self.set_terminator(b'\x00')
            self.state = 'stderr1'
        elif self.state == 'stderr1':
            stderr_len = int(self.data.decode(ENCODING))
            if stderr_len:
                self.set_terminator(stderr_len)
                self.state = 'stderr2'
            else:
                self.state = 'rc'
        elif self.state == 'stderr2':
            stderr = self.data.decode(STD_ENCODING)
            sys.stderr.write(stderr)
            self.set_terminator(b'\x00')
            self.state = 'rc'
        elif self.state == 'rc':
            rc = int(self.data.decode(ENCODING))
            sys.exit(rc)
            self.close_when_done()
        self.data = b''

    def handle_close(self):
        remote_ip, remote_port = self.socket.getpeername()
        print("%s:%s disconnected" %(remote_ip, remote_port))
        self.close()


parser = OptionParser()
(options, args) = parser.parse_args()

command = ' '.join(args)

HostRun(HOST_IP, TCP_PORT)

asyncore.loop()

The scripts take care of translating file paths. For this to work, you need to prepend paths passed as arguments to the client script with [FILE]

First start the server script on the host. Now you can pass commands to the client script:

brecht@krubuntu ~ $ ./host_run.py dir [FILE]/home

This will translate /home to K:\home and thus execute dir K:\home on the host. The server sends the stdout/stderr output and return code back to the client, which spits it back out to the shell prompt:

 Volume in drive K is root
 Volume Serial Number is 64C2-522A

 Directory of K:\home

07/22/2012  22:13    <DIR>          .
12/04/2012  06:53    <DIR>          ..
02/28/2013  21:56    <DIR>          brecht
               0 File(s)              0 bytes
               3 Dir(s)  12,723,302,400 bytes free

Brecht Machiels

Posted 2011-05-24T20:13:40.317

Reputation: 210

1

This could be done, but I think it would require network communication between guest and host, and a helper program running on each. You could alternatively just copy/paste if it's not too much hassle.

What's the software that won't run on Vista? I'm guessing you tried compatibility mode etc.

Spectre

Posted 2011-05-24T20:13:40.317

Reputation: 1 045

I could write some software to do this myself, a 'server' on the host run as me, with a client on the VM, but I was rather hoping that there was already a mechanism to do this. – Mark Booth – 2011-05-24T22:14:11.443