Automating VirtualBox screenshots with Python
Following on from last week's post, this post is going to look at using the VirtualBox API to take screenshots.
There are two ways the VirtualBox API can be called, either via a Component
Object Model (COM), or using a web service built using
SOAP, so long as the vboxwebsrv
process is started. This post is
going to look at using the COM interface, however the calls should be very
similar to the web service interface.
Setting up Python
The Python's standard library doesn't have any way to use COM interfaces, however the pywin32 module can be used to create COM objects. This can be installed using pip:
$ python -m venv vbox_venv
$ source vbox_venv/Scripts/activate
$ pip install pywin32
Collecting pywin32
Using cached https://files.pythonhosted.org/packages/d4/2d/b927e61c4a2b0aaaab72c8cb97cf748c319c399d804293164b0c43380d5f/pywin32-223-cp36-cp36m-win32.whl
Installing collected packages: pywin32
Successfully installed pywin32-223
Once pywin32
is installed, you should be able to import win32com.client
:
$ python
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 16:07:46) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import win32com.client
>>> win32com.client.__name__
'win32com.client'
Using the API
Once pywin32
is setup, the next thing to do is start a new session. This can
be done using Dispatch
:
import win32com.client
from win32com.client import constants
vbox = win32com.client.Dispatch("VirtualBox.VirtualBox")
session = win32com.client.Dispatch("VirtualBox.Session")
The findMachine
function can then be used to find the virtual machine we want
to screenshot:
machine = vbox.FindMachine("example_vm")
Note: you can either search for machines by name or UUID.
The VirtualBox API uses a locking mechanism to control access to virtual
machines. Before calling the screenshot function, we need to create a lock
using the lockMachine
function:
machine.LockMachine(session, constants.LockType_Shared)
This function takes two parameters, the session to create the lock with, and
the type of lock to create. The VirtualBox COM interface uses several
enumerations to store constants, like the lock type, these can be accessed from
win32com.client.constants
.
After successfully creating a lock it should be possible to use the
GetScreenResolution
and TakeScreenShotToArray
functions to work out the
require resolution and grab a screenshot:
display = session.Console.Display
width, height, _, _, _, _= display.GetScreenResolution(0)
screenshot = display.TakeScreenShotToArray(0, width, height, constants.BitmapFormat_PNG)
At this point we can now release our machine lock:
session.UnlockMachine()
Finally the screenshot data can be written out to a file:
with open('screenshot.png', 'wb') as output_png:
output_png.write(screenshot.tobytes())
If everything goes well, this should create a new screenshot:
$ file screenshot.png
screenshot.png: PNG image data, 720 x 400, 8-bit/color RGB, non-interlaced
Note: the lines above can obviously be put together into a short script.
COM interfaces tips
I've not previously done much with COM interfaces, below are a few points I wish I knew at the start:
-
The VirtualBox SDK docs are very comprehensive, and cover all available functions in detail, although they can be a bit tricky to navigate.
-
You can inspect available constants by calling
win32com.client.constants.__dict__["__dicts__"][0]
after you've called theDispatch
method. -
pywin32 comes with a simple object browser which can be used to browse available COM interface functions. You can access this by running:
$ python vbox_venv/Lib/site-packages/win32com/client/combrowse.py