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
RevLine 
[55400]1/* $Id: tstCAPIGlue.c 98297 2023-01-25 01:59:25Z vboxsync $ */
[50183]2/** @file tstCAPIGlue.c
3 * Demonstrator program to illustrate use of C bindings of Main API.
[16406]4 *
[50183]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.
[16406]8 */
9
[16483]10/*
[98103]11 * Copyright (C) 2009-2023 Oracle and/or its affiliates.
[16483]12 *
[96407]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
[16483]30 */
31
[57358]32
[62771]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
[57358]40/*********************************************************************************************************************************
41* Header Files *
42*********************************************************************************************************************************/
[50183]43#include "VBoxCAPIGlue.h"
[16406]44#include <stdio.h>
45#include <string.h>
46#include <stdlib.h>
[50183]47#ifndef WIN32
48# include <signal.h>
49# include <unistd.h>
50# include <sys/poll.h>
51#endif
[76557]52#ifdef IPRT_INCLUDED_cdefs_h
[62770]53# error "not supposed to involve any IPRT or VBox headers here."
54#endif
[16406]55
[50183]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
[57358]64/*********************************************************************************************************************************
65* Global Variables *
66*********************************************************************************************************************************/
[50183]67/** Set by Ctrl+C handler. */
[49539]68static volatile int g_fStop = 0;
69
[50183]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)
[49539]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";
[91363]86 case MachineState_AbortedSaved: return "Aborted-Saved";
[49539]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
[50183]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 */
[73505]115#ifdef WIN32
[50190]116static BOOL VBOX_WINAPI ctrlCHandler(DWORD iInfo)
[49539]117{
[50183]118 (void)iInfo;
119 g_fStop = 1;
120 return TRUE;
121}
[73505]122#else
123static void ctrlCHandler(int iInfo)
124{
125 (void)iInfo;
126 g_fStop = 1;
127}
128#endif
[49539]129
[48225]130/**
[50183]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.
[49539]134 */
[50183]135static HRESULT EventListenerDemoProcessEvent(IEvent *event)
[49539]136{
[50183]137 VBoxEventType_T evType;
[98297]138 HRESULT hrc;
[49539]139
140 if (!event)
141 {
142 printf("event null\n");
[50183]143 return S_OK;
[49539]144 }
145
146 evType = VBoxEventType_Invalid;
[98297]147 hrc = IEvent_get_Type(event, &evType);
148 if (FAILED(hrc))
[49539]149 {
[98297]150 printf("cannot get event type, hrc=%#x\n", (unsigned)hrc);
[50183]151 return S_OK;
[49539]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;
[98297]172 hrc = IEvent_QueryInterface(event, &IID_IStateChangedEvent, (void **)&ev);
173 if (FAILED(hrc))
[49539]174 {
[98297]175 printf("cannot get StateChangedEvent interface, hrc=%#x\n", (unsigned)hrc);
[50183]176 return S_OK;
[49539]177 }
178 if (!ev)
179 {
180 printf("StateChangedEvent reference null\n");
[50183]181 return S_OK;
[49539]182 }
[98297]183 hrc = IStateChangedEvent_get_State(ev, &state);
184 if (FAILED(hrc))
185 printf("warning: cannot get state, hrc=%#x\n", (unsigned)hrc);
[50183]186 IStateChangedEvent_Release(ev);
[49539]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
[91363]194 || state == MachineState_AbortedSaved
[49539]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
[50183]255 return S_OK;
[49539]256}
257
[50183]258#ifdef USE_ACTIVE_EVENT_LISTENER
259
260struct IEventListenerDemo;
261typedef struct IEventListenerDemo IEventListenerDemo;
262
263typedef struct IEventListenerDemoVtbl
[49539]264{
[50183]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;
[49539]276
[50183]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{
[49539]332 /* match iid */
[50183]333 if ( !memcmp(iid, &IID_IEventListener, sizeof(IID))
334 || !memcmp(iid, &IID_IDispatch, sizeof(IID))
335 || !memcmp(iid, &IID_IUnknown, sizeof(IID)))
[49539]336 {
[50183]337 IEventListenerDemo_AddRef(pThis);
[49539]338 *resultp = pThis;
[50183]339 return S_OK;
[49539]340 }
[50183]341#ifdef WIN32
342 if (!memcmp(iid, &IID_IMarshal, sizeof(IID)))
343 return IUnknown_QueryInterface(pThis->pUnkMarshaler, iid, resultp);
344#endif
[49539]345
[50183]346 return E_NOINTERFACE;
[49539]347}
348
[50183]349static HRESULT IEventListenerDemoImpl_AddRef(IEventListenerDemo *pThis)
[49539]350{
[50183]351 return ++(pThis->cRef);
[49539]352}
353
[50183]354static HRESULT IEventListenerDemoImpl_Release(IEventListenerDemo *pThis)
[49539]355{
[50183]356 HRESULT c;
[49539]357
[50183]358 c = --(pThis->cRef);
359 if (!c)
[49539]360 free(pThis);
361 return c;
362}
363
[50183]364#ifdef WIN32
365static HRESULT IEventListenerDemoImpl_GetTypeInfoCount(IEventListenerDemo *pThis, UINT *pctinfo)
[49539]366{
[50183]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{
[98297]394 HRESULT hrc;
[50183]395 ITypeLib *pTypeLib;
[98297]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);
[50183]400
401 /* No longer need access to the type lib, release it. */
402 ITypeLib_Release(pTypeLib);
403
[98297]404 return hrc;
[50183]405}
406#endif
407
408#ifdef __GNUC__
409typedef struct IEventListenerDemoVtblInt
410{
[49539]411 ptrdiff_t offset_to_top;
412 void *typeinfo;
[50183]413 IEventListenerDemoVtbl lpVtbl;
414} IEventListenerDemoVtblInt;
[49539]415
[50183]416static IEventListenerDemoVtblInt g_IEventListenerDemoVtblInt =
[49539]417{
418 0, /* offset_to_top */
419 NULL, /* typeinfo, not vital */
420 {
[50183]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
[49539]431 }
432};
[50183]433#elif defined(_MSC_VER)
434typedef struct IEventListenerDemoVtblInt
435{
436 IEventListenerDemoVtbl lpVtbl;
437} IEventListenerDemoVtblInt;
[49539]438
[50183]439static IEventListenerDemoVtblInt g_IEventListenerDemoVtblInt =
[49539]440{
[50183]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
[49539]457
458/**
[50183]459 * Register active event listener for the selected VM.
[49539]460 *
[48225]461 * @param virtualBox ptr to IVirtualBox object
462 * @param session ptr to ISession object
463 */
[62770]464static void registerActiveEventListener(IVirtualBox *virtualBox, ISession *session)
[48225]465{
[49539]466 IConsole *console = NULL;
[98297]467 HRESULT hrc;
[49539]468
[98297]469 hrc = ISession_get_Console(session, &console);
470 if (SUCCEEDED(hrc) && console)
[49539]471 {
472 IEventSource *es = NULL;
[98297]473 hrc = IConsole_get_EventSource(console, &es);
474 if (SUCCEEDED(hrc) && es)
[49539]475 {
[62770]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 };
[50183]496 SAFEARRAY *interestingEventsSA = NULL;
[49539]497 IEventListenerDemo *consoleListener = NULL;
[50183]498
499 /* The VirtualBox API expects enum values as VT_I4, which in the
500 * future can be hopefully relaxed. */
[62770]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));
[50183]506
[49539]507 consoleListener = calloc(1, sizeof(IEventListenerDemo));
508 if (consoleListener)
509 {
[50183]510 consoleListener->lpVtbl = &(g_IEventListenerDemoVtblInt.lpVtbl);
511#ifdef WIN32
512 CoCreateFreeThreadedMarshaler((IUnknown *)consoleListener, &consoleListener->pUnkMarshaler);
513#endif
514 IEventListenerDemo_AddRef(consoleListener);
[49539]515
[98297]516 hrc = IEventSource_RegisterListener(es, (IEventListener *)consoleListener,
517 ComSafeArrayAsInParam(interestingEventsSA),
518 1 /* active */);
519 if (SUCCEEDED(hrc))
[49539]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);
[50183]525#ifdef WIN32
526 SetConsoleCtrlHandler(ctrlCHandler, TRUE);
527#else
528 signal(SIGINT, (void (*)(int))ctrlCHandler);
529#endif
[49539]530
[50183]531 while (!g_fStop)
532 g_pVBoxFuncs->pfnProcessEventQueue(250);
[49539]533
[50183]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}
[49539]559
[50183]560#else /* !USE_ACTIVE_EVENT_LISTENER */
[49539]561
[50183]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 */
[62770]568static void registerPassiveEventListener(ISession *session)
[50183]569{
570 IConsole *console = NULL;
[98297]571 HRESULT hrc;
[49539]572
[98297]573 hrc = ISession_get_Console(session, &console);
574 if (SUCCEEDED(hrc) && console)
[50183]575 {
576 IEventSource *es = NULL;
[98297]577 hrc = IConsole_get_EventSource(console, &es);
578 if (SUCCEEDED(hrc) && es)
[50183]579 {
[62770]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 };
[50183]600 SAFEARRAY *interestingEventsSA = NULL;
601 IEventListener *consoleListener = NULL;
[49539]602
[50183]603 /* The VirtualBox API expects enum values as VT_I4, which in the
604 * future can be hopefully relaxed. */
[62770]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));
[50183]610
[98297]611 hrc = IEventSource_CreateListener(es, &consoleListener);
612 if (SUCCEEDED(hrc) && consoleListener)
[50183]613 {
[98297]614 hrc = IEventSource_RegisterListener(es, consoleListener,
615 ComSafeArrayAsInParam(interestingEventsSA),
616 0 /* passive */);
617 if (SUCCEEDED(hrc))
[50183]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
[73505]626 signal(SIGINT, ctrlCHandler);
[50183]627#endif
628
629 while (!g_fStop)
[49539]630 {
[50183]631 IEvent *ev = NULL;
[98297]632 hrc = IEventSource_GetEvent(es, consoleListener, 250, &ev);
633 if (FAILED(hrc))
[49539]634 {
[98297]635 printf("Failed getting event: %#x\n", (unsigned)hrc);
[50183]636 g_fStop = 1;
637 continue;
[49539]638 }
[50183]639 /* handle timeouts, resulting in NULL events */
640 if (!ev)
641 continue;
[98297]642 hrc = EventListenerDemoProcessEvent(ev);
643 if (FAILED(hrc))
[50183]644 {
[98297]645 printf("Failed processing event: %#x\n", (unsigned)hrc);
[50183]646 g_fStop = 1;
647 /* finish processing the event */
648 }
[98297]649 hrc = IEventSource_EventProcessed(es, consoleListener, ev);
650 if (FAILED(hrc))
[50183]651 {
[98297]652 printf("Failed to mark event as processed: %#x\n", (unsigned)hrc);
[50183]653 g_fStop = 1;
654 /* continue with event release */
655 }
656 if (ev)
657 {
658 IEvent_Release(ev);
659 ev = NULL;
660 }
[49539]661 }
[50183]662
663#ifdef WIN32
664 SetConsoleCtrlHandler(ctrlCHandler, FALSE);
665#else
[49539]666 signal(SIGINT, SIG_DFL);
[50183]667#endif
[49539]668 }
[50183]669 else
670 printf("Failed to register event listener.\n");
671 IEventSource_UnregisterListener(es, (IEventListener *)consoleListener);
672 IEventListener_Release(consoleListener);
[49539]673 }
674 else
[50183]675 printf("Failed to create an event listener instance.\n");
676 g_pVBoxFuncs->pfnSafeArrayDestroy(interestingEventsSA);
677 IEventSource_Release(es);
[49539]678 }
[50183]679 else
680 printf("Failed to get the event source instance.\n");
681 IConsole_Release(console);
[49539]682 }
683}
684
[50183]685#endif /* !USE_ACTIVE_EVENT_LISTENER */
686
[49539]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
[98297]691 * @param hrc COM/XPCOM result code
[49539]692 */
[98297]693static void PrintErrorInfo(const char *pszExecutable, const char *pszErrorMsg, HRESULT hrc)
[49539]694{
[50183]695 IErrorInfo *ex;
[98297]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)
[49539]700 {
701 IVirtualBoxErrorInfo *ei;
[98297]702 hrc2 = IErrorInfo_QueryInterface(ex, &IID_IVirtualBoxErrorInfo, (void **)&ei);
703 if (SUCCEEDED(hrc2) && ei != NULL)
[49539]704 {
705 /* got extended error info, maybe multiple infos */
706 do
707 {
[50183]708 LONG resultCode = S_OK;
709 BSTR componentUtf16 = NULL;
[49539]710 char *component = NULL;
[50183]711 BSTR textUtf16 = NULL;
[49539]712 char *text = NULL;
713 IVirtualBoxErrorInfo *ei_next = NULL;
714 fprintf(stderr, "Extended error info (IVirtualBoxErrorInfo):\n");
715
[50183]716 IVirtualBoxErrorInfo_get_ResultCode(ei, &resultCode);
[49539]717 fprintf(stderr, " resultCode=%#010x\n", (unsigned)resultCode);
718
[50183]719 IVirtualBoxErrorInfo_get_Component(ei, &componentUtf16);
[49539]720 g_pVBoxFuncs->pfnUtf16ToUtf8(componentUtf16, &component);
[50183]721 g_pVBoxFuncs->pfnComUnallocString(componentUtf16);
[49539]722 fprintf(stderr, " component=%s\n", component);
723 g_pVBoxFuncs->pfnUtf8Free(component);
724
[50183]725 IVirtualBoxErrorInfo_get_Text(ei, &textUtf16);
[49539]726 g_pVBoxFuncs->pfnUtf16ToUtf8(textUtf16, &text);
[50183]727 g_pVBoxFuncs->pfnComUnallocString(textUtf16);
[49539]728 fprintf(stderr, " text=%s\n", text);
729 g_pVBoxFuncs->pfnUtf8Free(text);
730
[98297]731 hrc2 = IVirtualBoxErrorInfo_get_Next(ei, &ei_next);
732 if (FAILED(hrc2))
[49539]733 ei_next = NULL;
[50183]734 IVirtualBoxErrorInfo_Release(ei);
[49539]735 ei = ei_next;
[62770]736 } while (ei);
[49539]737 }
738
[50183]739 IErrorInfo_Release(ex);
[49539]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 */
[50183]752static void startVM(const char *argv0, IVirtualBox *virtualBox, ISession *session, BSTR id)
[49539]753{
[98297]754 HRESULT hrc;
[48225]755 IMachine *machine = NULL;
756 IProgress *progress = NULL;
[80824]757 SAFEARRAY *env = NULL;
[50183]758 BSTR sessionType;
[50930]759 SAFEARRAY *groupsSA = g_pVBoxFuncs->pfnSafeArrayOutParamAlloc();
[16406]760
[98297]761 hrc = IVirtualBox_FindMachine(virtualBox, id, &machine);
762 if (FAILED(hrc) || !machine)
[48225]763 {
[98297]764 PrintErrorInfo(argv0, "Error: Couldn't get the Machine reference", hrc);
[48225]765 return;
766 }
767
[98297]768 hrc = IMachine_get_Groups(machine, ComSafeArrayAsOutTypeParam(groupsSA, BSTR));
769 if (SUCCEEDED(hrc))
[50930]770 {
771 BSTR *groups = NULL;
772 ULONG cbGroups = 0;
773 ULONG i, cGroups;
774 g_pVBoxFuncs->pfnSafeArrayCopyOutParamHelper((void **)&groups, &cbGroups, VT_BSTR, groupsSA);
[55752]775 g_pVBoxFuncs->pfnSafeArrayDestroy(groupsSA);
[50930]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);
[83828]785 printf("Groups[%u]: %s\n", (unsigned)i, group);
[50930]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
[48225]793 g_pVBoxFuncs->pfnUtf8ToUtf16("gui", &sessionType);
[98297]794 hrc = IMachine_LaunchVMProcess(machine, session, sessionType, ComSafeArrayAsInParam(env), &progress);
[48225]795 g_pVBoxFuncs->pfnUtf16Free(sessionType);
[98297]796 if (SUCCEEDED(hrc))
[48225]797 {
[50183]798 BOOL completed;
799 LONG resultCode;
[48225]800
801 printf("Waiting for the remote session to open...\n");
[50183]802 IProgress_WaitForCompletion(progress, -1);
[48225]803
[98297]804 hrc = IProgress_get_Completed(progress, &completed);
805 if (FAILED(hrc))
[49539]806 fprintf(stderr, "Error: GetCompleted status failed\n");
[48225]807
[50183]808 IProgress_get_ResultCode(progress, &resultCode);
809 if (FAILED(resultCode))
[48225]810 {
811 IVirtualBoxErrorInfo *errorInfo;
[50183]812 BSTR textUtf16;
[48225]813 char *text;
814
[50183]815 IProgress_get_ErrorInfo(progress, &errorInfo);
816 IVirtualBoxErrorInfo_get_Text(errorInfo, &textUtf16);
[48225]817 g_pVBoxFuncs->pfnUtf16ToUtf8(textUtf16, &text);
818 printf("Error: %s\n", text);
819
[50183]820 g_pVBoxFuncs->pfnComUnallocString(textUtf16);
[48225]821 g_pVBoxFuncs->pfnUtf8Free(text);
[50183]822 IVirtualBoxErrorInfo_Release(errorInfo);
[48225]823 }
824 else
825 {
[49539]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. */
[50183]830#ifdef USE_ACTIVE_EVENT_LISTENER
[62770]831 registerActiveEventListener(virtualBox, session);
832#else
833 registerPassiveEventListener(session);
834#endif
[48225]835 }
[50183]836 IProgress_Release(progress);
[48225]837 }
[49539]838 else
[98297]839 PrintErrorInfo(argv0, "Error: LaunchVMProcess failed", hrc);
[48225]840
841 /* It's important to always release resources. */
[50183]842 IMachine_Release(machine);
[48225]843}
844
[16406]845/**
846 * List the registered VMs.
847 *
[49539]848 * @param argv0 executable name
849 * @param virtualBox ptr to IVirtualBox object
850 * @param session ptr to ISession object
[16406]851 */
[50183]852static void listVMs(const char *argv0, IVirtualBox *virtualBox, ISession *session)
[16406]853{
[98297]854 HRESULT hrc;
[50183]855 SAFEARRAY *machinesSA = g_pVBoxFuncs->pfnSafeArrayOutParamAlloc();
[16406]856 IMachine **machines = NULL;
[50183]857 ULONG machineCnt = 0;
858 ULONG i;
[16406]859 unsigned start_id;
860
861 /*
862 * Get the list of all registered VMs.
863 */
[98297]864 hrc = IVirtualBox_get_Machines(virtualBox, ComSafeArrayAsOutIfaceParam(machinesSA, IMachine *));
865 if (FAILED(hrc))
[16406]866 {
[98297]867 PrintErrorInfo(argv0, "could not get list of machines", hrc);
[16406]868 return;
869 }
870
[50183]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)
[16500]879 {
[50930]880 g_pVBoxFuncs->pfnArrayOutFree(machines);
[16548]881 printf("\tNo VMs\n");
882 return;
883 }
[16500]884
[16548]885 printf("VM List:\n\n");
[16500]886
[16548]887 /*
888 * Iterate through the collection.
889 */
890 for (i = 0; i < machineCnt; ++i)
891 {
892 IMachine *machine = machines[i];
[50183]893 BOOL isAccessible = FALSE;
[16548]894
895 printf("\tMachine #%u\n", (unsigned)i);
896
897 if (!machine)
[16500]898 {
[16548]899 printf("\t(skipped, NULL)\n");
900 continue;
901 }
[16500]902
[50183]903 IMachine_get_Accessible(machine, &isAccessible);
[16500]904
[16548]905 if (isAccessible)
906 {
[50183]907 BSTR machineNameUtf16;
[16548]908 char *machineName;
[16500]909
[50183]910 IMachine_get_Name(machine, &machineNameUtf16);
[16835]911 g_pVBoxFuncs->pfnUtf16ToUtf8(machineNameUtf16,&machineName);
[50183]912 g_pVBoxFuncs->pfnComUnallocString(machineNameUtf16);
[16548]913 printf("\tName: %s\n", machineName);
[16835]914 g_pVBoxFuncs->pfnUtf8Free(machineName);
[16548]915 }
916 else
917 printf("\tName: <inaccessible>\n");
[16500]918
[16548]919 {
[50183]920 BSTR uuidUtf16;
[49539]921 char *uuidUtf8;
[16500]922
[50183]923 IMachine_get_Id(machine, &uuidUtf16);
[19375]924 g_pVBoxFuncs->pfnUtf16ToUtf8(uuidUtf16, &uuidUtf8);
[50183]925 g_pVBoxFuncs->pfnComUnallocString(uuidUtf16);
[19375]926 printf("\tUUID: %s\n", uuidUtf8);
927 g_pVBoxFuncs->pfnUtf8Free(uuidUtf8);
[16548]928 }
929
930 if (isAccessible)
931 {
[16500]932 {
[50183]933 BSTR configFileUtf16;
[49539]934 char *configFileUtf8;
[16500]935
[50183]936 IMachine_get_SettingsFilePath(machine, &configFileUtf16);
[49539]937 g_pVBoxFuncs->pfnUtf16ToUtf8(configFileUtf16, &configFileUtf8);
[50183]938 g_pVBoxFuncs->pfnComUnallocString(configFileUtf16);
[49539]939 printf("\tConfig file: %s\n", configFileUtf8);
940 g_pVBoxFuncs->pfnUtf8Free(configFileUtf8);
[16500]941 }
942
943 {
[50183]944 ULONG memorySize;
[16500]945
[50183]946 IMachine_get_MemorySize(machine, &memorySize);
[83828]947 printf("\tMemory size: %uMB\n", (unsigned)memorySize);
[16548]948 }
[16500]949
[16548]950 {
[50183]951 BSTR typeId;
952 BSTR osNameUtf16;
[16548]953 char *osName;
954 IGuestOSType *osType = NULL;
[16500]955
[50183]956 IMachine_get_OSTypeId(machine, &typeId);
957 IVirtualBox_GetGuestOSType(virtualBox, typeId, &osType);
958 g_pVBoxFuncs->pfnComUnallocString(typeId);
959 IGuestOSType_get_Description(osType, &osNameUtf16);
[16835]960 g_pVBoxFuncs->pfnUtf16ToUtf8(osNameUtf16,&osName);
[50183]961 g_pVBoxFuncs->pfnComUnallocString(osNameUtf16);
[16548]962 printf("\tGuest OS: %s\n\n", osName);
[49539]963 g_pVBoxFuncs->pfnUtf8Free(osName);
[16500]964
[50183]965 IGuestOSType_Release(osType);
[16500]966 }
967 }
[16548]968 }
[16500]969
[16548]970 /*
971 * Let the user chose a machine to start.
972 */
973 printf("Type Machine# to start (0 - %u) or 'quit' to do nothing: ",
[62770]974 (unsigned)(machineCnt - 1));
[16548]975 fflush(stdout);
[16500]976
[16548]977 if (scanf("%u", &start_id) == 1 && start_id < machineCnt)
978 {
979 IMachine *machine = machines[start_id];
980
981 if (machine)
[16500]982 {
[50183]983 BSTR uuidUtf16 = NULL;
[16500]984
[50183]985 IMachine_get_Id(machine, &uuidUtf16);
986 startVM(argv0, virtualBox, session, uuidUtf16);
987 g_pVBoxFuncs->pfnComUnallocString(uuidUtf16);
[16500]988 }
[16548]989 }
[16500]990
[16548]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)
[50183]999 IMachine_Release(machine);
[16500]1000 }
[50930]1001 g_pVBoxFuncs->pfnArrayOutFree(machines);
[16406]1002}
1003
1004/* Main - Start the ball rolling. */
1005
1006int main(int argc, char **argv)
1007{
[49539]1008 IVirtualBoxClient *vboxclient = NULL;
[48225]1009 IVirtualBox *vbox = NULL;
[16688]1010 ISession *session = NULL;
[50183]1011 ULONG revision = 0;
1012 BSTR versionUtf16 = NULL;
1013 BSTR homefolderUtf16 = NULL;
[98297]1014 HRESULT hrc; /* Result code of various function (method) calls. */
[62770]1015 (void)argc;
[16406]1016
[49539]1017 printf("Starting main()\n");
[16487]1018
[50254]1019 if (VBoxCGlueInit())
[16835]1020 {
[19028]1021 fprintf(stderr, "%s: FATAL: VBoxCGlueInit failed: %s\n",
1022 argv[0], g_szVBoxErrMsg);
[16835]1023 return EXIT_FAILURE;
1024 }
[16406]1025
[16487]1026 {
[49539]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
[50183]1033 g_pVBoxFuncs->pfnClientInitialize(NULL, &vboxclient);
1034 if (!vboxclient)
[49539]1035 {
1036 fprintf(stderr, "%s: FATAL: could not get VirtualBoxClient reference\n", argv[0]);
[16487]1037 return EXIT_FAILURE;
[16406]1038 }
[49539]1039
1040 printf("----------------------------------------------------\n");
1041
[98297]1042 hrc = IVirtualBoxClient_get_VirtualBox(vboxclient, &vbox);
1043 if (FAILED(hrc) || !vbox)
[16487]1044 {
[98297]1045 PrintErrorInfo(argv[0], "FATAL: could not get VirtualBox reference", hrc);
[16487]1046 return EXIT_FAILURE;
[16406]1047 }
[98297]1048 hrc = IVirtualBoxClient_get_Session(vboxclient, &session);
1049 if (FAILED(hrc) || !session)
[49539]1050 {
[98297]1051 PrintErrorInfo(argv[0], "FATAL: could not get Session reference", hrc);
[49539]1052 return EXIT_FAILURE;
1053 }
[16406]1054
[50183]1055#ifdef USE_ACTIVE_EVENT_LISTENER
1056# ifdef WIN32
[98297]1057 hrc = LoadTypeInfo(&IID_IEventListener, &g_pTInfoIEventListener);
1058 if (FAILED(hrc) || !g_pTInfoIEventListener)
[50183]1059 {
[98297]1060 PrintErrorInfo(argv[0], "FATAL: could not get type information for IEventListener", hrc);
[50183]1061 return EXIT_FAILURE;
1062 }
1063# endif /* WIN32 */
1064#endif /* USE_ACTIVE_EVENT_LISTENER */
[49539]1065
[16406]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 */
[16487]1071
[16406]1072 /* 1. Revision */
[98297]1073 hrc = IVirtualBox_get_Revision(vbox, &revision);
1074 if (SUCCEEDED(hrc))
[83828]1075 printf("\tRevision: %u\n", (unsigned)revision);
[16406]1076 else
[98297]1077 PrintErrorInfo(argv[0], "GetRevision() failed", hrc);
[16406]1078
1079 /* 2. Version */
[98297]1080 hrc = IVirtualBox_get_Version(vbox, &versionUtf16);
1081 if (SUCCEEDED(hrc))
[16406]1082 {
[16500]1083 char *version = NULL;
[16835]1084 g_pVBoxFuncs->pfnUtf16ToUtf8(versionUtf16, &version);
[16497]1085 printf("\tVersion: %s\n", version);
[16835]1086 g_pVBoxFuncs->pfnUtf8Free(version);
[50183]1087 g_pVBoxFuncs->pfnComUnallocString(versionUtf16);
[16406]1088 }
1089 else
[98297]1090 PrintErrorInfo(argv[0], "GetVersion() failed", hrc);
[16406]1091
1092 /* 3. Home Folder */
[98297]1093 hrc = IVirtualBox_get_HomeFolder(vbox, &homefolderUtf16);
1094 if (SUCCEEDED(hrc))
[16406]1095 {
[16500]1096 char *homefolder = NULL;
[16835]1097 g_pVBoxFuncs->pfnUtf16ToUtf8(homefolderUtf16, &homefolder);
[16500]1098 printf("\tHomeFolder: %s\n", homefolder);
[16835]1099 g_pVBoxFuncs->pfnUtf8Free(homefolder);
[50183]1100 g_pVBoxFuncs->pfnComUnallocString(homefolderUtf16);
[16406]1101 }
1102 else
[98297]1103 PrintErrorInfo(argv[0], "GetHomeFolder() failed", hrc);
[16406]1104
[50183]1105 listVMs(argv[0], vbox, session);
1106 ISession_UnlockMachine(session);
[16406]1107
1108 printf("----------------------------------------------------\n");
1109
1110 /*
1111 * Do as mom told us: always clean up after yourself.
1112 */
[50183]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
[49539]1123 if (session)
1124 {
[50183]1125 ISession_Release(session);
[49539]1126 session = NULL;
1127 }
1128 if (vbox)
1129 {
[50183]1130 IVirtualBox_Release(vbox);
[49539]1131 vbox = NULL;
1132 }
1133 if (vboxclient)
1134 {
[50183]1135 IVirtualBoxClient_Release(vboxclient);
[49539]1136 vboxclient = NULL;
1137 }
[50183]1138
[49539]1139 g_pVBoxFuncs->pfnClientUninitialize();
[16835]1140 VBoxCGlueTerm();
[49539]1141 printf("Finished main()\n");
[16406]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