VirtualBox

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

Last change on this file since 51574 was 50339, checked in by vboxsync, 11 years ago

Main/testcase: beat some sense into the XPCOM sample code, it was meant to be standalone, and actually was anything but separately compilable due to using IPRT. Fixed the template which is used for building the code, so that this will not slip through again, letting it be so unusable for a long time.

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