VirtualBox

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

Last change on this file since 51584 was 50930, checked in by vboxsync, 11 years ago

Main/cbinding: Add new helper function to free memory allocated by pfnSafeArrayCopyOutParamHelper, as on Windows getting the right free() function can be difficult otherwise. Fix pfnSafeArrayCopyOutParamHelper to be more robust (avoiding malloc(0) and returning bogus values in some error situations), and extend the sample code somewhat to print the groups (showing how to deal with string arrays)

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