Search code examples
python-3.xsoapvirtualbox

How to lock virtualbox to get a screenshot through SOAP API


I'm trying to use the SOAP interface of Virtualbox 6.1 from Python to get a screenshot of a machine. I can start the machine but get locking errors whenever I try to retrieve the screen layout.

This is the code:

import zeep

# helper to show the session lock status
def show_lock_state(session_id):
    session_state = service.ISession_getState(session_id)
    print('current session state:', session_state)

# connect
client = zeep.Client('http://127.0.0.1:18083?wsdl')
service = client.create_service("{http://www.virtualbox.org/}vboxBinding", 'http://127.0.0.1:18083?wsdl')
manager_id = service.IWebsessionManager_logon('fakeuser', 'fakepassword')
session_id = service.IWebsessionManager_getSessionObject(manager_id)

# get the machine id and start it
machine_id = service.IVirtualBox_findMachine(manager_id, 'Debian')
progress_id = service.IMachine_launchVMProcess(machine_id, session_id, 'gui')
service.IProgress_waitForCompletion(progress_id, -1)

print('Machine has been started!')
show_lock_state(session_id)

# unlock and then lock to be sure, doesn't have any effect apparently

service.ISession_unlockMachine(session_id)
service.IMachine_lockMachine(machine_id, session_id, 'Shared')


show_lock_state(session_id)

console_id = service.ISession_getConsole(session_id)
display_id = service.IConsole_getDisplay(console_id)
print(service.IDisplay_getGuestScreenLayout(display_id))

The machine is started properly but the last line gives the error VirtualBox error: rc=0x80004001 which from what I read around means locked session.

I tried to release and acquire the lock again, but even though it succeeds the error remains. I went through the documentation but cannot find other types of locks that I'm supposed to use, except the Write lock which is not usable here since the machine is running. I could not find any example in any language.


Solution

  • I found an Android app called VBoxManager with this SOAP screenshot capability. Running it through a MITM proxy I reconstructed the calls it performs and wrote them as the Zeep equivalent. In case anyone is interested in the future, the last lines of the above script are now:

    console_id = service.ISession_getConsole(session_id)
    display_id = service.IConsole_getDisplay(console_id)
    resolution = service.IDisplay_getScreenResolution(display_id, 0)
    print(f'display data: {resolution}')
    
    image_data = service.IDisplay_takeScreenShotToArray(
        display_id,
        0,
        resolution['width'],
        resolution['height'],
        'PNG')
    
    with open('screenshot.png', 'wb') as f:
        f.write(base64.b64decode(image_data))