diff options
Diffstat (limited to 'src/VBox/Main/cbinding/tstCAPIGlue.c')
-rw-r--r-- | src/VBox/Main/cbinding/tstCAPIGlue.c | 1145 |
1 files changed, 1145 insertions, 0 deletions
diff --git a/src/VBox/Main/cbinding/tstCAPIGlue.c b/src/VBox/Main/cbinding/tstCAPIGlue.c new file mode 100644 index 00000000..eb764d58 --- /dev/null +++ b/src/VBox/Main/cbinding/tstCAPIGlue.c @@ -0,0 +1,1145 @@ +/* $Id: tstCAPIGlue.c $ */ +/** @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-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/** @todo + * Our appologies for the 256+ missing return code checks in this sample file. + * + * We strongly recomment users of the VBoxCAPI to check all return codes! + */ + + +/********************************************************************************************************************************* +* 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 +#ifdef IPRT_INCLUDED_cdefs_h +# error "not supposed to involve any IPRT or VBox headers here." +#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_AbortedSaved: return "Aborted-Saved"; + 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_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). + */ +#ifdef WIN32 +static BOOL VBOX_WINAPI ctrlCHandler(DWORD iInfo) +{ + (void)iInfo; + g_fStop = 1; + return TRUE; +} +#else +static void ctrlCHandler(int iInfo) +{ + (void)iInfo; + g_fStop = 1; +} +#endif + +/** + * 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", (unsigned)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", (unsigned)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", (unsigned)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 + || state == MachineState_AbortedSaved + ) + 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 + */ +static void registerActiveEventListener(IVirtualBox *virtualBox, ISession *session) +{ + 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 s_auInterestingEvents[] = + { + 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(s_auInterestingEvents) + / sizeof(s_auInterestingEvents[0])); + g_pVBoxFuncs->pfnSafeArrayCopyInParamHelper(interestingEventsSA, &s_auInterestingEvents, + sizeof(s_auInterestingEvents)); + + 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 + */ +static void registerPassiveEventListener(ISession *session) +{ + 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 s_auInterestingEvents[] = + { + 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(s_auInterestingEvents) + / sizeof(s_auInterestingEvents[0])); + g_pVBoxFuncs->pfnSafeArrayCopyInParamHelper(interestingEventsSA, &s_auInterestingEvents, + sizeof(s_auInterestingEvents)); + + 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, ctrlCHandler); +#endif + + while (!g_fStop) + { + IEvent *ev = NULL; + rc = IEventSource_GetEvent(es, consoleListener, 250, &ev); + if (FAILED(rc)) + { + printf("Failed getting event: %#x\n", (unsigned)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", (unsigned)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", (unsigned)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; + fprintf(stderr, "%s: %s (rc=%#010x)\n", pszExecutable, pszErrorMsg, (unsigned)rc); + rc2 = g_pVBoxFuncs->pfnGetException(&ex); + if (SUCCEEDED(rc2) && ex) + { + IVirtualBoxErrorInfo *ei; + rc2 = IErrorInfo_QueryInterface(ex, &IID_IVirtualBoxErrorInfo, (void **)&ei); + if (SUCCEEDED(rc2) && ei != NULL) + { + /* 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; + SAFEARRAY *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); + g_pVBoxFuncs->pfnSafeArrayDestroy(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[%u]: %s\n", (unsigned)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, ComSafeArrayAsInParam(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); +#else + registerPassiveEventListener(session); +#endif + } + 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", (unsigned)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. */ + (void)argc; + + 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", (unsigned)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: */ |