VirtualBox

source: vbox/trunk/src/VBox/Main/testcase/tstVBoxAPIXPCOM.cpp@ 94660

Last change on this file since 94660 was 94660, checked in by vboxsync, 3 years ago

doc/manual,Main,Frontends: API changes in preparation for full VM encryption, guarded by VBOX_WITH_FULL_VM_ENCRYPTION (disabled by default) and returning a not supported error for now, bugref:9955

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.1 KB
Line 
1/* $Id: tstVBoxAPIXPCOM.cpp 94660 2022-04-21 08:38:34Z vboxsync $ */
2/** @file
3 *
4 * tstVBoxAPIXPCOM - sample program to illustrate the VirtualBox
5 * XPCOM API for machine management.
6 * It only uses standard C/C++ and XPCOM semantics,
7 * no additional VBox classes/macros/helpers.
8 */
9
10/*
11 * Copyright (C) 2006-2022 Oracle Corporation
12 *
13 * This file is part of VirtualBox Open Source Edition (OSE), as
14 * available from http://www.virtualbox.org. This file is free software;
15 * you can redistribute it and/or modify it under the terms of the GNU
16 * General Public License (GPL) as published by the Free Software
17 * Foundation, in version 2 as it comes in the "COPYING" file of the
18 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20 */
21
22/*
23 * PURPOSE OF THIS SAMPLE PROGRAM
24 * ------------------------------
25 *
26 * This sample program is intended to demonstrate the minimal code necessary
27 * to use VirtualBox XPCOM API for learning puroses only. The program uses
28 * pure XPCOM and doesn't have any extra dependencies to let you better
29 * understand what is going on when a client talks to the VirtualBox core
30 * using the XPCOM framework.
31 *
32 * However, if you want to write a real application, it is highly recommended
33 * to use our MS COM XPCOM Glue library and helper C++ classes. This way, you
34 * will get at least the following benefits:
35 *
36 * a) better portability: both the MS COM (used on Windows) and XPCOM (used
37 * everywhere else) VirtualBox client application from the same source code
38 * (including common smart C++ templates for automatic interface pointer
39 * reference counter and string data management);
40 * b) simpler XPCOM initialization and shutdown (only a single method call
41 * that does everything right).
42 *
43 * Currently, there is no separate sample program that uses the VirtualBox MS
44 * COM XPCOM Glue library. Please refer to the sources of stock VirtualBox
45 * applications such as the VirtualBox GUI frontend or the VBoxManage command
46 * line frontend.
47 *
48 *
49 * RUNNING THIS SAMPLE PROGRAM
50 * ---------------------------
51 *
52 * This sample program needs to know where the VirtualBox core files reside
53 * and where to search for VirtualBox shared libraries. Therefore, you need to
54 * use the following (or similar) command to execute it:
55 *
56 * $ env VBOX_XPCOM_HOME=../../.. LD_LIBRARY_PATH=../../.. ./tstVBoxAPIXPCOM
57 *
58 * The above command assumes that VBoxRT.so, VBoxXPCOM.so and others reside in
59 * the directory ../../..
60 */
61
62
63#include <stdio.h>
64#include <stdlib.h>
65#include <iconv.h>
66
67/*
68 * Include the XPCOM headers
69 */
70#include <nsMemory.h>
71#include <nsString.h>
72#include <nsIServiceManager.h>
73#include <nsEventQueueUtils.h>
74
75#include <nsIExceptionService.h>
76
77/*
78 * VirtualBox XPCOM interface. This header is generated
79 * from IDL which in turn is generated from a custom XML format.
80 */
81#include "VirtualBox_XPCOM.h"
82
83/*
84 * Prototypes
85 */
86
87char *nsIDToString(nsID *guid);
88void printErrorInfo();
89
90
91/**
92 * Display all registered VMs on the screen with some information about each
93 *
94 * @param virtualBox VirtualBox instance object.
95 */
96void listVMs(IVirtualBox *virtualBox)
97{
98 nsresult rc;
99
100 printf("----------------------------------------------------\n");
101 printf("VM List:\n\n");
102
103 /*
104 * Get the list of all registered VMs
105 */
106 IMachine **machines = NULL;
107 PRUint32 cMachines = 0;
108
109 rc = virtualBox->GetMachines(&cMachines, &machines);
110 if (NS_SUCCEEDED(rc))
111 {
112 /*
113 * Iterate through the collection
114 */
115 for (PRUint32 i = 0; i < cMachines; ++ i)
116 {
117 IMachine *machine = machines[i];
118 if (machine)
119 {
120 PRBool isAccessible = PR_FALSE;
121 machine->GetAccessible(&isAccessible);
122
123 if (isAccessible)
124 {
125 nsXPIDLString machineName;
126 machine->GetName(getter_Copies(machineName));
127 char *machineNameAscii = ToNewCString(machineName);
128 printf("\tName: %s\n", machineNameAscii);
129 free(machineNameAscii);
130 }
131 else
132 {
133 printf("\tName: <inaccessible>\n");
134 }
135
136 nsXPIDLString iid;
137 machine->GetId(getter_Copies(iid));
138 const char *uuidString = ToNewCString(iid);
139 printf("\tUUID: %s\n", uuidString);
140 free((void*)uuidString);
141
142 if (isAccessible)
143 {
144 nsXPIDLString configFile;
145 machine->GetSettingsFilePath(getter_Copies(configFile));
146 char *configFileAscii = ToNewCString(configFile);
147 printf("\tConfig file: %s\n", configFileAscii);
148 free(configFileAscii);
149
150 PRUint32 memorySize;
151 machine->GetMemorySize(&memorySize);
152 printf("\tMemory size: %uMB\n", memorySize);
153
154 nsXPIDLString typeId;
155 machine->GetOSTypeId(getter_Copies(typeId));
156 IGuestOSType *osType = nsnull;
157 virtualBox->GetGuestOSType(typeId.get(), &osType);
158 nsXPIDLString osName;
159 osType->GetDescription(getter_Copies(osName));
160 char *osNameAscii = ToNewCString(osName);
161 printf("\tGuest OS: %s\n\n", osNameAscii);
162 free(osNameAscii);
163 osType->Release();
164 }
165
166 /* don't forget to release the objects in the array... */
167 machine->Release();
168 }
169 }
170 nsMemory::Free(machines);
171 }
172 printf("----------------------------------------------------\n\n");
173}
174
175/**
176 * Create a sample VM
177 *
178 * @param virtualBox VirtualBox instance object.
179 */
180void createVM(IVirtualBox *virtualBox)
181{
182 nsresult rc;
183 /*
184 * First create a unnamed new VM. It will be unconfigured and not be saved
185 * in the configuration until we explicitely choose to do so.
186 */
187 nsCOMPtr<IMachine> machine;
188 rc = virtualBox->CreateMachine(NULL, /* settings file */
189 NS_LITERAL_STRING("A brand new name").get(),
190 0, nsnull, /* groups (safearray)*/
191 nsnull, /* ostype */
192 nsnull, /* create flags */
193 nsnull, /* cipher */
194 nsnull, /* password id */
195 nsnull, /* password */
196 getter_AddRefs(machine));
197 if (NS_FAILED(rc))
198 {
199 printf("Error: could not create machine! rc=%#x\n", rc);
200 return;
201 }
202
203 /*
204 * Set some properties
205 */
206 /* alternative to illustrate the use of string classes */
207 rc = machine->SetName(NS_ConvertUTF8toUTF16("A new name").get());
208 rc = machine->SetMemorySize(128);
209
210 /*
211 * Now a more advanced property -- the guest OS type. This is
212 * an object by itself which has to be found first. Note that we
213 * use the ID of the guest OS type here which is an internal
214 * representation (you can find that by configuring the OS type of
215 * a machine in the GUI and then looking at the <Guest ostype=""/>
216 * setting in the XML file. It is also possible to get the OS type from
217 * its description (win2k would be "Windows 2000") by getting the
218 * guest OS type collection and enumerating it.
219 */
220 nsCOMPtr<IGuestOSType> osType;
221 rc = virtualBox->GetGuestOSType(NS_LITERAL_STRING("Windows2000").get(),
222 getter_AddRefs(osType));
223 if (NS_FAILED(rc))
224 {
225 printf("Error: could not find guest OS type! rc=%#x\n", rc);
226 }
227 else
228 {
229 machine->SetOSTypeId(NS_LITERAL_STRING("Windows2000").get());
230 }
231
232 /*
233 * Register the VM. Note that this call also saves the VM config
234 * to disk. It is also possible to save the VM settings but not
235 * register the VM.
236 *
237 * Also note that due to current VirtualBox limitations, the machine
238 * must be registered *before* we can attach hard disks to it.
239 */
240 rc = virtualBox->RegisterMachine(machine);
241 if (NS_FAILED(rc))
242 {
243 printf("Error: could not register machine! rc=%#x\n", rc);
244 printErrorInfo();
245 return;
246 }
247
248 nsCOMPtr<IMachine> origMachine = machine;
249
250 /*
251 * In order to manipulate the registered machine, we must open a session
252 * for that machine. Do it now.
253 */
254 nsCOMPtr<ISession> session;
255 nsCOMPtr<IMachine> sessionMachine;
256 {
257 nsCOMPtr<nsIComponentManager> manager;
258 rc = NS_GetComponentManager(getter_AddRefs(manager));
259 if (NS_FAILED(rc))
260 {
261 printf("Error: could not get component manager! rc=%#x\n", rc);
262 return;
263 }
264 rc = manager->CreateInstanceByContractID(NS_SESSION_CONTRACTID,
265 nsnull,
266 NS_GET_IID(ISession),
267 getter_AddRefs(session));
268 if (NS_FAILED(rc))
269 {
270 printf("Error, could not instantiate session object! rc=%#x\n", rc);
271 return;
272 }
273
274 rc = machine->LockMachine(session, LockType_Write);
275 if (NS_FAILED(rc))
276 {
277 printf("Error, could not lock the machine for the session! rc=%#x\n", rc);
278 return;
279 }
280
281 /*
282 * After the machine is registered, the initial machine object becomes
283 * immutable. In order to get a mutable machine object, we must query
284 * it from the opened session object.
285 */
286 rc = session->GetMachine(getter_AddRefs(sessionMachine));
287 if (NS_FAILED(rc))
288 {
289 printf("Error, could not get machine session! rc=%#x\n", rc);
290 return;
291 }
292 }
293
294 /*
295 * Create a virtual harddisk
296 */
297 nsCOMPtr<IMedium> hardDisk = 0;
298 rc = virtualBox->CreateMedium(NS_LITERAL_STRING("VDI").get(),
299 NS_LITERAL_STRING("/tmp/TestHardDisk.vdi").get(),
300 AccessMode_ReadWrite, DeviceType_HardDisk,
301 getter_AddRefs(hardDisk));
302 if (NS_FAILED(rc))
303 {
304 printf("Failed creating a hard disk object! rc=%#x\n", rc);
305 }
306 else
307 {
308 /*
309 * We have only created an object so far. No on disk representation exists
310 * because none of its properties has been set so far. Let's continue creating
311 * a dynamically expanding image.
312 */
313 nsCOMPtr<IProgress> progress;
314 MediumVariant_T mediumVariants[] =
315 { MediumVariant_Standard };
316 rc = hardDisk->CreateBaseStorage(100 * 1024 * 1024, // size in bytes
317 sizeof(mediumVariants) / sizeof(mediumVariants[0]), mediumVariants,
318 getter_AddRefs(progress)); // optional progress object
319 if (NS_FAILED(rc))
320 {
321 printf("Failed creating hard disk image! rc=%#x\n", rc);
322 }
323 else
324 {
325 /*
326 * Creating the image is done in the background because it can take quite
327 * some time (at least fixed size images). We have to wait for its completion.
328 * Here we wait forever (timeout -1) which is potentially dangerous.
329 */
330 rc = progress->WaitForCompletion(-1);
331 PRInt32 resultCode;
332 progress->GetResultCode(&resultCode);
333 if (NS_FAILED(rc) || NS_FAILED(resultCode))
334 {
335 printf("Error: could not create hard disk! rc=%#x\n",
336 NS_FAILED(rc) ? rc : resultCode);
337 }
338 else
339 {
340 /*
341 * Now that it's created, we can assign it to the VM.
342 */
343 rc = sessionMachine->AttachDevice(
344 NS_LITERAL_STRING("IDE Controller").get(), // controller identifier
345 0, // channel number on the controller
346 0, // device number on the controller
347 DeviceType_HardDisk,
348 hardDisk);
349 if (NS_FAILED(rc))
350 {
351 printf("Error: could not attach hard disk! rc=%#x\n", rc);
352 }
353 }
354 }
355 }
356
357 /*
358 * It's got a hard disk but that one is new and thus not bootable. Make it
359 * boot from an ISO file. This requires some processing. First the ISO file
360 * has to be registered and then mounted to the VM's DVD drive and selected
361 * as the boot device.
362 */
363 nsCOMPtr<IMedium> dvdImage;
364 rc = virtualBox->OpenMedium(NS_LITERAL_STRING("/home/vbox/isos/winnt4ger.iso").get(),
365 DeviceType_DVD,
366 AccessMode_ReadOnly,
367 false /* fForceNewUuid */,
368 getter_AddRefs(dvdImage));
369 if (NS_FAILED(rc))
370 printf("Error: could not open CD image! rc=%#x\n", rc);
371 else
372 {
373 /*
374 * Now assign it to our VM
375 */
376 rc = sessionMachine->MountMedium(
377 NS_LITERAL_STRING("IDE Controller").get(), // controller identifier
378 2, // channel number on the controller
379 0, // device number on the controller
380 dvdImage,
381 PR_FALSE); // aForce
382 if (NS_FAILED(rc))
383 {
384 printf("Error: could not mount ISO image! rc=%#x\n", rc);
385 }
386 else
387 {
388 /*
389 * Last step: tell the VM to boot from the CD.
390 */
391 rc = sessionMachine->SetBootOrder(1, DeviceType::DVD);
392 if (NS_FAILED(rc))
393 {
394 printf("Could not set boot device! rc=%#x\n", rc);
395 }
396 }
397 }
398
399 /*
400 * Save all changes we've just made.
401 */
402 rc = sessionMachine->SaveSettings();
403 if (NS_FAILED(rc))
404 printf("Could not save machine settings! rc=%#x\n", rc);
405
406 /*
407 * It is always important to close the open session when it becomes not
408 * necessary any more.
409 */
410 session->UnlockMachine();
411
412 IMedium **aMedia;
413 PRUint32 cMedia;
414 rc = machine->Unregister((CleanupMode_T)CleanupMode_DetachAllReturnHardDisksOnly,
415 &cMedia, &aMedia);
416 if (NS_FAILED(rc))
417 printf("Unregistering the machine failed! rc=%#x\n", rc);
418 else
419 {
420 nsCOMPtr<IProgress> pProgress;
421 rc = machine->DeleteConfig(cMedia, aMedia, getter_AddRefs(pProgress));
422 if (NS_FAILED(rc))
423 printf("Deleting of machine failed! rc=%#x\n", rc);
424 else
425 {
426 rc = pProgress->WaitForCompletion(-1);
427 PRInt32 resultCode;
428 pProgress->GetResultCode(&resultCode);
429 if (NS_FAILED(rc) || NS_FAILED(resultCode))
430 printf("Failed to delete the machine! rc=%#x\n",
431 NS_FAILED(rc) ? rc : resultCode);
432 }
433
434 /* Release the media array: */
435 for (PRUint32 i = 0; i < cMedia; i++)
436 if (aMedia[i])
437 aMedia[i]->Release();
438 nsMemory::Free(aMedia);
439 }
440}
441
442// main
443///////////////////////////////////////////////////////////////////////////////
444
445int main(int argc, char **argv)
446{
447 /*
448 * Check that PRUnichar is equal in size to what compiler composes L""
449 * strings from; otherwise NS_LITERAL_STRING macros won't work correctly
450 * and we will get a meaningless SIGSEGV. This, of course, must be checked
451 * at compile time in xpcom/string/nsTDependentString.h, but XPCOM lacks
452 * compile-time assert macros and I'm not going to add them now.
453 */
454 if (sizeof(PRUnichar) != sizeof(wchar_t))
455 {
456 printf("Error: sizeof(PRUnichar) {%lu} != sizeof(wchar_t) {%lu}!\n"
457 "Probably, you forgot the -fshort-wchar compiler option.\n",
458 (unsigned long) sizeof(PRUnichar),
459 (unsigned long) sizeof(wchar_t));
460 return -1;
461 }
462
463#if 1 /* Please ignore this! It is very very crude. */
464# ifdef RTPATH_APP_PRIVATE_ARCH
465 if (!getenv("VBOX_XPCOM_HOME"))
466 setenv("VBOX_XPCOM_HOME", RTPATH_APP_PRIVATE_ARCH, 1);
467# else
468 char szTmp[8192];
469 if (!getenv("VBOX_XPCOM_HOME"))
470 {
471 strcpy(szTmp, argv[0]);
472 *strrchr(szTmp, '/') = '\0';
473 strcat(szTmp, "/..");
474 fprintf(stderr, "tstVBoxAPIXPCOM: VBOX_XPCOM_HOME is not set, using '%s' instead\n", szTmp);
475 setenv("VBOX_XPCOM_HOME", szTmp, 1);
476 }
477# endif
478#endif
479 (void)argc; (void)argv;
480
481 nsresult rc;
482
483 /*
484 * This is the standard XPCOM init procedure.
485 * What we do is just follow the required steps to get an instance
486 * of our main interface, which is IVirtualBox.
487 *
488 * Note that we scope all nsCOMPtr variables in order to have all XPCOM
489 * objects automatically released before we call NS_ShutdownXPCOM at the
490 * end. This is an XPCOM requirement.
491 */
492 {
493 nsCOMPtr<nsIServiceManager> serviceManager;
494 rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), nsnull, nsnull);
495 if (NS_FAILED(rc))
496 {
497 printf("Error: XPCOM could not be initialized! rc=%#x\n", rc);
498 return -1;
499 }
500
501#if 0
502 /*
503 * Register our components. This step is only necessary if this executable
504 * implements XPCOM components itself which is not the case for this
505 * simple example.
506 */
507 nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(serviceManager);
508 if (!registrar)
509 {
510 printf("Error: could not query nsIComponentRegistrar interface!\n");
511 return -1;
512 }
513 registrar->AutoRegister(nsnull);
514#endif
515
516 /*
517 * Make sure the main event queue is created. This event queue is
518 * responsible for dispatching incoming XPCOM IPC messages. The main
519 * thread should run this event queue's loop during lengthy non-XPCOM
520 * operations to ensure messages from the VirtualBox server and other
521 * XPCOM IPC clients are processed. This use case doesn't perform such
522 * operations so it doesn't run the event loop.
523 */
524 nsCOMPtr<nsIEventQueue> eventQ;
525 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
526 if (NS_FAILED(rc))
527 {
528 printf("Error: could not get main event queue! rc=%#x\n", rc);
529 return -1;
530 }
531
532 /*
533 * Now XPCOM is ready and we can start to do real work.
534 * IVirtualBox is the root interface of VirtualBox and will be
535 * retrieved from the XPCOM component manager. We use the
536 * XPCOM provided smart pointer nsCOMPtr for all objects because
537 * that's very convenient and removes the need deal with reference
538 * counting and freeing.
539 */
540 nsCOMPtr<nsIComponentManager> manager;
541 rc = NS_GetComponentManager(getter_AddRefs(manager));
542 if (NS_FAILED(rc))
543 {
544 printf("Error: could not get component manager! rc=%#x\n", rc);
545 return -1;
546 }
547
548 nsCOMPtr<IVirtualBox> virtualBox;
549 rc = manager->CreateInstanceByContractID(NS_VIRTUALBOX_CONTRACTID,
550 nsnull,
551 NS_GET_IID(IVirtualBox),
552 getter_AddRefs(virtualBox));
553 if (NS_FAILED(rc))
554 {
555 printf("Error, could not instantiate VirtualBox object! rc=%#x\n", rc);
556 return -1;
557 }
558 printf("VirtualBox object created\n");
559
560 ////////////////////////////////////////////////////////////////////////////////
561 ////////////////////////////////////////////////////////////////////////////////
562 ////////////////////////////////////////////////////////////////////////////////
563
564
565 listVMs(virtualBox);
566
567 createVM(virtualBox);
568
569
570 ////////////////////////////////////////////////////////////////////////////////
571 ////////////////////////////////////////////////////////////////////////////////
572 ////////////////////////////////////////////////////////////////////////////////
573
574 /* this is enough to free the IVirtualBox instance -- smart pointers rule! */
575 virtualBox = nsnull;
576
577 /*
578 * Process events that might have queued up in the XPCOM event
579 * queue. If we don't process them, the server might hang.
580 */
581 eventQ->ProcessPendingEvents();
582 }
583
584 /*
585 * Perform the standard XPCOM shutdown procedure.
586 */
587 NS_ShutdownXPCOM(nsnull);
588 printf("Done!\n");
589 return 0;
590}
591
592
593//////////////////////////////////////////////////////////////////////////////////////////////////////
594//// Helpers
595//////////////////////////////////////////////////////////////////////////////////////////////////////
596
597/**
598 * Helper function to convert an nsID into a human readable string
599 *
600 * @returns result string, allocated. Has to be freed using free()
601 * @param guid Pointer to nsID that will be converted.
602 */
603char *nsIDToString(nsID *guid)
604{
605 char *res = (char*)malloc(39);
606
607 if (res != NULL)
608 {
609 snprintf(res, 39, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
610 guid->m0, (PRUint32)guid->m1, (PRUint32)guid->m2,
611 (PRUint32)guid->m3[0], (PRUint32)guid->m3[1], (PRUint32)guid->m3[2],
612 (PRUint32)guid->m3[3], (PRUint32)guid->m3[4], (PRUint32)guid->m3[5],
613 (PRUint32)guid->m3[6], (PRUint32)guid->m3[7]);
614 }
615 return res;
616}
617
618/**
619 * Helper function to print XPCOM exception information set on the current
620 * thread after a failed XPCOM method call. This function will also print
621 * extended VirtualBox error info if it is available.
622 */
623void printErrorInfo()
624{
625 nsresult rc;
626
627 nsCOMPtr<nsIExceptionService> es;
628 es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
629 if (NS_SUCCEEDED(rc))
630 {
631 nsCOMPtr<nsIExceptionManager> em;
632 rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
633 if (NS_SUCCEEDED(rc))
634 {
635 nsCOMPtr<nsIException> ex;
636 rc = em->GetCurrentException(getter_AddRefs(ex));
637 if (NS_SUCCEEDED(rc) && ex)
638 {
639 nsCOMPtr<IVirtualBoxErrorInfo> info;
640 info = do_QueryInterface(ex, &rc);
641 if (NS_SUCCEEDED(rc) && info)
642 {
643 /* got extended error info */
644 printf("Extended error info (IVirtualBoxErrorInfo):\n");
645 PRInt32 resultCode = NS_OK;
646 info->GetResultCode(&resultCode);
647 printf(" resultCode=%08X\n", resultCode);
648 nsXPIDLString component;
649 info->GetComponent(getter_Copies(component));
650 printf(" component=%s\n", NS_ConvertUTF16toUTF8(component).get());
651 nsXPIDLString text;
652 info->GetText(getter_Copies(text));
653 printf(" text=%s\n", NS_ConvertUTF16toUTF8(text).get());
654 }
655 else
656 {
657 /* got basic error info */
658 printf("Basic error info (nsIException):\n");
659 nsresult resultCode = NS_OK;
660 ex->GetResult(&resultCode);
661 printf(" resultCode=%08X\n", resultCode);
662 nsXPIDLCString message;
663 ex->GetMessage(getter_Copies(message));
664 printf(" message=%s\n", message.get());
665 }
666
667 /* reset the exception to NULL to indicate we've processed it */
668 em->SetCurrentException(NULL);
669
670 rc = NS_OK;
671 }
672 }
673 }
674}
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