VirtualBox

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

Last change on this file since 100841 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

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