tstCAPIGlue.c revision 123c444cea5d205957edac72f9c082010a45a76d
/* $Revision$ */
/** @file tstCAPIGlue.c
* Demonstrator program to illustrate use of C bindings of Main API.
*
* It has sample code showing how to retrieve all available error information,
* and how to handle active (event delivery through callbacks) or passive
* (event delivery through a polling mechanism) event listeners.
*/
/*
* Copyright (C) 2009-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "VBoxCAPIGlue.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifndef WIN32
# include <signal.h>
# include <unistd.h>
# include <sys/poll.h>
#endif
/**
* Select between active event listener (defined) and passive event listener
* (undefined). The active event listener case needs much more code, and
* additionally requires a lot more platform dependent code.
*/
#undef USE_ACTIVE_EVENT_LISTENER
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Set by Ctrl+C handler. */
static volatile int g_fStop = 0;
#ifdef USE_ACTIVE_EVENT_LISTENER
# ifdef WIN32
/** The COM type information for IEventListener, for implementing IDispatch. */
static ITypeInfo *g_pTInfoIEventListener = NULL;
# endif /* WIN32 */
#endif /* USE_ACTIVE_EVENT_LISTENER */
static const char *GetStateName(MachineState_T machineState)
{
switch (machineState)
{
case MachineState_Null: return "<null>";
case MachineState_PoweredOff: return "PoweredOff";
case MachineState_Saved: return "Saved";
case MachineState_Teleported: return "Teleported";
case MachineState_Aborted: return "Aborted";
case MachineState_Running: return "Running";
case MachineState_Paused: return "Paused";
case MachineState_Stuck: return "Stuck";
case MachineState_Teleporting: return "Teleporting";
case MachineState_LiveSnapshotting: return "LiveSnapshotting";
case MachineState_Starting: return "Starting";
case MachineState_Stopping: return "Stopping";
case MachineState_Saving: return "Saving";
case MachineState_Restoring: return "Restoring";
case MachineState_TeleportingPausedVM: return "TeleportingPausedVM";
case MachineState_TeleportingIn: return "TeleportingIn";
case MachineState_FaultTolerantSyncing: return "FaultTolerantSyncing";
case MachineState_DeletingSnapshotOnline: return "DeletingSnapshotOnline";
case MachineState_DeletingSnapshotPaused: return "DeletingSnapshotPaused";
case MachineState_RestoringSnapshot: return "RestoringSnapshot";
case MachineState_DeletingSnapshot: return "DeletingSnapshot";
case MachineState_SettingUp: return "SettingUp";
default: return "no idea";
}
}
/**
* Ctrl+C handler, terminate event listener.
*
* Remember that most function calls are not allowed in this context (including
* printf!), so make sure that this does as little as possible.
*
* @param iInfo Platform dependent detail info (ignored).
*/
static BOOL VBOX_WINAPI ctrlCHandler(DWORD iInfo)
{
(void)iInfo;
g_fStop = 1;
return TRUE;
}
/**
* Sample event processing function, dumping some event information.
* Shared between active and passive event demo, to highlight that this part
* is identical between the two.
*/
static HRESULT EventListenerDemoProcessEvent(IEvent *event)
{
VBoxEventType_T evType;
HRESULT rc;
if (!event)
{
printf("event null\n");
return S_OK;
}
evType = VBoxEventType_Invalid;
rc = IEvent_get_Type(event, &evType);
if (FAILED(rc))
{
printf("cannot get event type, rc=%#x\n", rc);
return S_OK;
}
switch (evType)
{
case VBoxEventType_OnMousePointerShapeChanged:
printf("OnMousePointerShapeChanged\n");
break;
case VBoxEventType_OnMouseCapabilityChanged:
printf("OnMouseCapabilityChanged\n");
break;
case VBoxEventType_OnKeyboardLedsChanged:
printf("OnMouseCapabilityChanged\n");
break;
case VBoxEventType_OnStateChanged:
{
IStateChangedEvent *ev = NULL;
enum MachineState state;
rc = IEvent_QueryInterface(event, &IID_IStateChangedEvent, (void **)&ev);
if (FAILED(rc))
{
printf("cannot get StateChangedEvent interface, rc=%#x\n", rc);
return S_OK;
}
if (!ev)
{
printf("StateChangedEvent reference null\n");
return S_OK;
}
rc = IStateChangedEvent_get_State(ev, &state);
if (FAILED(rc))
printf("warning: cannot get state, rc=%#x\n", rc);
IStateChangedEvent_Release(ev);
printf("OnStateChanged: %s\n", GetStateName(state));
fflush(stdout);
if ( state == MachineState_PoweredOff
|| state == MachineState_Saved
|| state == MachineState_Teleported
|| state == MachineState_Aborted
)
g_fStop = 1;
break;
}
case VBoxEventType_OnAdditionsStateChanged:
printf("OnAdditionsStateChanged\n");
break;
case VBoxEventType_OnNetworkAdapterChanged:
printf("OnNetworkAdapterChanged\n");
break;
case VBoxEventType_OnSerialPortChanged:
printf("OnSerialPortChanged\n");
break;
case VBoxEventType_OnParallelPortChanged:
printf("OnParallelPortChanged\n");
break;
case VBoxEventType_OnStorageControllerChanged:
printf("OnStorageControllerChanged\n");
break;
case VBoxEventType_OnMediumChanged:
printf("OnMediumChanged\n");
break;
case VBoxEventType_OnVRDEServerChanged:
printf("OnVRDEServerChanged\n");
break;
case VBoxEventType_OnUSBControllerChanged:
printf("OnUSBControllerChanged\n");
break;
case VBoxEventType_OnUSBDeviceStateChanged:
printf("OnUSBDeviceStateChanged\n");
break;
case VBoxEventType_OnSharedFolderChanged:
printf("OnSharedFolderChanged\n");
break;
case VBoxEventType_OnRuntimeError:
printf("OnRuntimeError\n");
break;
case VBoxEventType_OnCanShowWindow:
printf("OnCanShowWindow\n");
break;
case VBoxEventType_OnShowWindow:
printf("OnShowWindow\n");
break;
default:
printf("unknown event: %d\n", evType);
}
return S_OK;
}
#ifdef USE_ACTIVE_EVENT_LISTENER
struct IEventListenerDemo;
typedef struct IEventListenerDemo IEventListenerDemo;
typedef struct IEventListenerDemoVtbl
{
HRESULT (*QueryInterface)(IEventListenerDemo *pThis, REFIID riid, void **ppvObject);
ULONG (*AddRef)(IEventListenerDemo *pThis);
ULONG (*Release)(IEventListenerDemo *pThis);
#ifdef WIN32
HRESULT (*GetTypeInfoCount)(IEventListenerDemo *pThis, UINT *pctinfo);
HRESULT (*GetTypeInfo)(IEventListenerDemo *pThis, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
HRESULT (*GetIDsOfNames)(IEventListenerDemo *pThis, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
HRESULT (*Invoke)(IEventListenerDemo *pThis, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
#endif
HRESULT (*HandleEvent)(IEventListenerDemo *pThis, IEvent *aEvent);
} IEventListenerDemoVtbl;
typedef struct IEventListenerDemo
{
struct IEventListenerDemoVtbl *lpVtbl;
int cRef;
#ifdef WIN32
/* Active event delivery needs a free threaded marshaler, as the default
* proxy marshaling cannot deal correctly with this case. */
IUnknown *pUnkMarshaler;
#endif
} IEventListenerDemo;
/* Defines for easily calling IEventListenerDemo functions. */
/* IUnknown functions. */
#define IEventListenerDemo_QueryInterface(This,riid,ppvObject) \
( (This)->lpVtbl->QueryInterface(This,riid,ppvObject) )
#define IEventListenerDemo_AddRef(This) \
( (This)->lpVtbl->AddRef(This) )
#define IEventListenerDemo_Release(This) \
( (This)->lpVtbl->Release(This) )
#ifdef WIN32
/* IDispatch functions. */
#define IEventListenerDemo_GetTypeInfoCount(This,pctinfo) \
( (This)->lpVtbl->GetTypeInfoCount(This,pctinfo) )
#define IEventListenerDemo_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
( (This)->lpVtbl->GetTypeInfo(This,iTInfo,lcid,ppTInfo) )
#define IEventListenerDemo_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
( (This)->lpVtbl->GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) )
#define IEventListenerDemo_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
( (This)->lpVtbl->Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) )
#endif
/* IEventListener functions. */
#define IEventListenerDemo_HandleEvent(This,aEvent) \
( (This)->lpVtbl->HandleEvent(This,aEvent) )
/**
* Event handler function, for active event processing.
*/
static HRESULT IEventListenerDemoImpl_HandleEvent(IEventListenerDemo *pThis, IEvent *event)
{
return EventListenerDemoProcessEvent(event);
}
static HRESULT IEventListenerDemoImpl_QueryInterface(IEventListenerDemo *pThis, const IID *iid, void **resultp)
{
/* match iid */
if ( !memcmp(iid, &IID_IEventListener, sizeof(IID))
|| !memcmp(iid, &IID_IDispatch, sizeof(IID))
|| !memcmp(iid, &IID_IUnknown, sizeof(IID)))
{
IEventListenerDemo_AddRef(pThis);
*resultp = pThis;
return S_OK;
}
#ifdef WIN32
if (!memcmp(iid, &IID_IMarshal, sizeof(IID)))
return IUnknown_QueryInterface(pThis->pUnkMarshaler, iid, resultp);
#endif
return E_NOINTERFACE;
}
static HRESULT IEventListenerDemoImpl_AddRef(IEventListenerDemo *pThis)
{
return ++(pThis->cRef);
}
static HRESULT IEventListenerDemoImpl_Release(IEventListenerDemo *pThis)
{
HRESULT c;
c = --(pThis->cRef);
if (!c)
free(pThis);
return c;
}
#ifdef WIN32
static HRESULT IEventListenerDemoImpl_GetTypeInfoCount(IEventListenerDemo *pThis, UINT *pctinfo)
{
if (!pctinfo)
return E_POINTER;
*pctinfo = 1;
return S_OK;
}
static HRESULT IEventListenerDemoImpl_GetTypeInfo(IEventListenerDemo *pThis, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
if (!ppTInfo)
return E_POINTER;
ITypeInfo_AddRef(g_pTInfoIEventListener);
*ppTInfo = g_pTInfoIEventListener;
return S_OK;
}
static HRESULT IEventListenerDemoImpl_GetIDsOfNames(IEventListenerDemo *pThis, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
return ITypeInfo_GetIDsOfNames(g_pTInfoIEventListener, rgszNames, cNames, rgDispId);
}
static HRESULT IEventListenerDemoImpl_Invoke(IEventListenerDemo *pThis, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
return ITypeInfo_Invoke(g_pTInfoIEventListener, (IDispatch *)pThis, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
}
static HRESULT LoadTypeInfo(REFIID riid, ITypeInfo **pTInfo)
{
HRESULT rc;
ITypeLib *pTypeLib;
rc = LoadRegTypeLib(&LIBID_VirtualBox, 1 /* major */, 0 /* minor */, 0 /* lcid */, &pTypeLib);
if (FAILED(rc))
return rc;
rc = ITypeLib_GetTypeInfoOfGuid(pTypeLib, riid, pTInfo);
/* No longer need access to the type lib, release it. */
ITypeLib_Release(pTypeLib);
return rc;
}
#endif
#ifdef __GNUC__
typedef struct IEventListenerDemoVtblInt
{
ptrdiff_t offset_to_top;
void *typeinfo;
IEventListenerDemoVtbl lpVtbl;
} IEventListenerDemoVtblInt;
static IEventListenerDemoVtblInt g_IEventListenerDemoVtblInt =
{
0, /* offset_to_top */
NULL, /* typeinfo, not vital */
{
IEventListenerDemoImpl_QueryInterface,
IEventListenerDemoImpl_AddRef,
IEventListenerDemoImpl_Release,
#ifdef WIN32
IEventListenerDemoImpl_GetTypeInfoCount,
IEventListenerDemoImpl_GetTypeInfo,
IEventListenerDemoImpl_GetIDsOfNames,
IEventListenerDemoImpl_Invoke,
#endif
IEventListenerDemoImpl_HandleEvent
}
};
#elif defined(_MSC_VER)
typedef struct IEventListenerDemoVtblInt
{
IEventListenerDemoVtbl lpVtbl;
} IEventListenerDemoVtblInt;
static IEventListenerDemoVtblInt g_IEventListenerDemoVtblInt =
{
{
IEventListenerDemoImpl_QueryInterface,
IEventListenerDemoImpl_AddRef,
IEventListenerDemoImpl_Release,
#ifdef WIN32
IEventListenerDemoImpl_GetTypeInfoCount,
IEventListenerDemoImpl_GetTypeInfo,
IEventListenerDemoImpl_GetIDsOfNames,
IEventListenerDemoImpl_Invoke,
#endif
IEventListenerDemoImpl_HandleEvent
}
};
#else
# error Port me!
#endif
/**
* Register active event listener for the selected VM.
*
* @param virtualBox ptr to IVirtualBox object
* @param session ptr to ISession object
* @param id identifies the machine to start
*/
static void registerActiveEventListener(IVirtualBox *virtualBox, ISession *session, BSTR machineId)
{
IConsole *console = NULL;
HRESULT rc;
rc = ISession_get_Console(session, &console);
if ((SUCCEEDED(rc)) && console)
{
IEventSource *es = NULL;
rc = IConsole_get_EventSource(console, &es);
if (SUCCEEDED(rc) && es)
{
static const ULONG interestingEvents[] =
{
VBoxEventType_OnMousePointerShapeChanged,
VBoxEventType_OnMouseCapabilityChanged,
VBoxEventType_OnKeyboardLedsChanged,
VBoxEventType_OnStateChanged,
VBoxEventType_OnAdditionsStateChanged,
VBoxEventType_OnNetworkAdapterChanged,
VBoxEventType_OnSerialPortChanged,
VBoxEventType_OnParallelPortChanged,
VBoxEventType_OnStorageControllerChanged,
VBoxEventType_OnMediumChanged,
VBoxEventType_OnVRDEServerChanged,
VBoxEventType_OnUSBControllerChanged,
VBoxEventType_OnUSBDeviceStateChanged,
VBoxEventType_OnSharedFolderChanged,
VBoxEventType_OnRuntimeError,
VBoxEventType_OnCanShowWindow,
VBoxEventType_OnShowWindow
};
SAFEARRAY *interestingEventsSA = NULL;
IEventListenerDemo *consoleListener = NULL;
/* The VirtualBox API expects enum values as VT_I4, which in the
* future can be hopefully relaxed. */
interestingEventsSA = g_pVBoxFuncs->pfnSafeArrayCreateVector(VT_I4, 0, sizeof(interestingEvents) / sizeof(interestingEvents[0]));
g_pVBoxFuncs->pfnSafeArrayCopyInParamHelper(interestingEventsSA, &interestingEvents, sizeof(interestingEvents));
consoleListener = calloc(1, sizeof(IEventListenerDemo));
if (consoleListener)
{
consoleListener->lpVtbl = &(g_IEventListenerDemoVtblInt.lpVtbl);
#ifdef WIN32
CoCreateFreeThreadedMarshaler((IUnknown *)consoleListener, &consoleListener->pUnkMarshaler);
#endif
IEventListenerDemo_AddRef(consoleListener);
rc = IEventSource_RegisterListener(es, (IEventListener *)consoleListener,
ComSafeArrayAsInParam(interestingEventsSA),
1 /* active */);
if (SUCCEEDED(rc))
{
/* Just wait here for events, no easy way to do this better
* as there's not much to do after this completes. */
printf("Entering event loop, PowerOff the machine to exit or press Ctrl-C to terminate\n");
fflush(stdout);
#ifdef WIN32
SetConsoleCtrlHandler(ctrlCHandler, TRUE);
#else
signal(SIGINT, (void (*)(int))ctrlCHandler);
#endif
while (!g_fStop)
{
g_pVBoxFuncs->pfnProcessEventQueue(250);
}
#ifdef WIN32
SetConsoleCtrlHandler(ctrlCHandler, FALSE);
#else
signal(SIGINT, SIG_DFL);
#endif
}
else
{
printf("Failed to register event listener.\n");
}
IEventSource_UnregisterListener(es, (IEventListener *)consoleListener);
#ifdef WIN32
if (consoleListener->pUnkMarshaler)
IUnknown_Release(consoleListener->pUnkMarshaler);
#endif
IEventListenerDemo_Release(consoleListener);
}
else
{
printf("Failed while allocating memory for console event listener.\n");
}
g_pVBoxFuncs->pfnSafeArrayDestroy(interestingEventsSA);
IEventSource_Release(es);
}
else
{
printf("Failed to get the event source instance.\n");
}
IConsole_Release(console);
}
}
#else /* !USE_ACTIVE_EVENT_LISTENER */
/**
* Register passive event listener for the selected VM.
*
* @param virtualBox ptr to IVirtualBox object
* @param session ptr to ISession object
* @param id identifies the machine to start
*/
static void registerPassiveEventListener(IVirtualBox *virtualBox, ISession *session, BSTR machineId)
{
IConsole *console = NULL;
HRESULT rc;
rc = ISession_get_Console(session, &console);
if ((SUCCEEDED(rc)) && console)
{
IEventSource *es = NULL;
rc = IConsole_get_EventSource(console, &es);
if (SUCCEEDED(rc) && es)
{
static const ULONG interestingEvents[] =
{
VBoxEventType_OnMousePointerShapeChanged,
VBoxEventType_OnMouseCapabilityChanged,
VBoxEventType_OnKeyboardLedsChanged,
VBoxEventType_OnStateChanged,
VBoxEventType_OnAdditionsStateChanged,
VBoxEventType_OnNetworkAdapterChanged,
VBoxEventType_OnSerialPortChanged,
VBoxEventType_OnParallelPortChanged,
VBoxEventType_OnStorageControllerChanged,
VBoxEventType_OnMediumChanged,
VBoxEventType_OnVRDEServerChanged,
VBoxEventType_OnUSBControllerChanged,
VBoxEventType_OnUSBDeviceStateChanged,
VBoxEventType_OnSharedFolderChanged,
VBoxEventType_OnRuntimeError,
VBoxEventType_OnCanShowWindow,
VBoxEventType_OnShowWindow
};
SAFEARRAY *interestingEventsSA = NULL;
IEventListener *consoleListener = NULL;
/* The VirtualBox API expects enum values as VT_I4, which in the
* future can be hopefully relaxed. */
interestingEventsSA = g_pVBoxFuncs->pfnSafeArrayCreateVector(VT_I4, 0, sizeof(interestingEvents) / sizeof(interestingEvents[0]));
g_pVBoxFuncs->pfnSafeArrayCopyInParamHelper(interestingEventsSA, &interestingEvents, sizeof(interestingEvents));
rc = IEventSource_CreateListener(es, &consoleListener);
if (SUCCEEDED(rc) && consoleListener)
{
rc = IEventSource_RegisterListener(es, consoleListener,
ComSafeArrayAsInParam(interestingEventsSA),
0 /* passive */);
if (SUCCEEDED(rc))
{
/* Just wait here for events, no easy way to do this better
* as there's not much to do after this completes. */
printf("Entering event loop, PowerOff the machine to exit or press Ctrl-C to terminate\n");
fflush(stdout);
#ifdef WIN32
SetConsoleCtrlHandler(ctrlCHandler, TRUE);
#else
signal(SIGINT, (void (*)(int))ctrlCHandler);
#endif
while (!g_fStop)
{
IEvent *ev = NULL;
rc = IEventSource_GetEvent(es, consoleListener, 250, &ev);
if (FAILED(rc))
{
printf("Failed getting event: %#x\n", rc);
g_fStop = 1;
continue;
}
/* handle timeouts, resulting in NULL events */
if (!ev)
continue;
rc = EventListenerDemoProcessEvent(ev);
if (FAILED(rc))
{
printf("Failed processing event: %#x\n", rc);
g_fStop = 1;
/* finish processing the event */
}
rc = IEventSource_EventProcessed(es, consoleListener, ev);
if (FAILED(rc))
{
printf("Failed to mark event as processed: %#x\n", rc);
g_fStop = 1;
/* continue with event release */
}
if (ev)
{
IEvent_Release(ev);
ev = NULL;
}
}
#ifdef WIN32
SetConsoleCtrlHandler(ctrlCHandler, FALSE);
#else
signal(SIGINT, SIG_DFL);
#endif
}
else
{
printf("Failed to register event listener.\n");
}
IEventSource_UnregisterListener(es, (IEventListener *)consoleListener);
IEventListener_Release(consoleListener);
}
else
{
printf("Failed to create an event listener instance.\n");
}
g_pVBoxFuncs->pfnSafeArrayDestroy(interestingEventsSA);
IEventSource_Release(es);
}
else
{
printf("Failed to get the event source instance.\n");
}
IConsole_Release(console);
}
}
#endif /* !USE_ACTIVE_EVENT_LISTENER */
/**
* Print detailed error information if available.
* @param pszExecutable string with the executable name
* @param pszErrorMsg string containing the code location specific error message
* @param rc COM/XPCOM result code
*/
static void PrintErrorInfo(const char *pszExecutable, const char *pszErrorMsg, HRESULT rc)
{
IErrorInfo *ex;
HRESULT rc2 = S_OK;
fprintf(stderr, "%s: %s (rc=%#010x)\n", pszExecutable, pszErrorMsg, (unsigned)rc);
rc2 = g_pVBoxFuncs->pfnGetException(&ex);
if (SUCCEEDED(rc2))
{
IVirtualBoxErrorInfo *ei;
rc2 = IErrorInfo_QueryInterface(ex, &IID_IVirtualBoxErrorInfo, (void **)&ei);
if (FAILED(rc2))
ei = NULL;
if (ei)
{
/* got extended error info, maybe multiple infos */
do
{
LONG resultCode = S_OK;
BSTR componentUtf16 = NULL;
char *component = NULL;
BSTR textUtf16 = NULL;
char *text = NULL;
IVirtualBoxErrorInfo *ei_next = NULL;
fprintf(stderr, "Extended error info (IVirtualBoxErrorInfo):\n");
IVirtualBoxErrorInfo_get_ResultCode(ei, &resultCode);
fprintf(stderr, " resultCode=%#010x\n", (unsigned)resultCode);
IVirtualBoxErrorInfo_get_Component(ei, &componentUtf16);
g_pVBoxFuncs->pfnUtf16ToUtf8(componentUtf16, &component);
g_pVBoxFuncs->pfnComUnallocString(componentUtf16);
fprintf(stderr, " component=%s\n", component);
g_pVBoxFuncs->pfnUtf8Free(component);
IVirtualBoxErrorInfo_get_Text(ei, &textUtf16);
g_pVBoxFuncs->pfnUtf16ToUtf8(textUtf16, &text);
g_pVBoxFuncs->pfnComUnallocString(textUtf16);
fprintf(stderr, " text=%s\n", text);
g_pVBoxFuncs->pfnUtf8Free(text);
rc2 = IVirtualBoxErrorInfo_get_Next(ei, &ei_next);
if (FAILED(rc2))
ei_next = NULL;
IVirtualBoxErrorInfo_Release(ei);
ei = ei_next;
}
while (ei);
}
IErrorInfo_Release(ex);
g_pVBoxFuncs->pfnClearException();
}
}
/**
* Start a VM.
*
* @param argv0 executable name
* @param virtualBox ptr to IVirtualBox object
* @param session ptr to ISession object
* @param id identifies the machine to start
*/
static void startVM(const char *argv0, IVirtualBox *virtualBox, ISession *session, BSTR id)
{
HRESULT rc;
IMachine *machine = NULL;
IProgress *progress = NULL;
BSTR env = NULL;
BSTR sessionType;
SAFEARRAY *groupsSA = g_pVBoxFuncs->pfnSafeArrayOutParamAlloc();
rc = IVirtualBox_FindMachine(virtualBox, id, &machine);
if (FAILED(rc) || !machine)
{
PrintErrorInfo(argv0, "Error: Couldn't get the Machine reference", rc);
return;
}
rc = IMachine_get_Groups(machine, ComSafeArrayAsOutTypeParam(groupsSA, BSTR));
if (SUCCEEDED(rc))
{
BSTR *groups = NULL;
ULONG cbGroups = 0;
ULONG i, cGroups;
g_pVBoxFuncs->pfnSafeArrayCopyOutParamHelper((void **)&groups, &cbGroups, VT_BSTR, groupsSA);
cGroups = cbGroups / sizeof(groups[0]);
for (i = 0; i < cGroups; ++i)
{
/* Note that the use of %S might be tempting, but it is not
* available on all platforms, and even where it is usable it
* may depend on correct compiler options to make wchar_t a
* 16 bit number. So better play safe and use UTF-8. */
char *group;
g_pVBoxFuncs->pfnUtf16ToUtf8(groups[i], &group);
printf("Groups[%d]: %s\n", i, group);
g_pVBoxFuncs->pfnUtf8Free(group);
}
for (i = 0; i < cGroups; ++i)
g_pVBoxFuncs->pfnComUnallocString(groups[i]);
g_pVBoxFuncs->pfnArrayOutFree(groups);
}
g_pVBoxFuncs->pfnUtf8ToUtf16("gui", &sessionType);
rc = IMachine_LaunchVMProcess(machine, session, sessionType, env, &progress);
g_pVBoxFuncs->pfnUtf16Free(sessionType);
if (SUCCEEDED(rc))
{
BOOL completed;
LONG resultCode;
printf("Waiting for the remote session to open...\n");
IProgress_WaitForCompletion(progress, -1);
rc = IProgress_get_Completed(progress, &completed);
if (FAILED(rc))
fprintf(stderr, "Error: GetCompleted status failed\n");
IProgress_get_ResultCode(progress, &resultCode);
if (FAILED(resultCode))
{
IVirtualBoxErrorInfo *errorInfo;
BSTR textUtf16;
char *text;
IProgress_get_ErrorInfo(progress, &errorInfo);
IVirtualBoxErrorInfo_get_Text(errorInfo, &textUtf16);
g_pVBoxFuncs->pfnUtf16ToUtf8(textUtf16, &text);
printf("Error: %s\n", text);
g_pVBoxFuncs->pfnComUnallocString(textUtf16);
g_pVBoxFuncs->pfnUtf8Free(text);
IVirtualBoxErrorInfo_Release(errorInfo);
}
else
{
fprintf(stderr, "VM process has been successfully started\n");
/* Kick off the event listener demo part, which is quite separate.
* Ignore it if you need a more basic sample. */
#ifdef USE_ACTIVE_EVENT_LISTENER
registerActiveEventListener(virtualBox, session, id);
#else /* !USE_ACTIVE_EVENT_LISTENER */
registerPassiveEventListener(virtualBox, session, id);
#endif /* !USE_ACTIVE_EVENT_LISTENER */
}
IProgress_Release(progress);
}
else
PrintErrorInfo(argv0, "Error: LaunchVMProcess failed", rc);
/* It's important to always release resources. */
IMachine_Release(machine);
}
/**
* List the registered VMs.
*
* @param argv0 executable name
* @param virtualBox ptr to IVirtualBox object
* @param session ptr to ISession object
*/
static void listVMs(const char *argv0, IVirtualBox *virtualBox, ISession *session)
{
HRESULT rc;
SAFEARRAY *machinesSA = g_pVBoxFuncs->pfnSafeArrayOutParamAlloc();
IMachine **machines = NULL;
ULONG machineCnt = 0;
ULONG i;
unsigned start_id;
/*
* Get the list of all registered VMs.
*/
rc = IVirtualBox_get_Machines(virtualBox, ComSafeArrayAsOutIfaceParam(machinesSA, IMachine *));
if (FAILED(rc))
{
PrintErrorInfo(argv0, "could not get list of machines", rc);
return;
}
/*
* Extract interface pointers from machinesSA, and update the reference
* counter of each object, as destroying machinesSA would call Release.
*/
g_pVBoxFuncs->pfnSafeArrayCopyOutIfaceParamHelper((IUnknown ***)&machines, &machineCnt, machinesSA);
g_pVBoxFuncs->pfnSafeArrayDestroy(machinesSA);
if (!machineCnt)
{
g_pVBoxFuncs->pfnArrayOutFree(machines);
printf("\tNo VMs\n");
return;
}
printf("VM List:\n\n");
/*
* Iterate through the collection.
*/
for (i = 0; i < machineCnt; ++i)
{
IMachine *machine = machines[i];
BOOL isAccessible = FALSE;
printf("\tMachine #%u\n", (unsigned)i);
if (!machine)
{
printf("\t(skipped, NULL)\n");
continue;
}
IMachine_get_Accessible(machine, &isAccessible);
if (isAccessible)
{
BSTR machineNameUtf16;
char *machineName;
IMachine_get_Name(machine, &machineNameUtf16);
g_pVBoxFuncs->pfnUtf16ToUtf8(machineNameUtf16,&machineName);
g_pVBoxFuncs->pfnComUnallocString(machineNameUtf16);
printf("\tName: %s\n", machineName);
g_pVBoxFuncs->pfnUtf8Free(machineName);
}
else
{
printf("\tName: <inaccessible>\n");
}
{
BSTR uuidUtf16;
char *uuidUtf8;
IMachine_get_Id(machine, &uuidUtf16);
g_pVBoxFuncs->pfnUtf16ToUtf8(uuidUtf16, &uuidUtf8);
g_pVBoxFuncs->pfnComUnallocString(uuidUtf16);
printf("\tUUID: %s\n", uuidUtf8);
g_pVBoxFuncs->pfnUtf8Free(uuidUtf8);
}
if (isAccessible)
{
{
BSTR configFileUtf16;
char *configFileUtf8;
IMachine_get_SettingsFilePath(machine, &configFileUtf16);
g_pVBoxFuncs->pfnUtf16ToUtf8(configFileUtf16, &configFileUtf8);
g_pVBoxFuncs->pfnComUnallocString(configFileUtf16);
printf("\tConfig file: %s\n", configFileUtf8);
g_pVBoxFuncs->pfnUtf8Free(configFileUtf8);
}
{
ULONG memorySize;
IMachine_get_MemorySize(machine, &memorySize);
printf("\tMemory size: %uMB\n", memorySize);
}
{
BSTR typeId;
BSTR osNameUtf16;
char *osName;
IGuestOSType *osType = NULL;
IMachine_get_OSTypeId(machine, &typeId);
IVirtualBox_GetGuestOSType(virtualBox, typeId, &osType);
g_pVBoxFuncs->pfnComUnallocString(typeId);
IGuestOSType_get_Description(osType, &osNameUtf16);
g_pVBoxFuncs->pfnUtf16ToUtf8(osNameUtf16,&osName);
g_pVBoxFuncs->pfnComUnallocString(osNameUtf16);
printf("\tGuest OS: %s\n\n", osName);
g_pVBoxFuncs->pfnUtf8Free(osName);
IGuestOSType_Release(osType);
}
}
}
/*
* Let the user chose a machine to start.
*/
printf("Type Machine# to start (0 - %u) or 'quit' to do nothing: ",
(unsigned)(machineCnt - 1));
fflush(stdout);
if (scanf("%u", &start_id) == 1 && start_id < machineCnt)
{
IMachine *machine = machines[start_id];
if (machine)
{
BSTR uuidUtf16 = NULL;
IMachine_get_Id(machine, &uuidUtf16);
startVM(argv0, virtualBox, session, uuidUtf16);
g_pVBoxFuncs->pfnComUnallocString(uuidUtf16);
}
}
/*
* Don't forget to release the objects in the array.
*/
for (i = 0; i < machineCnt; ++i)
{
IMachine *machine = machines[i];
if (machine)
{
IMachine_Release(machine);
}
}
g_pVBoxFuncs->pfnArrayOutFree(machines);
}
/* Main - Start the ball rolling. */
int main(int argc, char **argv)
{
IVirtualBoxClient *vboxclient = NULL;
IVirtualBox *vbox = NULL;
ISession *session = NULL;
ULONG revision = 0;
BSTR versionUtf16 = NULL;
BSTR homefolderUtf16 = NULL;
HRESULT rc; /* Result code of various function (method) calls. */
printf("Starting main()\n");
if (VBoxCGlueInit())
{
fprintf(stderr, "%s: FATAL: VBoxCGlueInit failed: %s\n",
argv[0], g_szVBoxErrMsg);
return EXIT_FAILURE;
}
{
unsigned ver = g_pVBoxFuncs->pfnGetVersion();
printf("VirtualBox version: %u.%u.%u\n", ver / 1000000, ver / 1000 % 1000, ver % 1000);
ver = g_pVBoxFuncs->pfnGetAPIVersion();
printf("VirtualBox API version: %u.%u\n", ver / 1000, ver % 1000);
}
g_pVBoxFuncs->pfnClientInitialize(NULL, &vboxclient);
if (!vboxclient)
{
fprintf(stderr, "%s: FATAL: could not get VirtualBoxClient reference\n", argv[0]);
return EXIT_FAILURE;
}
printf("----------------------------------------------------\n");
rc = IVirtualBoxClient_get_VirtualBox(vboxclient, &vbox);
if (FAILED(rc) || !vbox)
{
PrintErrorInfo(argv[0], "FATAL: could not get VirtualBox reference", rc);
return EXIT_FAILURE;
}
rc = IVirtualBoxClient_get_Session(vboxclient, &session);
if (FAILED(rc) || !session)
{
PrintErrorInfo(argv[0], "FATAL: could not get Session reference", rc);
return EXIT_FAILURE;
}
#ifdef USE_ACTIVE_EVENT_LISTENER
# ifdef WIN32
rc = LoadTypeInfo(&IID_IEventListener, &g_pTInfoIEventListener);
if (FAILED(rc) || !g_pTInfoIEventListener)
{
PrintErrorInfo(argv[0], "FATAL: could not get type information for IEventListener", rc);
return EXIT_FAILURE;
}
# endif /* WIN32 */
#endif /* USE_ACTIVE_EVENT_LISTENER */
/*
* Now ask for revision, version and home folder information of
* this vbox. Were not using fancy macros here so it
* remains easy to see how we access C++'s vtable.
*/
/* 1. Revision */
rc = IVirtualBox_get_Revision(vbox, &revision);
if (SUCCEEDED(rc))
printf("\tRevision: %u\n", revision);
else
PrintErrorInfo(argv[0], "GetRevision() failed", rc);
/* 2. Version */
rc = IVirtualBox_get_Version(vbox, &versionUtf16);
if (SUCCEEDED(rc))
{
char *version = NULL;
g_pVBoxFuncs->pfnUtf16ToUtf8(versionUtf16, &version);
printf("\tVersion: %s\n", version);
g_pVBoxFuncs->pfnUtf8Free(version);
g_pVBoxFuncs->pfnComUnallocString(versionUtf16);
}
else
PrintErrorInfo(argv[0], "GetVersion() failed", rc);
/* 3. Home Folder */
rc = IVirtualBox_get_HomeFolder(vbox, &homefolderUtf16);
if (SUCCEEDED(rc))
{
char *homefolder = NULL;
g_pVBoxFuncs->pfnUtf16ToUtf8(homefolderUtf16, &homefolder);
printf("\tHomeFolder: %s\n", homefolder);
g_pVBoxFuncs->pfnUtf8Free(homefolder);
g_pVBoxFuncs->pfnComUnallocString(homefolderUtf16);
}
else
PrintErrorInfo(argv[0], "GetHomeFolder() failed", rc);
listVMs(argv[0], vbox, session);
ISession_UnlockMachine(session);
printf("----------------------------------------------------\n");
/*
* Do as mom told us: always clean up after yourself.
*/
#ifdef USE_ACTIVE_EVENT_LISTENER
# ifdef WIN32
if (g_pTInfoIEventListener)
{
ITypeInfo_Release(g_pTInfoIEventListener);
g_pTInfoIEventListener = NULL;
}
# endif /* WIN32 */
#endif /* USE_ACTIVE_EVENT_LISTENER */
if (session)
{
ISession_Release(session);
session = NULL;
}
if (vbox)
{
IVirtualBox_Release(vbox);
vbox = NULL;
}
if (vboxclient)
{
IVirtualBoxClient_Release(vboxclient);
vboxclient = NULL;
}
g_pVBoxFuncs->pfnClientUninitialize();
VBoxCGlueTerm();
printf("Finished main()\n");
return 0;
}
/* vim: set ts=4 sw=4 et: */