VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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