VirtualBox

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

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

scm --update-copyright-year

  • 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 93115 2022-01-01 11:31:46Z 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-2022 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