VirtualBox

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

Last change on this file since 62517 was 62485, checked in by vboxsync, 8 years ago

(C) 2016

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