VirtualBox

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

Last change on this file was 98297, checked in by vboxsync, 16 months ago

Main: rc -> hrc/vrc for all but testcases. Enabled scm rc checks accordingly. bugref:10223

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

© 2023 Oracle
ContactPrivacy policyTerms of Use