VirtualBox

source: vbox/trunk/src/VBox/Main/cbinding/tstCAPIGlue.c@ 91363

Last change on this file since 91363 was 91363, checked in by vboxsync, 3 years ago

FE/VBoxSDL+VirtualBox,Main/Console+Machine+VirtualBox.xidl: VMs which
crash while restoring from the 'Saved' state shouldn't lose their saved
state file. bugref:1484

A new machine state named 'AbortedSaved' has been added which a VM will
enter if it crashes when restoring from the 'Saved' state before the
'Running' state has been reached. A VM in the 'AbortedSaved' machine
state will have its saved state file preserved so that the VM can still be
restored once the cause of the failure to powerUp() and reach the
'Running' state has been resolved.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.3 KB
Line 
1/* $Id: tstCAPIGlue.c 91363 2021-09-24 13:08:32Z vboxsync $ */
2/** @file tstCAPIGlue.c
3 * Demonstrator program to illustrate use of C bindings of Main API.
4 *
5 * It has sample code showing how to retrieve all available error information,
6 * and how to handle active (event delivery through callbacks) or passive
7 * (event delivery through a polling mechanism) event listeners.
8 */
9
10/*
11 * Copyright (C) 2009-2020 Oracle Corporation
12 *
13 * This file is part of VirtualBox Open Source Edition (OSE), as
14 * available from http://www.virtualbox.org. This file is free software;
15 * you can redistribute it and/or modify it under the terms of the GNU
16 * General Public License (GPL) as published by the Free Software
17 * Foundation, in version 2 as it comes in the "COPYING" file of the
18 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20 */
21
22
23/** @todo
24 * Our appologies for the 256+ missing return code checks in this sample file.
25 *
26 * We strongly recomment users of the VBoxCAPI to check all return codes!
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#include "VBoxCAPIGlue.h"
34#include <stdio.h>
35#include <string.h>
36#include <stdlib.h>
37#ifndef WIN32
38# include <signal.h>
39# include <unistd.h>
40# include <sys/poll.h>
41#endif
42#ifdef IPRT_INCLUDED_cdefs_h
43# error "not supposed to involve any IPRT or VBox headers here."
44#endif
45
46/**
47 * Select between active event listener (defined) and passive event listener
48 * (undefined). The active event listener case needs much more code, and
49 * additionally requires a lot more platform dependent code.
50 */
51#undef USE_ACTIVE_EVENT_LISTENER
52
53
54/*********************************************************************************************************************************
55* Global Variables *
56*********************************************************************************************************************************/
57/** Set by Ctrl+C handler. */
58static volatile int g_fStop = 0;
59
60#ifdef USE_ACTIVE_EVENT_LISTENER
61# ifdef WIN32
62/** The COM type information for IEventListener, for implementing IDispatch. */
63static ITypeInfo *g_pTInfoIEventListener = NULL;
64# endif /* WIN32 */
65#endif /* USE_ACTIVE_EVENT_LISTENER */
66
67static const char *GetStateName(MachineState_T machineState)
68{
69 switch (machineState)
70 {
71 case MachineState_Null: return "<null>";
72 case MachineState_PoweredOff: return "PoweredOff";
73 case MachineState_Saved: return "Saved";
74 case MachineState_Teleported: return "Teleported";
75 case MachineState_Aborted: return "Aborted";
76 case MachineState_AbortedSaved: return "Aborted-Saved";
77 case MachineState_Running: return "Running";
78 case MachineState_Paused: return "Paused";
79 case MachineState_Stuck: return "Stuck";
80 case MachineState_Teleporting: return "Teleporting";
81 case MachineState_LiveSnapshotting: return "LiveSnapshotting";
82 case MachineState_Starting: return "Starting";
83 case MachineState_Stopping: return "Stopping";
84 case MachineState_Saving: return "Saving";
85 case MachineState_Restoring: return "Restoring";
86 case MachineState_TeleportingPausedVM: return "TeleportingPausedVM";
87 case MachineState_TeleportingIn: return "TeleportingIn";
88 case MachineState_DeletingSnapshotOnline: return "DeletingSnapshotOnline";
89 case MachineState_DeletingSnapshotPaused: return "DeletingSnapshotPaused";
90 case MachineState_RestoringSnapshot: return "RestoringSnapshot";
91 case MachineState_DeletingSnapshot: return "DeletingSnapshot";
92 case MachineState_SettingUp: return "SettingUp";
93 default: return "no idea";
94 }
95}
96
97/**
98 * Ctrl+C handler, terminate event listener.
99 *
100 * Remember that most function calls are not allowed in this context (including
101 * printf!), so make sure that this does as little as possible.
102 *
103 * @param iInfo Platform dependent detail info (ignored).
104 */
105#ifdef WIN32
106static BOOL VBOX_WINAPI ctrlCHandler(DWORD iInfo)
107{
108 (void)iInfo;
109 g_fStop = 1;
110 return TRUE;
111}
112#else
113static void ctrlCHandler(int iInfo)
114{
115 (void)iInfo;
116 g_fStop = 1;
117}
118#endif
119
120/**
121 * Sample event processing function, dumping some event information.
122 * Shared between active and passive event demo, to highlight that this part
123 * is identical between the two.
124 */
125static HRESULT EventListenerDemoProcessEvent(IEvent *event)
126{
127 VBoxEventType_T evType;
128 HRESULT rc;
129
130 if (!event)
131 {
132 printf("event null\n");
133 return S_OK;
134 }
135
136 evType = VBoxEventType_Invalid;
137 rc = IEvent_get_Type(event, &evType);
138 if (FAILED(rc))
139 {
140 printf("cannot get event type, rc=%#x\n", (unsigned)rc);
141 return S_OK;
142 }
143
144 switch (evType)
145 {
146 case VBoxEventType_OnMousePointerShapeChanged:
147 printf("OnMousePointerShapeChanged\n");
148 break;
149
150 case VBoxEventType_OnMouseCapabilityChanged:
151 printf("OnMouseCapabilityChanged\n");
152 break;
153
154 case VBoxEventType_OnKeyboardLedsChanged:
155 printf("OnMouseCapabilityChanged\n");
156 break;
157
158 case VBoxEventType_OnStateChanged:
159 {
160 IStateChangedEvent *ev = NULL;
161 enum MachineState state;
162 rc = IEvent_QueryInterface(event, &IID_IStateChangedEvent, (void **)&ev);
163 if (FAILED(rc))
164 {
165 printf("cannot get StateChangedEvent interface, rc=%#x\n", (unsigned)rc);
166 return S_OK;
167 }
168 if (!ev)
169 {
170 printf("StateChangedEvent reference null\n");
171 return S_OK;
172 }
173 rc = IStateChangedEvent_get_State(ev, &state);
174 if (FAILED(rc))
175 printf("warning: cannot get state, rc=%#x\n", (unsigned)rc);
176 IStateChangedEvent_Release(ev);
177 printf("OnStateChanged: %s\n", GetStateName(state));
178
179 fflush(stdout);
180 if ( state == MachineState_PoweredOff
181 || state == MachineState_Saved
182 || state == MachineState_Teleported
183 || state == MachineState_Aborted
184 || state == MachineState_AbortedSaved
185 )
186 g_fStop = 1;
187 break;
188 }
189
190 case VBoxEventType_OnAdditionsStateChanged:
191 printf("OnAdditionsStateChanged\n");
192 break;
193
194 case VBoxEventType_OnNetworkAdapterChanged:
195 printf("OnNetworkAdapterChanged\n");
196 break;
197
198 case VBoxEventType_OnSerialPortChanged:
199 printf("OnSerialPortChanged\n");
200 break;
201
202 case VBoxEventType_OnParallelPortChanged:
203 printf("OnParallelPortChanged\n");
204 break;
205
206 case VBoxEventType_OnStorageControllerChanged:
207 printf("OnStorageControllerChanged\n");
208 break;
209
210 case VBoxEventType_OnMediumChanged:
211 printf("OnMediumChanged\n");
212 break;
213
214 case VBoxEventType_OnVRDEServerChanged:
215 printf("OnVRDEServerChanged\n");
216 break;
217
218 case VBoxEventType_OnUSBControllerChanged:
219 printf("OnUSBControllerChanged\n");
220 break;
221
222 case VBoxEventType_OnUSBDeviceStateChanged:
223 printf("OnUSBDeviceStateChanged\n");
224 break;
225
226 case VBoxEventType_OnSharedFolderChanged:
227 printf("OnSharedFolderChanged\n");
228 break;
229
230 case VBoxEventType_OnRuntimeError:
231 printf("OnRuntimeError\n");
232 break;
233
234 case VBoxEventType_OnCanShowWindow:
235 printf("OnCanShowWindow\n");
236 break;
237 case VBoxEventType_OnShowWindow:
238 printf("OnShowWindow\n");
239 break;
240
241 default:
242 printf("unknown event: %d\n", evType);
243 }
244
245 return S_OK;
246}
247
248#ifdef USE_ACTIVE_EVENT_LISTENER
249
250struct IEventListenerDemo;
251typedef struct IEventListenerDemo IEventListenerDemo;
252
253typedef struct IEventListenerDemoVtbl
254{
255 HRESULT (*QueryInterface)(IEventListenerDemo *pThis, REFIID riid, void **ppvObject);
256 ULONG (*AddRef)(IEventListenerDemo *pThis);
257 ULONG (*Release)(IEventListenerDemo *pThis);
258#ifdef WIN32
259 HRESULT (*GetTypeInfoCount)(IEventListenerDemo *pThis, UINT *pctinfo);
260 HRESULT (*GetTypeInfo)(IEventListenerDemo *pThis, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
261 HRESULT (*GetIDsOfNames)(IEventListenerDemo *pThis, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
262 HRESULT (*Invoke)(IEventListenerDemo *pThis, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr);
263#endif
264 HRESULT (*HandleEvent)(IEventListenerDemo *pThis, IEvent *aEvent);
265} IEventListenerDemoVtbl;
266
267typedef struct IEventListenerDemo
268{
269 struct IEventListenerDemoVtbl *lpVtbl;
270
271 int cRef;
272
273#ifdef WIN32
274 /* Active event delivery needs a free threaded marshaler, as the default
275 * proxy marshaling cannot deal correctly with this case. */
276 IUnknown *pUnkMarshaler;
277#endif
278} IEventListenerDemo;
279
280/* Defines for easily calling IEventListenerDemo functions. */
281
282/* IUnknown functions. */
283#define IEventListenerDemo_QueryInterface(This,riid,ppvObject) \
284 ( (This)->lpVtbl->QueryInterface(This,riid,ppvObject) )
285
286#define IEventListenerDemo_AddRef(This) \
287 ( (This)->lpVtbl->AddRef(This) )
288
289#define IEventListenerDemo_Release(This) \
290 ( (This)->lpVtbl->Release(This) )
291
292#ifdef WIN32
293/* IDispatch functions. */
294#define IEventListenerDemo_GetTypeInfoCount(This,pctinfo) \
295 ( (This)->lpVtbl->GetTypeInfoCount(This,pctinfo) )
296
297#define IEventListenerDemo_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \
298 ( (This)->lpVtbl->GetTypeInfo(This,iTInfo,lcid,ppTInfo) )
299
300#define IEventListenerDemo_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \
301 ( (This)->lpVtbl->GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) )
302
303#define IEventListenerDemo_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \
304 ( (This)->lpVtbl->Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) )
305#endif
306
307/* IEventListener functions. */
308#define IEventListenerDemo_HandleEvent(This,aEvent) \
309 ( (This)->lpVtbl->HandleEvent(This,aEvent) )
310
311
312/**
313 * Event handler function, for active event processing.
314 */
315static HRESULT IEventListenerDemoImpl_HandleEvent(IEventListenerDemo *pThis, IEvent *event)
316{
317 return EventListenerDemoProcessEvent(event);
318}
319
320static HRESULT IEventListenerDemoImpl_QueryInterface(IEventListenerDemo *pThis, const IID *iid, void **resultp)
321{
322 /* match iid */
323 if ( !memcmp(iid, &IID_IEventListener, sizeof(IID))
324 || !memcmp(iid, &IID_IDispatch, sizeof(IID))
325 || !memcmp(iid, &IID_IUnknown, sizeof(IID)))
326 {
327 IEventListenerDemo_AddRef(pThis);
328 *resultp = pThis;
329 return S_OK;
330 }
331#ifdef WIN32
332 if (!memcmp(iid, &IID_IMarshal, sizeof(IID)))
333 return IUnknown_QueryInterface(pThis->pUnkMarshaler, iid, resultp);
334#endif
335
336 return E_NOINTERFACE;
337}
338
339static HRESULT IEventListenerDemoImpl_AddRef(IEventListenerDemo *pThis)
340{
341 return ++(pThis->cRef);
342}
343
344static HRESULT IEventListenerDemoImpl_Release(IEventListenerDemo *pThis)
345{
346 HRESULT c;
347
348 c = --(pThis->cRef);
349 if (!c)
350 free(pThis);
351 return c;
352}
353
354#ifdef WIN32
355static HRESULT IEventListenerDemoImpl_GetTypeInfoCount(IEventListenerDemo *pThis, UINT *pctinfo)
356{
357 if (!pctinfo)
358 return E_POINTER;
359 *pctinfo = 1;
360 return S_OK;
361}
362
363static HRESULT IEventListenerDemoImpl_GetTypeInfo(IEventListenerDemo *pThis, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
364{
365 if (!ppTInfo)
366 return E_POINTER;
367 ITypeInfo_AddRef(g_pTInfoIEventListener);
368 *ppTInfo = g_pTInfoIEventListener;
369 return S_OK;
370}
371
372static HRESULT IEventListenerDemoImpl_GetIDsOfNames(IEventListenerDemo *pThis, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
373{
374 return ITypeInfo_GetIDsOfNames(g_pTInfoIEventListener, rgszNames, cNames, rgDispId);
375}
376
377static HRESULT IEventListenerDemoImpl_Invoke(IEventListenerDemo *pThis, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
378{
379 return ITypeInfo_Invoke(g_pTInfoIEventListener, (IDispatch *)pThis, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
380}
381
382static HRESULT LoadTypeInfo(REFIID riid, ITypeInfo **pTInfo)
383{
384 HRESULT rc;
385 ITypeLib *pTypeLib;
386 rc = LoadRegTypeLib(&LIBID_VirtualBox, 1 /* major */, 0 /* minor */, 0 /* lcid */, &pTypeLib);
387 if (FAILED(rc))
388 return rc;
389 rc = ITypeLib_GetTypeInfoOfGuid(pTypeLib, riid, pTInfo);
390
391 /* No longer need access to the type lib, release it. */
392 ITypeLib_Release(pTypeLib);
393
394 return rc;
395}
396#endif
397
398#ifdef __GNUC__
399typedef struct IEventListenerDemoVtblInt
400{
401 ptrdiff_t offset_to_top;
402 void *typeinfo;
403 IEventListenerDemoVtbl lpVtbl;
404} IEventListenerDemoVtblInt;
405
406static IEventListenerDemoVtblInt g_IEventListenerDemoVtblInt =
407{
408 0, /* offset_to_top */
409 NULL, /* typeinfo, not vital */
410 {
411 IEventListenerDemoImpl_QueryInterface,
412 IEventListenerDemoImpl_AddRef,
413 IEventListenerDemoImpl_Release,
414#ifdef WIN32
415 IEventListenerDemoImpl_GetTypeInfoCount,
416 IEventListenerDemoImpl_GetTypeInfo,
417 IEventListenerDemoImpl_GetIDsOfNames,
418 IEventListenerDemoImpl_Invoke,
419#endif
420 IEventListenerDemoImpl_HandleEvent
421 }
422};
423#elif defined(_MSC_VER)
424typedef struct IEventListenerDemoVtblInt
425{
426 IEventListenerDemoVtbl lpVtbl;
427} IEventListenerDemoVtblInt;
428
429static IEventListenerDemoVtblInt g_IEventListenerDemoVtblInt =
430{
431 {
432 IEventListenerDemoImpl_QueryInterface,
433 IEventListenerDemoImpl_AddRef,
434 IEventListenerDemoImpl_Release,
435#ifdef WIN32
436 IEventListenerDemoImpl_GetTypeInfoCount,
437 IEventListenerDemoImpl_GetTypeInfo,
438 IEventListenerDemoImpl_GetIDsOfNames,
439 IEventListenerDemoImpl_Invoke,
440#endif
441 IEventListenerDemoImpl_HandleEvent
442 }
443};
444#else
445# error Port me!
446#endif
447
448/**
449 * Register active event listener for the selected VM.
450 *
451 * @param virtualBox ptr to IVirtualBox object
452 * @param session ptr to ISession object
453 */
454static void registerActiveEventListener(IVirtualBox *virtualBox, ISession *session)
455{
456 IConsole *console = NULL;
457 HRESULT rc;
458
459 rc = ISession_get_Console(session, &console);
460 if ((SUCCEEDED(rc)) && console)
461 {
462 IEventSource *es = NULL;
463 rc = IConsole_get_EventSource(console, &es);
464 if (SUCCEEDED(rc) && es)
465 {
466 static const ULONG s_auInterestingEvents[] =
467 {
468 VBoxEventType_OnMousePointerShapeChanged,
469 VBoxEventType_OnMouseCapabilityChanged,
470 VBoxEventType_OnKeyboardLedsChanged,
471 VBoxEventType_OnStateChanged,
472 VBoxEventType_OnAdditionsStateChanged,
473 VBoxEventType_OnNetworkAdapterChanged,
474 VBoxEventType_OnSerialPortChanged,
475 VBoxEventType_OnParallelPortChanged,
476 VBoxEventType_OnStorageControllerChanged,
477 VBoxEventType_OnMediumChanged,
478 VBoxEventType_OnVRDEServerChanged,
479 VBoxEventType_OnUSBControllerChanged,
480 VBoxEventType_OnUSBDeviceStateChanged,
481 VBoxEventType_OnSharedFolderChanged,
482 VBoxEventType_OnRuntimeError,
483 VBoxEventType_OnCanShowWindow,
484 VBoxEventType_OnShowWindow
485 };
486 SAFEARRAY *interestingEventsSA = NULL;
487 IEventListenerDemo *consoleListener = NULL;
488
489 /* The VirtualBox API expects enum values as VT_I4, which in the
490 * future can be hopefully relaxed. */
491 interestingEventsSA = g_pVBoxFuncs->pfnSafeArrayCreateVector(VT_I4, 0,
492 sizeof(s_auInterestingEvents)
493 / sizeof(s_auInterestingEvents[0]));
494 g_pVBoxFuncs->pfnSafeArrayCopyInParamHelper(interestingEventsSA, &s_auInterestingEvents,
495 sizeof(s_auInterestingEvents));
496
497 consoleListener = calloc(1, sizeof(IEventListenerDemo));
498 if (consoleListener)
499 {
500 consoleListener->lpVtbl = &(g_IEventListenerDemoVtblInt.lpVtbl);
501#ifdef WIN32
502 CoCreateFreeThreadedMarshaler((IUnknown *)consoleListener, &consoleListener->pUnkMarshaler);
503#endif
504 IEventListenerDemo_AddRef(consoleListener);
505
506 rc = IEventSource_RegisterListener(es, (IEventListener *)consoleListener,
507 ComSafeArrayAsInParam(interestingEventsSA),
508 1 /* active */);
509 if (SUCCEEDED(rc))
510 {
511 /* Just wait here for events, no easy way to do this better
512 * as there's not much to do after this completes. */
513 printf("Entering event loop, PowerOff the machine to exit or press Ctrl-C to terminate\n");
514 fflush(stdout);
515#ifdef WIN32
516 SetConsoleCtrlHandler(ctrlCHandler, TRUE);
517#else
518 signal(SIGINT, (void (*)(int))ctrlCHandler);
519#endif
520
521 while (!g_fStop)
522 g_pVBoxFuncs->pfnProcessEventQueue(250);
523
524#ifdef WIN32
525 SetConsoleCtrlHandler(ctrlCHandler, FALSE);
526#else
527 signal(SIGINT, SIG_DFL);
528#endif
529 }
530 else
531 printf("Failed to register event listener.\n");
532 IEventSource_UnregisterListener(es, (IEventListener *)consoleListener);
533#ifdef WIN32
534 if (consoleListener->pUnkMarshaler)
535 IUnknown_Release(consoleListener->pUnkMarshaler);
536#endif
537 IEventListenerDemo_Release(consoleListener);
538 }
539 else
540 printf("Failed while allocating memory for console event listener.\n");
541 g_pVBoxFuncs->pfnSafeArrayDestroy(interestingEventsSA);
542 IEventSource_Release(es);
543 }
544 else
545 printf("Failed to get the event source instance.\n");
546 IConsole_Release(console);
547 }
548}
549
550#else /* !USE_ACTIVE_EVENT_LISTENER */
551
552/**
553 * Register passive event listener for the selected VM.
554 *
555 * @param virtualBox ptr to IVirtualBox object
556 * @param session ptr to ISession object
557 */
558static void registerPassiveEventListener(ISession *session)
559{
560 IConsole *console = NULL;
561 HRESULT rc;
562
563 rc = ISession_get_Console(session, &console);
564 if (SUCCEEDED(rc) && console)
565 {
566 IEventSource *es = NULL;
567 rc = IConsole_get_EventSource(console, &es);
568 if (SUCCEEDED(rc) && es)
569 {
570 static const ULONG s_auInterestingEvents[] =
571 {
572 VBoxEventType_OnMousePointerShapeChanged,
573 VBoxEventType_OnMouseCapabilityChanged,
574 VBoxEventType_OnKeyboardLedsChanged,
575 VBoxEventType_OnStateChanged,
576 VBoxEventType_OnAdditionsStateChanged,
577 VBoxEventType_OnNetworkAdapterChanged,
578 VBoxEventType_OnSerialPortChanged,
579 VBoxEventType_OnParallelPortChanged,
580 VBoxEventType_OnStorageControllerChanged,
581 VBoxEventType_OnMediumChanged,
582 VBoxEventType_OnVRDEServerChanged,
583 VBoxEventType_OnUSBControllerChanged,
584 VBoxEventType_OnUSBDeviceStateChanged,
585 VBoxEventType_OnSharedFolderChanged,
586 VBoxEventType_OnRuntimeError,
587 VBoxEventType_OnCanShowWindow,
588 VBoxEventType_OnShowWindow
589 };
590 SAFEARRAY *interestingEventsSA = NULL;
591 IEventListener *consoleListener = NULL;
592
593 /* The VirtualBox API expects enum values as VT_I4, which in the
594 * future can be hopefully relaxed. */
595 interestingEventsSA = g_pVBoxFuncs->pfnSafeArrayCreateVector(VT_I4, 0,
596 sizeof(s_auInterestingEvents)
597 / sizeof(s_auInterestingEvents[0]));
598 g_pVBoxFuncs->pfnSafeArrayCopyInParamHelper(interestingEventsSA, &s_auInterestingEvents,
599 sizeof(s_auInterestingEvents));
600
601 rc = IEventSource_CreateListener(es, &consoleListener);
602 if (SUCCEEDED(rc) && consoleListener)
603 {
604 rc = IEventSource_RegisterListener(es, consoleListener,
605 ComSafeArrayAsInParam(interestingEventsSA),
606 0 /* passive */);
607 if (SUCCEEDED(rc))
608 {
609 /* Just wait here for events, no easy way to do this better
610 * as there's not much to do after this completes. */
611 printf("Entering event loop, PowerOff the machine to exit or press Ctrl-C to terminate\n");
612 fflush(stdout);
613#ifdef WIN32
614 SetConsoleCtrlHandler(ctrlCHandler, TRUE);
615#else
616 signal(SIGINT, ctrlCHandler);
617#endif
618
619 while (!g_fStop)
620 {
621 IEvent *ev = NULL;
622 rc = IEventSource_GetEvent(es, consoleListener, 250, &ev);
623 if (FAILED(rc))
624 {
625 printf("Failed getting event: %#x\n", (unsigned)rc);
626 g_fStop = 1;
627 continue;
628 }
629 /* handle timeouts, resulting in NULL events */
630 if (!ev)
631 continue;
632 rc = EventListenerDemoProcessEvent(ev);
633 if (FAILED(rc))
634 {
635 printf("Failed processing event: %#x\n", (unsigned)rc);
636 g_fStop = 1;
637 /* finish processing the event */
638 }
639 rc = IEventSource_EventProcessed(es, consoleListener, ev);
640 if (FAILED(rc))
641 {
642 printf("Failed to mark event as processed: %#x\n", (unsigned)rc);
643 g_fStop = 1;
644 /* continue with event release */
645 }
646 if (ev)
647 {
648 IEvent_Release(ev);
649 ev = NULL;
650 }
651 }
652
653#ifdef WIN32
654 SetConsoleCtrlHandler(ctrlCHandler, FALSE);
655#else
656 signal(SIGINT, SIG_DFL);
657#endif
658 }
659 else
660 printf("Failed to register event listener.\n");
661 IEventSource_UnregisterListener(es, (IEventListener *)consoleListener);
662 IEventListener_Release(consoleListener);
663 }
664 else
665 printf("Failed to create an event listener instance.\n");
666 g_pVBoxFuncs->pfnSafeArrayDestroy(interestingEventsSA);
667 IEventSource_Release(es);
668 }
669 else
670 printf("Failed to get the event source instance.\n");
671 IConsole_Release(console);
672 }
673}
674
675#endif /* !USE_ACTIVE_EVENT_LISTENER */
676
677/**
678 * Print detailed error information if available.
679 * @param pszExecutable string with the executable name
680 * @param pszErrorMsg string containing the code location specific error message
681 * @param rc COM/XPCOM result code
682 */
683static void PrintErrorInfo(const char *pszExecutable, const char *pszErrorMsg, HRESULT rc)
684{
685 IErrorInfo *ex;
686 HRESULT rc2;
687 fprintf(stderr, "%s: %s (rc=%#010x)\n", pszExecutable, pszErrorMsg, (unsigned)rc);
688 rc2 = g_pVBoxFuncs->pfnGetException(&ex);
689 if (SUCCEEDED(rc2) && ex)
690 {
691 IVirtualBoxErrorInfo *ei;
692 rc2 = IErrorInfo_QueryInterface(ex, &IID_IVirtualBoxErrorInfo, (void **)&ei);
693 if (SUCCEEDED(rc2) && ei != NULL)
694 {
695 /* got extended error info, maybe multiple infos */
696 do
697 {
698 LONG resultCode = S_OK;
699 BSTR componentUtf16 = NULL;
700 char *component = NULL;
701 BSTR textUtf16 = NULL;
702 char *text = NULL;
703 IVirtualBoxErrorInfo *ei_next = NULL;
704 fprintf(stderr, "Extended error info (IVirtualBoxErrorInfo):\n");
705
706 IVirtualBoxErrorInfo_get_ResultCode(ei, &resultCode);
707 fprintf(stderr, " resultCode=%#010x\n", (unsigned)resultCode);
708
709 IVirtualBoxErrorInfo_get_Component(ei, &componentUtf16);
710 g_pVBoxFuncs->pfnUtf16ToUtf8(componentUtf16, &component);
711 g_pVBoxFuncs->pfnComUnallocString(componentUtf16);
712 fprintf(stderr, " component=%s\n", component);
713 g_pVBoxFuncs->pfnUtf8Free(component);
714
715 IVirtualBoxErrorInfo_get_Text(ei, &textUtf16);
716 g_pVBoxFuncs->pfnUtf16ToUtf8(textUtf16, &text);
717 g_pVBoxFuncs->pfnComUnallocString(textUtf16);
718 fprintf(stderr, " text=%s\n", text);
719 g_pVBoxFuncs->pfnUtf8Free(text);
720
721 rc2 = IVirtualBoxErrorInfo_get_Next(ei, &ei_next);
722 if (FAILED(rc2))
723 ei_next = NULL;
724 IVirtualBoxErrorInfo_Release(ei);
725 ei = ei_next;
726 } while (ei);
727 }
728
729 IErrorInfo_Release(ex);
730 g_pVBoxFuncs->pfnClearException();
731 }
732}
733
734/**
735 * Start a VM.
736 *
737 * @param argv0 executable name
738 * @param virtualBox ptr to IVirtualBox object
739 * @param session ptr to ISession object
740 * @param id identifies the machine to start
741 */
742static void startVM(const char *argv0, IVirtualBox *virtualBox, ISession *session, BSTR id)
743{
744 HRESULT rc;
745 IMachine *machine = NULL;
746 IProgress *progress = NULL;
747 SAFEARRAY *env = NULL;
748 BSTR sessionType;
749 SAFEARRAY *groupsSA = g_pVBoxFuncs->pfnSafeArrayOutParamAlloc();
750
751 rc = IVirtualBox_FindMachine(virtualBox, id, &machine);
752 if (FAILED(rc) || !machine)
753 {
754 PrintErrorInfo(argv0, "Error: Couldn't get the Machine reference", rc);
755 return;
756 }
757
758 rc = IMachine_get_Groups(machine, ComSafeArrayAsOutTypeParam(groupsSA, BSTR));
759 if (SUCCEEDED(rc))
760 {
761 BSTR *groups = NULL;
762 ULONG cbGroups = 0;
763 ULONG i, cGroups;
764 g_pVBoxFuncs->pfnSafeArrayCopyOutParamHelper((void **)&groups, &cbGroups, VT_BSTR, groupsSA);
765 g_pVBoxFuncs->pfnSafeArrayDestroy(groupsSA);
766 cGroups = cbGroups / sizeof(groups[0]);
767 for (i = 0; i < cGroups; ++i)
768 {
769 /* Note that the use of %S might be tempting, but it is not
770 * available on all platforms, and even where it is usable it
771 * may depend on correct compiler options to make wchar_t a
772 * 16 bit number. So better play safe and use UTF-8. */
773 char *group;
774 g_pVBoxFuncs->pfnUtf16ToUtf8(groups[i], &group);
775 printf("Groups[%u]: %s\n", (unsigned)i, group);
776 g_pVBoxFuncs->pfnUtf8Free(group);
777 }
778 for (i = 0; i < cGroups; ++i)
779 g_pVBoxFuncs->pfnComUnallocString(groups[i]);
780 g_pVBoxFuncs->pfnArrayOutFree(groups);
781 }
782
783 g_pVBoxFuncs->pfnUtf8ToUtf16("gui", &sessionType);
784 rc = IMachine_LaunchVMProcess(machine, session, sessionType, ComSafeArrayAsInParam(env), &progress);
785 g_pVBoxFuncs->pfnUtf16Free(sessionType);
786 if (SUCCEEDED(rc))
787 {
788 BOOL completed;
789 LONG resultCode;
790
791 printf("Waiting for the remote session to open...\n");
792 IProgress_WaitForCompletion(progress, -1);
793
794 rc = IProgress_get_Completed(progress, &completed);
795 if (FAILED(rc))
796 fprintf(stderr, "Error: GetCompleted status failed\n");
797
798 IProgress_get_ResultCode(progress, &resultCode);
799 if (FAILED(resultCode))
800 {
801 IVirtualBoxErrorInfo *errorInfo;
802 BSTR textUtf16;
803 char *text;
804
805 IProgress_get_ErrorInfo(progress, &errorInfo);
806 IVirtualBoxErrorInfo_get_Text(errorInfo, &textUtf16);
807 g_pVBoxFuncs->pfnUtf16ToUtf8(textUtf16, &text);
808 printf("Error: %s\n", text);
809
810 g_pVBoxFuncs->pfnComUnallocString(textUtf16);
811 g_pVBoxFuncs->pfnUtf8Free(text);
812 IVirtualBoxErrorInfo_Release(errorInfo);
813 }
814 else
815 {
816 fprintf(stderr, "VM process has been successfully started\n");
817
818 /* Kick off the event listener demo part, which is quite separate.
819 * Ignore it if you need a more basic sample. */
820#ifdef USE_ACTIVE_EVENT_LISTENER
821 registerActiveEventListener(virtualBox, session);
822#else
823 registerPassiveEventListener(session);
824#endif
825 }
826 IProgress_Release(progress);
827 }
828 else
829 PrintErrorInfo(argv0, "Error: LaunchVMProcess failed", rc);
830
831 /* It's important to always release resources. */
832 IMachine_Release(machine);
833}
834
835/**
836 * List the registered VMs.
837 *
838 * @param argv0 executable name
839 * @param virtualBox ptr to IVirtualBox object
840 * @param session ptr to ISession object
841 */
842static void listVMs(const char *argv0, IVirtualBox *virtualBox, ISession *session)
843{
844 HRESULT rc;
845 SAFEARRAY *machinesSA = g_pVBoxFuncs->pfnSafeArrayOutParamAlloc();
846 IMachine **machines = NULL;
847 ULONG machineCnt = 0;
848 ULONG i;
849 unsigned start_id;
850
851 /*
852 * Get the list of all registered VMs.
853 */
854 rc = IVirtualBox_get_Machines(virtualBox, ComSafeArrayAsOutIfaceParam(machinesSA, IMachine *));
855 if (FAILED(rc))
856 {
857 PrintErrorInfo(argv0, "could not get list of machines", rc);
858 return;
859 }
860
861 /*
862 * Extract interface pointers from machinesSA, and update the reference
863 * counter of each object, as destroying machinesSA would call Release.
864 */
865 g_pVBoxFuncs->pfnSafeArrayCopyOutIfaceParamHelper((IUnknown ***)&machines, &machineCnt, machinesSA);
866 g_pVBoxFuncs->pfnSafeArrayDestroy(machinesSA);
867
868 if (!machineCnt)
869 {
870 g_pVBoxFuncs->pfnArrayOutFree(machines);
871 printf("\tNo VMs\n");
872 return;
873 }
874
875 printf("VM List:\n\n");
876
877 /*
878 * Iterate through the collection.
879 */
880 for (i = 0; i < machineCnt; ++i)
881 {
882 IMachine *machine = machines[i];
883 BOOL isAccessible = FALSE;
884
885 printf("\tMachine #%u\n", (unsigned)i);
886
887 if (!machine)
888 {
889 printf("\t(skipped, NULL)\n");
890 continue;
891 }
892
893 IMachine_get_Accessible(machine, &isAccessible);
894
895 if (isAccessible)
896 {
897 BSTR machineNameUtf16;
898 char *machineName;
899
900 IMachine_get_Name(machine, &machineNameUtf16);
901 g_pVBoxFuncs->pfnUtf16ToUtf8(machineNameUtf16,&machineName);
902 g_pVBoxFuncs->pfnComUnallocString(machineNameUtf16);
903 printf("\tName: %s\n", machineName);
904 g_pVBoxFuncs->pfnUtf8Free(machineName);
905 }
906 else
907 printf("\tName: <inaccessible>\n");
908
909 {
910 BSTR uuidUtf16;
911 char *uuidUtf8;
912
913 IMachine_get_Id(machine, &uuidUtf16);
914 g_pVBoxFuncs->pfnUtf16ToUtf8(uuidUtf16, &uuidUtf8);
915 g_pVBoxFuncs->pfnComUnallocString(uuidUtf16);
916 printf("\tUUID: %s\n", uuidUtf8);
917 g_pVBoxFuncs->pfnUtf8Free(uuidUtf8);
918 }
919
920 if (isAccessible)
921 {
922 {
923 BSTR configFileUtf16;
924 char *configFileUtf8;
925
926 IMachine_get_SettingsFilePath(machine, &configFileUtf16);
927 g_pVBoxFuncs->pfnUtf16ToUtf8(configFileUtf16, &configFileUtf8);
928 g_pVBoxFuncs->pfnComUnallocString(configFileUtf16);
929 printf("\tConfig file: %s\n", configFileUtf8);
930 g_pVBoxFuncs->pfnUtf8Free(configFileUtf8);
931 }
932
933 {
934 ULONG memorySize;
935
936 IMachine_get_MemorySize(machine, &memorySize);
937 printf("\tMemory size: %uMB\n", (unsigned)memorySize);
938 }
939
940 {
941 BSTR typeId;
942 BSTR osNameUtf16;
943 char *osName;
944 IGuestOSType *osType = NULL;
945
946 IMachine_get_OSTypeId(machine, &typeId);
947 IVirtualBox_GetGuestOSType(virtualBox, typeId, &osType);
948 g_pVBoxFuncs->pfnComUnallocString(typeId);
949 IGuestOSType_get_Description(osType, &osNameUtf16);
950 g_pVBoxFuncs->pfnUtf16ToUtf8(osNameUtf16,&osName);
951 g_pVBoxFuncs->pfnComUnallocString(osNameUtf16);
952 printf("\tGuest OS: %s\n\n", osName);
953 g_pVBoxFuncs->pfnUtf8Free(osName);
954
955 IGuestOSType_Release(osType);
956 }
957 }
958 }
959
960 /*
961 * Let the user chose a machine to start.
962 */
963 printf("Type Machine# to start (0 - %u) or 'quit' to do nothing: ",
964 (unsigned)(machineCnt - 1));
965 fflush(stdout);
966
967 if (scanf("%u", &start_id) == 1 && start_id < machineCnt)
968 {
969 IMachine *machine = machines[start_id];
970
971 if (machine)
972 {
973 BSTR uuidUtf16 = NULL;
974
975 IMachine_get_Id(machine, &uuidUtf16);
976 startVM(argv0, virtualBox, session, uuidUtf16);
977 g_pVBoxFuncs->pfnComUnallocString(uuidUtf16);
978 }
979 }
980
981 /*
982 * Don't forget to release the objects in the array.
983 */
984 for (i = 0; i < machineCnt; ++i)
985 {
986 IMachine *machine = machines[i];
987
988 if (machine)
989 IMachine_Release(machine);
990 }
991 g_pVBoxFuncs->pfnArrayOutFree(machines);
992}
993
994/* Main - Start the ball rolling. */
995
996int main(int argc, char **argv)
997{
998 IVirtualBoxClient *vboxclient = NULL;
999 IVirtualBox *vbox = NULL;
1000 ISession *session = NULL;
1001 ULONG revision = 0;
1002 BSTR versionUtf16 = NULL;
1003 BSTR homefolderUtf16 = NULL;
1004 HRESULT rc; /* Result code of various function (method) calls. */
1005 (void)argc;
1006
1007 printf("Starting main()\n");
1008
1009 if (VBoxCGlueInit())
1010 {
1011 fprintf(stderr, "%s: FATAL: VBoxCGlueInit failed: %s\n",
1012 argv[0], g_szVBoxErrMsg);
1013 return EXIT_FAILURE;
1014 }
1015
1016 {
1017 unsigned ver = g_pVBoxFuncs->pfnGetVersion();
1018 printf("VirtualBox version: %u.%u.%u\n", ver / 1000000, ver / 1000 % 1000, ver % 1000);
1019 ver = g_pVBoxFuncs->pfnGetAPIVersion();
1020 printf("VirtualBox API version: %u.%u\n", ver / 1000, ver % 1000);
1021 }
1022
1023 g_pVBoxFuncs->pfnClientInitialize(NULL, &vboxclient);
1024 if (!vboxclient)
1025 {
1026 fprintf(stderr, "%s: FATAL: could not get VirtualBoxClient reference\n", argv[0]);
1027 return EXIT_FAILURE;
1028 }
1029
1030 printf("----------------------------------------------------\n");
1031
1032 rc = IVirtualBoxClient_get_VirtualBox(vboxclient, &vbox);
1033 if (FAILED(rc) || !vbox)
1034 {
1035 PrintErrorInfo(argv[0], "FATAL: could not get VirtualBox reference", rc);
1036 return EXIT_FAILURE;
1037 }
1038 rc = IVirtualBoxClient_get_Session(vboxclient, &session);
1039 if (FAILED(rc) || !session)
1040 {
1041 PrintErrorInfo(argv[0], "FATAL: could not get Session reference", rc);
1042 return EXIT_FAILURE;
1043 }
1044
1045#ifdef USE_ACTIVE_EVENT_LISTENER
1046# ifdef WIN32
1047 rc = LoadTypeInfo(&IID_IEventListener, &g_pTInfoIEventListener);
1048 if (FAILED(rc) || !g_pTInfoIEventListener)
1049 {
1050 PrintErrorInfo(argv[0], "FATAL: could not get type information for IEventListener", rc);
1051 return EXIT_FAILURE;
1052 }
1053# endif /* WIN32 */
1054#endif /* USE_ACTIVE_EVENT_LISTENER */
1055
1056 /*
1057 * Now ask for revision, version and home folder information of
1058 * this vbox. Were not using fancy macros here so it
1059 * remains easy to see how we access C++'s vtable.
1060 */
1061
1062 /* 1. Revision */
1063 rc = IVirtualBox_get_Revision(vbox, &revision);
1064 if (SUCCEEDED(rc))
1065 printf("\tRevision: %u\n", (unsigned)revision);
1066 else
1067 PrintErrorInfo(argv[0], "GetRevision() failed", rc);
1068
1069 /* 2. Version */
1070 rc = IVirtualBox_get_Version(vbox, &versionUtf16);
1071 if (SUCCEEDED(rc))
1072 {
1073 char *version = NULL;
1074 g_pVBoxFuncs->pfnUtf16ToUtf8(versionUtf16, &version);
1075 printf("\tVersion: %s\n", version);
1076 g_pVBoxFuncs->pfnUtf8Free(version);
1077 g_pVBoxFuncs->pfnComUnallocString(versionUtf16);
1078 }
1079 else
1080 PrintErrorInfo(argv[0], "GetVersion() failed", rc);
1081
1082 /* 3. Home Folder */
1083 rc = IVirtualBox_get_HomeFolder(vbox, &homefolderUtf16);
1084 if (SUCCEEDED(rc))
1085 {
1086 char *homefolder = NULL;
1087 g_pVBoxFuncs->pfnUtf16ToUtf8(homefolderUtf16, &homefolder);
1088 printf("\tHomeFolder: %s\n", homefolder);
1089 g_pVBoxFuncs->pfnUtf8Free(homefolder);
1090 g_pVBoxFuncs->pfnComUnallocString(homefolderUtf16);
1091 }
1092 else
1093 PrintErrorInfo(argv[0], "GetHomeFolder() failed", rc);
1094
1095 listVMs(argv[0], vbox, session);
1096 ISession_UnlockMachine(session);
1097
1098 printf("----------------------------------------------------\n");
1099
1100 /*
1101 * Do as mom told us: always clean up after yourself.
1102 */
1103#ifdef USE_ACTIVE_EVENT_LISTENER
1104# ifdef WIN32
1105 if (g_pTInfoIEventListener)
1106 {
1107 ITypeInfo_Release(g_pTInfoIEventListener);
1108 g_pTInfoIEventListener = NULL;
1109 }
1110# endif /* WIN32 */
1111#endif /* USE_ACTIVE_EVENT_LISTENER */
1112
1113 if (session)
1114 {
1115 ISession_Release(session);
1116 session = NULL;
1117 }
1118 if (vbox)
1119 {
1120 IVirtualBox_Release(vbox);
1121 vbox = NULL;
1122 }
1123 if (vboxclient)
1124 {
1125 IVirtualBoxClient_Release(vboxclient);
1126 vboxclient = NULL;
1127 }
1128
1129 g_pVBoxFuncs->pfnClientUninitialize();
1130 VBoxCGlueTerm();
1131 printf("Finished main()\n");
1132
1133 return 0;
1134}
1135/* vim: set ts=4 sw=4 et: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette