VirtualBox

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

Last change on this file since 86616 was 86616, checked in by vboxsync, 4 years ago

Main/tstVBoxAPIXPCOM.cpp: Must get VBOX_XPCOM_HOME right as loading two different VBoxRT.so files just leads to utter confusion (one from /opt/virtualbox/ and one from whereever the testcase was put). Would assert with a duplicate vfs chain reigstration. bugref:9841

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.7 KB
Line 
1/* $Id: tstVBoxAPIXPCOM.cpp 86616 2020-10-16 18:00:58Z 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-2020 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 getter_AddRefs(machine));
194 if (NS_FAILED(rc))
195 {
196 printf("Error: could not create machine! rc=%#x\n", rc);
197 return;
198 }
199
200 /*
201 * Set some properties
202 */
203 /* alternative to illustrate the use of string classes */
204 rc = machine->SetName(NS_ConvertUTF8toUTF16("A new name").get());
205 rc = machine->SetMemorySize(128);
206
207 /*
208 * Now a more advanced property -- the guest OS type. This is
209 * an object by itself which has to be found first. Note that we
210 * use the ID of the guest OS type here which is an internal
211 * representation (you can find that by configuring the OS type of
212 * a machine in the GUI and then looking at the <Guest ostype=""/>
213 * setting in the XML file. It is also possible to get the OS type from
214 * its description (win2k would be "Windows 2000") by getting the
215 * guest OS type collection and enumerating it.
216 */
217 nsCOMPtr<IGuestOSType> osType;
218 rc = virtualBox->GetGuestOSType(NS_LITERAL_STRING("Windows2000").get(),
219 getter_AddRefs(osType));
220 if (NS_FAILED(rc))
221 {
222 printf("Error: could not find guest OS type! rc=%#x\n", rc);
223 }
224 else
225 {
226 machine->SetOSTypeId(NS_LITERAL_STRING("Windows2000").get());
227 }
228
229 /*
230 * Register the VM. Note that this call also saves the VM config
231 * to disk. It is also possible to save the VM settings but not
232 * register the VM.
233 *
234 * Also note that due to current VirtualBox limitations, the machine
235 * must be registered *before* we can attach hard disks to it.
236 */
237 rc = virtualBox->RegisterMachine(machine);
238 if (NS_FAILED(rc))
239 {
240 printf("Error: could not register machine! rc=%#x\n", rc);
241 printErrorInfo();
242 return;
243 }
244
245 nsCOMPtr<IMachine> origMachine = machine;
246
247 /*
248 * In order to manipulate the registered machine, we must open a session
249 * for that machine. Do it now.
250 */
251 nsCOMPtr<ISession> session;
252 nsCOMPtr<IMachine> sessionMachine;
253 {
254 nsCOMPtr<nsIComponentManager> manager;
255 rc = NS_GetComponentManager(getter_AddRefs(manager));
256 if (NS_FAILED(rc))
257 {
258 printf("Error: could not get component manager! rc=%#x\n", rc);
259 return;
260 }
261 rc = manager->CreateInstanceByContractID(NS_SESSION_CONTRACTID,
262 nsnull,
263 NS_GET_IID(ISession),
264 getter_AddRefs(session));
265 if (NS_FAILED(rc))
266 {
267 printf("Error, could not instantiate session object! rc=%#x\n", rc);
268 return;
269 }
270
271 rc = machine->LockMachine(session, LockType_Write);
272 if (NS_FAILED(rc))
273 {
274 printf("Error, could not lock the machine for the session! rc=%#x\n", rc);
275 return;
276 }
277
278 /*
279 * After the machine is registered, the initial machine object becomes
280 * immutable. In order to get a mutable machine object, we must query
281 * it from the opened session object.
282 */
283 rc = session->GetMachine(getter_AddRefs(sessionMachine));
284 if (NS_FAILED(rc))
285 {
286 printf("Error, could not get machine session! rc=%#x\n", rc);
287 return;
288 }
289 }
290
291 /*
292 * Create a virtual harddisk
293 */
294 nsCOMPtr<IMedium> hardDisk = 0;
295 rc = virtualBox->CreateMedium(NS_LITERAL_STRING("VDI").get(),
296 NS_LITERAL_STRING("/tmp/TestHardDisk.vdi").get(),
297 AccessMode_ReadWrite, DeviceType_HardDisk,
298 getter_AddRefs(hardDisk));
299 if (NS_FAILED(rc))
300 {
301 printf("Failed creating a hard disk object! rc=%#x\n", rc);
302 }
303 else
304 {
305 /*
306 * We have only created an object so far. No on disk representation exists
307 * because none of its properties has been set so far. Let's continue creating
308 * a dynamically expanding image.
309 */
310 nsCOMPtr<IProgress> progress;
311 MediumVariant_T mediumVariants[] =
312 { MediumVariant_Standard };
313 rc = hardDisk->CreateBaseStorage(100 * 1024 * 1024, // size in bytes
314 sizeof(mediumVariants) / sizeof(mediumVariants[0]), mediumVariants,
315 getter_AddRefs(progress)); // optional progress object
316 if (NS_FAILED(rc))
317 {
318 printf("Failed creating hard disk image! rc=%#x\n", rc);
319 }
320 else
321 {
322 /*
323 * Creating the image is done in the background because it can take quite
324 * some time (at least fixed size images). We have to wait for its completion.
325 * Here we wait forever (timeout -1) which is potentially dangerous.
326 */
327 rc = progress->WaitForCompletion(-1);
328 PRInt32 resultCode;
329 progress->GetResultCode(&resultCode);
330 if (NS_FAILED(rc) || NS_FAILED(resultCode))
331 {
332 printf("Error: could not create hard disk! rc=%#x\n",
333 NS_FAILED(rc) ? rc : resultCode);
334 }
335 else
336 {
337 /*
338 * Now that it's created, we can assign it to the VM.
339 */
340 rc = sessionMachine->AttachDevice(
341 NS_LITERAL_STRING("IDE Controller").get(), // controller identifier
342 0, // channel number on the controller
343 0, // device number on the controller
344 DeviceType_HardDisk,
345 hardDisk);
346 if (NS_FAILED(rc))
347 {
348 printf("Error: could not attach hard disk! rc=%#x\n", rc);
349 }
350 }
351 }
352 }
353
354 /*
355 * It's got a hard disk but that one is new and thus not bootable. Make it
356 * boot from an ISO file. This requires some processing. First the ISO file
357 * has to be registered and then mounted to the VM's DVD drive and selected
358 * as the boot device.
359 */
360 nsCOMPtr<IMedium> dvdImage;
361 rc = virtualBox->OpenMedium(NS_LITERAL_STRING("/home/vbox/isos/winnt4ger.iso").get(),
362 DeviceType_DVD,
363 AccessMode_ReadOnly,
364 false /* fForceNewUuid */,
365 getter_AddRefs(dvdImage));
366 if (NS_FAILED(rc))
367 printf("Error: could not open CD image! rc=%#x\n", rc);
368 else
369 {
370 /*
371 * Now assign it to our VM
372 */
373 rc = sessionMachine->MountMedium(
374 NS_LITERAL_STRING("IDE Controller").get(), // controller identifier
375 2, // channel number on the controller
376 0, // device number on the controller
377 dvdImage,
378 PR_FALSE); // aForce
379 if (NS_FAILED(rc))
380 {
381 printf("Error: could not mount ISO image! rc=%#x\n", rc);
382 }
383 else
384 {
385 /*
386 * Last step: tell the VM to boot from the CD.
387 */
388 rc = sessionMachine->SetBootOrder(1, DeviceType::DVD);
389 if (NS_FAILED(rc))
390 {
391 printf("Could not set boot device! rc=%#x\n", rc);
392 }
393 }
394 }
395
396 /*
397 * Save all changes we've just made.
398 */
399 rc = sessionMachine->SaveSettings();
400 if (NS_FAILED(rc))
401 printf("Could not save machine settings! rc=%#x\n", rc);
402
403 /*
404 * It is always important to close the open session when it becomes not
405 * necessary any more.
406 */
407 session->UnlockMachine();
408
409 IMedium **aMedia;
410 PRUint32 cMedia;
411 rc = machine->Unregister((CleanupMode_T)CleanupMode_DetachAllReturnHardDisksOnly,
412 &cMedia, &aMedia);
413 if (NS_FAILED(rc))
414 printf("Unregistering the machine failed! rc=%#x\n", rc);
415 else
416 {
417 nsCOMPtr<IProgress> pProgress;
418 rc = machine->DeleteConfig(cMedia, aMedia, getter_AddRefs(pProgress));
419 if (NS_FAILED(rc))
420 printf("Deleting of machine failed! rc=%#x\n", rc);
421 else
422 {
423 rc = pProgress->WaitForCompletion(-1);
424 PRInt32 resultCode;
425 pProgress->GetResultCode(&resultCode);
426 if (NS_FAILED(rc) || NS_FAILED(resultCode))
427 printf("Failed to delete the machine! rc=%#x\n",
428 NS_FAILED(rc) ? rc : resultCode);
429 }
430 }
431}
432
433// main
434///////////////////////////////////////////////////////////////////////////////
435
436int main(int argc, char **argv)
437{
438 /*
439 * Check that PRUnichar is equal in size to what compiler composes L""
440 * strings from; otherwise NS_LITERAL_STRING macros won't work correctly
441 * and we will get a meaningless SIGSEGV. This, of course, must be checked
442 * at compile time in xpcom/string/nsTDependentString.h, but XPCOM lacks
443 * compile-time assert macros and I'm not going to add them now.
444 */
445 if (sizeof(PRUnichar) != sizeof(wchar_t))
446 {
447 printf("Error: sizeof(PRUnichar) {%lu} != sizeof(wchar_t) {%lu}!\n"
448 "Probably, you forgot the -fshort-wchar compiler option.\n",
449 (unsigned long) sizeof(PRUnichar),
450 (unsigned long) sizeof(wchar_t));
451 return -1;
452 }
453
454#if 1 /* Please ignore this! It is very very crude. */
455# ifdef RTPATH_APP_PRIVATE_ARCH
456 if (!getenv("VBOX_XPCOM_HOME"))
457 setenv("VBOX_XPCOM_HOME", RTPATH_APP_PRIVATE_ARCH, 1);
458# else
459 char szTmp[8192];
460 if (!getenv("VBOX_XPCOM_HOME"))
461 {
462 strcpy(szTmp, argv[0]);
463 *strrchr(szTmp, '/') = '\0';
464 strcat(szTmp, "/..");
465 fprintf(stderr, "tstVBoxAPIXPCOM: VBOX_XPCOM_HOME is not set, using '%s' instead\n", szTmp);
466 setenv("VBOX_XPCOM_HOME", szTmp, 1);
467 }
468# endif
469#endif
470 (void)argc; (void)argv;
471
472 nsresult rc;
473
474 /*
475 * This is the standard XPCOM init procedure.
476 * What we do is just follow the required steps to get an instance
477 * of our main interface, which is IVirtualBox.
478 *
479 * Note that we scope all nsCOMPtr variables in order to have all XPCOM
480 * objects automatically released before we call NS_ShutdownXPCOM at the
481 * end. This is an XPCOM requirement.
482 */
483 {
484 nsCOMPtr<nsIServiceManager> serviceManager;
485 rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), nsnull, nsnull);
486 if (NS_FAILED(rc))
487 {
488 printf("Error: XPCOM could not be initialized! rc=%#x\n", rc);
489 return -1;
490 }
491
492#if 0
493 /*
494 * Register our components. This step is only necessary if this executable
495 * implements XPCOM components itself which is not the case for this
496 * simple example.
497 */
498 nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(serviceManager);
499 if (!registrar)
500 {
501 printf("Error: could not query nsIComponentRegistrar interface!\n");
502 return -1;
503 }
504 registrar->AutoRegister(nsnull);
505#endif
506
507 /*
508 * Make sure the main event queue is created. This event queue is
509 * responsible for dispatching incoming XPCOM IPC messages. The main
510 * thread should run this event queue's loop during lengthy non-XPCOM
511 * operations to ensure messages from the VirtualBox server and other
512 * XPCOM IPC clients are processed. This use case doesn't perform such
513 * operations so it doesn't run the event loop.
514 */
515 nsCOMPtr<nsIEventQueue> eventQ;
516 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
517 if (NS_FAILED(rc))
518 {
519 printf("Error: could not get main event queue! rc=%#x\n", rc);
520 return -1;
521 }
522
523 /*
524 * Now XPCOM is ready and we can start to do real work.
525 * IVirtualBox is the root interface of VirtualBox and will be
526 * retrieved from the XPCOM component manager. We use the
527 * XPCOM provided smart pointer nsCOMPtr for all objects because
528 * that's very convenient and removes the need deal with reference
529 * counting and freeing.
530 */
531 nsCOMPtr<nsIComponentManager> manager;
532 rc = NS_GetComponentManager(getter_AddRefs(manager));
533 if (NS_FAILED(rc))
534 {
535 printf("Error: could not get component manager! rc=%#x\n", rc);
536 return -1;
537 }
538
539 nsCOMPtr<IVirtualBox> virtualBox;
540 rc = manager->CreateInstanceByContractID(NS_VIRTUALBOX_CONTRACTID,
541 nsnull,
542 NS_GET_IID(IVirtualBox),
543 getter_AddRefs(virtualBox));
544 if (NS_FAILED(rc))
545 {
546 printf("Error, could not instantiate VirtualBox object! rc=%#x\n", rc);
547 return -1;
548 }
549 printf("VirtualBox object created\n");
550
551 ////////////////////////////////////////////////////////////////////////////////
552 ////////////////////////////////////////////////////////////////////////////////
553 ////////////////////////////////////////////////////////////////////////////////
554
555
556 listVMs(virtualBox);
557
558 createVM(virtualBox);
559
560
561 ////////////////////////////////////////////////////////////////////////////////
562 ////////////////////////////////////////////////////////////////////////////////
563 ////////////////////////////////////////////////////////////////////////////////
564
565 /* this is enough to free the IVirtualBox instance -- smart pointers rule! */
566 virtualBox = nsnull;
567
568 /*
569 * Process events that might have queued up in the XPCOM event
570 * queue. If we don't process them, the server might hang.
571 */
572 eventQ->ProcessPendingEvents();
573 }
574
575 /*
576 * Perform the standard XPCOM shutdown procedure.
577 */
578 NS_ShutdownXPCOM(nsnull);
579 printf("Done!\n");
580 return 0;
581}
582
583
584//////////////////////////////////////////////////////////////////////////////////////////////////////
585//// Helpers
586//////////////////////////////////////////////////////////////////////////////////////////////////////
587
588/**
589 * Helper function to convert an nsID into a human readable string
590 *
591 * @returns result string, allocated. Has to be freed using free()
592 * @param guid Pointer to nsID that will be converted.
593 */
594char *nsIDToString(nsID *guid)
595{
596 char *res = (char*)malloc(39);
597
598 if (res != NULL)
599 {
600 snprintf(res, 39, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
601 guid->m0, (PRUint32)guid->m1, (PRUint32)guid->m2,
602 (PRUint32)guid->m3[0], (PRUint32)guid->m3[1], (PRUint32)guid->m3[2],
603 (PRUint32)guid->m3[3], (PRUint32)guid->m3[4], (PRUint32)guid->m3[5],
604 (PRUint32)guid->m3[6], (PRUint32)guid->m3[7]);
605 }
606 return res;
607}
608
609/**
610 * Helper function to print XPCOM exception information set on the current
611 * thread after a failed XPCOM method call. This function will also print
612 * extended VirtualBox error info if it is available.
613 */
614void printErrorInfo()
615{
616 nsresult rc;
617
618 nsCOMPtr<nsIExceptionService> es;
619 es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
620 if (NS_SUCCEEDED(rc))
621 {
622 nsCOMPtr<nsIExceptionManager> em;
623 rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
624 if (NS_SUCCEEDED(rc))
625 {
626 nsCOMPtr<nsIException> ex;
627 rc = em->GetCurrentException(getter_AddRefs(ex));
628 if (NS_SUCCEEDED(rc) && ex)
629 {
630 nsCOMPtr<IVirtualBoxErrorInfo> info;
631 info = do_QueryInterface(ex, &rc);
632 if (NS_SUCCEEDED(rc) && info)
633 {
634 /* got extended error info */
635 printf("Extended error info (IVirtualBoxErrorInfo):\n");
636 PRInt32 resultCode = NS_OK;
637 info->GetResultCode(&resultCode);
638 printf(" resultCode=%08X\n", resultCode);
639 nsXPIDLString component;
640 info->GetComponent(getter_Copies(component));
641 printf(" component=%s\n", NS_ConvertUTF16toUTF8(component).get());
642 nsXPIDLString text;
643 info->GetText(getter_Copies(text));
644 printf(" text=%s\n", NS_ConvertUTF16toUTF8(text).get());
645 }
646 else
647 {
648 /* got basic error info */
649 printf("Basic error info (nsIException):\n");
650 nsresult resultCode = NS_OK;
651 ex->GetResult(&resultCode);
652 printf(" resultCode=%08X\n", resultCode);
653 nsXPIDLCString message;
654 ex->GetMessage(getter_Copies(message));
655 printf(" message=%s\n", message.get());
656 }
657
658 /* reset the exception to NULL to indicate we've processed it */
659 em->SetCurrentException(NULL);
660
661 rc = NS_OK;
662 }
663 }
664 }
665}
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