VirtualBox

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

Last change on this file was 107013, checked in by vboxsync, 3 weeks ago

Main/cbinding/tstCAPIGlue: Don't run interactively (i.e. by requiring user input) by default. This will cause timeouts on the testboxes if we forgot to blacklist testcases for whatever reason [non-Windows build fix].

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