VirtualBox

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

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

Main/tstVBoxAPIXPCOM.cpp: Fixed another leak, this time the array returned by IMachine->Unregister. bugref:9841

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.9 KB
Line 
1/* $Id: tstVBoxAPIXPCOM.cpp 86617 2020-10-16 22:47:55Z 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 /* Release the media array: */
432 for (PRUint32 i = 0; i < cMedia; i++)
433 if (aMedia[i])
434 aMedia[i]->Release();
435 nsMemory::Free(aMedia);
436 }
437}
438
439// main
440///////////////////////////////////////////////////////////////////////////////
441
442int main(int argc, char **argv)
443{
444 /*
445 * Check that PRUnichar is equal in size to what compiler composes L""
446 * strings from; otherwise NS_LITERAL_STRING macros won't work correctly
447 * and we will get a meaningless SIGSEGV. This, of course, must be checked
448 * at compile time in xpcom/string/nsTDependentString.h, but XPCOM lacks
449 * compile-time assert macros and I'm not going to add them now.
450 */
451 if (sizeof(PRUnichar) != sizeof(wchar_t))
452 {
453 printf("Error: sizeof(PRUnichar) {%lu} != sizeof(wchar_t) {%lu}!\n"
454 "Probably, you forgot the -fshort-wchar compiler option.\n",
455 (unsigned long) sizeof(PRUnichar),
456 (unsigned long) sizeof(wchar_t));
457 return -1;
458 }
459
460#if 1 /* Please ignore this! It is very very crude. */
461# ifdef RTPATH_APP_PRIVATE_ARCH
462 if (!getenv("VBOX_XPCOM_HOME"))
463 setenv("VBOX_XPCOM_HOME", RTPATH_APP_PRIVATE_ARCH, 1);
464# else
465 char szTmp[8192];
466 if (!getenv("VBOX_XPCOM_HOME"))
467 {
468 strcpy(szTmp, argv[0]);
469 *strrchr(szTmp, '/') = '\0';
470 strcat(szTmp, "/..");
471 fprintf(stderr, "tstVBoxAPIXPCOM: VBOX_XPCOM_HOME is not set, using '%s' instead\n", szTmp);
472 setenv("VBOX_XPCOM_HOME", szTmp, 1);
473 }
474# endif
475#endif
476 (void)argc; (void)argv;
477
478 nsresult rc;
479
480 /*
481 * This is the standard XPCOM init procedure.
482 * What we do is just follow the required steps to get an instance
483 * of our main interface, which is IVirtualBox.
484 *
485 * Note that we scope all nsCOMPtr variables in order to have all XPCOM
486 * objects automatically released before we call NS_ShutdownXPCOM at the
487 * end. This is an XPCOM requirement.
488 */
489 {
490 nsCOMPtr<nsIServiceManager> serviceManager;
491 rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), nsnull, nsnull);
492 if (NS_FAILED(rc))
493 {
494 printf("Error: XPCOM could not be initialized! rc=%#x\n", rc);
495 return -1;
496 }
497
498#if 0
499 /*
500 * Register our components. This step is only necessary if this executable
501 * implements XPCOM components itself which is not the case for this
502 * simple example.
503 */
504 nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(serviceManager);
505 if (!registrar)
506 {
507 printf("Error: could not query nsIComponentRegistrar interface!\n");
508 return -1;
509 }
510 registrar->AutoRegister(nsnull);
511#endif
512
513 /*
514 * Make sure the main event queue is created. This event queue is
515 * responsible for dispatching incoming XPCOM IPC messages. The main
516 * thread should run this event queue's loop during lengthy non-XPCOM
517 * operations to ensure messages from the VirtualBox server and other
518 * XPCOM IPC clients are processed. This use case doesn't perform such
519 * operations so it doesn't run the event loop.
520 */
521 nsCOMPtr<nsIEventQueue> eventQ;
522 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
523 if (NS_FAILED(rc))
524 {
525 printf("Error: could not get main event queue! rc=%#x\n", rc);
526 return -1;
527 }
528
529 /*
530 * Now XPCOM is ready and we can start to do real work.
531 * IVirtualBox is the root interface of VirtualBox and will be
532 * retrieved from the XPCOM component manager. We use the
533 * XPCOM provided smart pointer nsCOMPtr for all objects because
534 * that's very convenient and removes the need deal with reference
535 * counting and freeing.
536 */
537 nsCOMPtr<nsIComponentManager> manager;
538 rc = NS_GetComponentManager(getter_AddRefs(manager));
539 if (NS_FAILED(rc))
540 {
541 printf("Error: could not get component manager! rc=%#x\n", rc);
542 return -1;
543 }
544
545 nsCOMPtr<IVirtualBox> virtualBox;
546 rc = manager->CreateInstanceByContractID(NS_VIRTUALBOX_CONTRACTID,
547 nsnull,
548 NS_GET_IID(IVirtualBox),
549 getter_AddRefs(virtualBox));
550 if (NS_FAILED(rc))
551 {
552 printf("Error, could not instantiate VirtualBox object! rc=%#x\n", rc);
553 return -1;
554 }
555 printf("VirtualBox object created\n");
556
557 ////////////////////////////////////////////////////////////////////////////////
558 ////////////////////////////////////////////////////////////////////////////////
559 ////////////////////////////////////////////////////////////////////////////////
560
561
562 listVMs(virtualBox);
563
564 createVM(virtualBox);
565
566
567 ////////////////////////////////////////////////////////////////////////////////
568 ////////////////////////////////////////////////////////////////////////////////
569 ////////////////////////////////////////////////////////////////////////////////
570
571 /* this is enough to free the IVirtualBox instance -- smart pointers rule! */
572 virtualBox = nsnull;
573
574 /*
575 * Process events that might have queued up in the XPCOM event
576 * queue. If we don't process them, the server might hang.
577 */
578 eventQ->ProcessPendingEvents();
579 }
580
581 /*
582 * Perform the standard XPCOM shutdown procedure.
583 */
584 NS_ShutdownXPCOM(nsnull);
585 printf("Done!\n");
586 return 0;
587}
588
589
590//////////////////////////////////////////////////////////////////////////////////////////////////////
591//// Helpers
592//////////////////////////////////////////////////////////////////////////////////////////////////////
593
594/**
595 * Helper function to convert an nsID into a human readable string
596 *
597 * @returns result string, allocated. Has to be freed using free()
598 * @param guid Pointer to nsID that will be converted.
599 */
600char *nsIDToString(nsID *guid)
601{
602 char *res = (char*)malloc(39);
603
604 if (res != NULL)
605 {
606 snprintf(res, 39, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
607 guid->m0, (PRUint32)guid->m1, (PRUint32)guid->m2,
608 (PRUint32)guid->m3[0], (PRUint32)guid->m3[1], (PRUint32)guid->m3[2],
609 (PRUint32)guid->m3[3], (PRUint32)guid->m3[4], (PRUint32)guid->m3[5],
610 (PRUint32)guid->m3[6], (PRUint32)guid->m3[7]);
611 }
612 return res;
613}
614
615/**
616 * Helper function to print XPCOM exception information set on the current
617 * thread after a failed XPCOM method call. This function will also print
618 * extended VirtualBox error info if it is available.
619 */
620void printErrorInfo()
621{
622 nsresult rc;
623
624 nsCOMPtr<nsIExceptionService> es;
625 es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
626 if (NS_SUCCEEDED(rc))
627 {
628 nsCOMPtr<nsIExceptionManager> em;
629 rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
630 if (NS_SUCCEEDED(rc))
631 {
632 nsCOMPtr<nsIException> ex;
633 rc = em->GetCurrentException(getter_AddRefs(ex));
634 if (NS_SUCCEEDED(rc) && ex)
635 {
636 nsCOMPtr<IVirtualBoxErrorInfo> info;
637 info = do_QueryInterface(ex, &rc);
638 if (NS_SUCCEEDED(rc) && info)
639 {
640 /* got extended error info */
641 printf("Extended error info (IVirtualBoxErrorInfo):\n");
642 PRInt32 resultCode = NS_OK;
643 info->GetResultCode(&resultCode);
644 printf(" resultCode=%08X\n", resultCode);
645 nsXPIDLString component;
646 info->GetComponent(getter_Copies(component));
647 printf(" component=%s\n", NS_ConvertUTF16toUTF8(component).get());
648 nsXPIDLString text;
649 info->GetText(getter_Copies(text));
650 printf(" text=%s\n", NS_ConvertUTF16toUTF8(text).get());
651 }
652 else
653 {
654 /* got basic error info */
655 printf("Basic error info (nsIException):\n");
656 nsresult resultCode = NS_OK;
657 ex->GetResult(&resultCode);
658 printf(" resultCode=%08X\n", resultCode);
659 nsXPIDLCString message;
660 ex->GetMessage(getter_Copies(message));
661 printf(" message=%s\n", message.get());
662 }
663
664 /* reset the exception to NULL to indicate we've processed it */
665 em->SetCurrentException(NULL);
666
667 rc = NS_OK;
668 }
669 }
670 }
671}
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