VirtualBox

source: vbox/trunk/src/VBox/Main/ProgressImpl.cpp@ 15051

Last change on this file since 15051 was 15051, checked in by vboxsync, 16 years ago

Main: Cleaned up the long standing const BSTR = const (OLECHAR *) on WIn32 vs (const PRunichar) * on XPCOM clash. Cleaned up BSTR/GUID macros (IN_BSTR replaces INPTR BSTR, IN_GUID replaces INPTR GUIDPARAM, OUT_GUID replaces GUIDPARAMOUT).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.9 KB
Line 
1/* $Id: ProgressImpl.cpp 15051 2008-12-05 17:20:00Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox COM class implementation
5 */
6
7/*
8 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <iprt/types.h>
24
25#if defined (VBOX_WITH_XPCOM)
26#include <nsIServiceManager.h>
27#include <nsIExceptionService.h>
28#include <nsCOMPtr.h>
29#endif /* defined (VBOX_WITH_XPCOM) */
30
31#include "ProgressImpl.h"
32
33#include "VirtualBoxImpl.h"
34#include "VirtualBoxErrorInfoImpl.h"
35
36#include "Logging.h"
37
38#include <iprt/time.h>
39#include <iprt/semaphore.h>
40
41#include <VBox/err.h>
42
43////////////////////////////////////////////////////////////////////////////////
44// ProgressBase class
45////////////////////////////////////////////////////////////////////////////////
46
47// constructor / destructor
48////////////////////////////////////////////////////////////////////////////////
49
50DEFINE_EMPTY_CTOR_DTOR (ProgressBase)
51
52/**
53 * Subclasses must call this method from their FinalConstruct() implementations.
54 */
55HRESULT ProgressBase::FinalConstruct()
56{
57 mCancelable = FALSE;
58 mCompleted = FALSE;
59 mCanceled = FALSE;
60 mResultCode = S_OK;
61 mOperationCount = 0;
62 mOperation = 0;
63 mOperationPercent = 0;
64
65 return S_OK;
66}
67
68// protected initializer/uninitializer for internal purposes only
69////////////////////////////////////////////////////////////////////////////////
70
71/**
72 * Initializes the progress base object.
73 *
74 * Subclasses should call this or any other #protectedInit() method from their
75 * init() implementations.
76 *
77 * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass.
78 * @param aParent Parent object (only for server-side Progress objects).
79 * @param aInitiator Initiator of the task (for server-side objects. Can be
80 * NULL which means initiator = parent, otherwise must not
81 * be NULL).
82 * @param aDescription Task description.
83 * @param aID Address of result GUID structure (optional).
84 *
85 * @return COM result indicator.
86 */
87HRESULT ProgressBase::protectedInit (AutoInitSpan &aAutoInitSpan,
88#if !defined (VBOX_COM_INPROC)
89 VirtualBox *aParent,
90#endif
91 IUnknown *aInitiator,
92 CBSTR aDescription, OUT_GUID aId /* = NULL */)
93{
94 /* Guarantees subclasses call this method at the proper time */
95 NOREF (aAutoInitSpan);
96
97 AutoCaller autoCaller (this);
98 AssertReturn (autoCaller.state() == InInit, E_FAIL);
99
100#if !defined (VBOX_COM_INPROC)
101 AssertReturn (aParent, E_INVALIDARG);
102#else
103 AssertReturn (aInitiator, E_INVALIDARG);
104#endif
105
106 AssertReturn (aDescription, E_INVALIDARG);
107
108#if !defined (VBOX_COM_INPROC)
109 /* share parent weakly */
110 unconst (mParent) = aParent;
111
112 /* register with parent early, since uninit() will unconditionally
113 * unregister on failure */
114 mParent->addDependentChild (this);
115#endif
116
117#if !defined (VBOX_COM_INPROC)
118 /* assign (and therefore addref) initiator only if it is not VirtualBox
119 * (to avoid cycling); otherwise mInitiator will remain null which means
120 * that it is the same as the parent */
121 if (aInitiator && !mParent.equalsTo (aInitiator))
122 unconst (mInitiator) = aInitiator;
123#else
124 unconst (mInitiator) = aInitiator;
125#endif
126
127 unconst (mId).create();
128 if (aId)
129 mId.cloneTo (aId);
130
131#if !defined (VBOX_COM_INPROC)
132 /* add to the global colleciton of progess operations (note: after
133 * creating mId) */
134 mParent->addProgress (this);
135#endif
136
137 unconst (mDescription) = aDescription;
138
139 return S_OK;
140}
141
142/**
143 * Initializes the progress base object.
144 *
145 * This is a special initializer that doesn't initialize any field. Used by one
146 * of the Progress::init() forms to create sub-progress operations combined
147 * together using a CombinedProgress instance, so it doesn't require the parent,
148 * initiator, description and doesn't create an ID.
149 *
150 * Subclasses should call this or any other #protectedInit() method from their
151 * init() implementations.
152 *
153 * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass.
154 */
155HRESULT ProgressBase::protectedInit (AutoInitSpan &aAutoInitSpan)
156{
157 /* Guarantees subclasses call this method at the proper time */
158 NOREF (aAutoInitSpan);
159
160 return S_OK;
161}
162
163/**
164 * Uninitializes the instance.
165 *
166 * Subclasses should call this from their uninit() implementations.
167 *
168 * @param aAutoUninitSpan AutoUninitSpan object instantiated by a subclass.
169 *
170 * @note Using the mParent member after this method returns is forbidden.
171 */
172void ProgressBase::protectedUninit (AutoUninitSpan &aAutoUninitSpan)
173{
174 /* release initiator (effective only if mInitiator has been assigned in
175 * init()) */
176 unconst (mInitiator).setNull();
177
178#if !defined (VBOX_COM_INPROC)
179 if (mParent)
180 {
181 /* remove the added progress on failure to complete the initialization */
182 if (aAutoUninitSpan.initFailed() && !mId.isEmpty())
183 mParent->removeProgress (mId);
184
185 mParent->removeDependentChild (this);
186
187 unconst (mParent).setNull();
188 }
189#endif
190}
191
192// IProgress properties
193/////////////////////////////////////////////////////////////////////////////
194
195STDMETHODIMP ProgressBase::COMGETTER(Id) (OUT_GUID aId)
196{
197 CheckComArgOutPointerValid(aId);
198
199 AutoCaller autoCaller (this);
200 CheckComRCReturnRC (autoCaller.rc());
201
202 /* mId is constant during life time, no need to lock */
203 mId.cloneTo (aId);
204
205 return S_OK;
206}
207
208STDMETHODIMP ProgressBase::COMGETTER(Description) (BSTR *aDescription)
209{
210 CheckComArgOutPointerValid(aDescription);
211
212 AutoCaller autoCaller (this);
213 CheckComRCReturnRC (autoCaller.rc());
214
215 /* mDescription is constant during life time, no need to lock */
216 mDescription.cloneTo (aDescription);
217
218 return S_OK;
219}
220
221STDMETHODIMP ProgressBase::COMGETTER(Initiator) (IUnknown **aInitiator)
222{
223 CheckComArgOutPointerValid(aInitiator);
224
225 AutoCaller autoCaller (this);
226 CheckComRCReturnRC (autoCaller.rc());
227
228 /* mInitiator/mParent are constant during life time, no need to lock */
229
230#if !defined (VBOX_COM_INPROC)
231 if (mInitiator)
232 mInitiator.queryInterfaceTo (aInitiator);
233 else
234 mParent.queryInterfaceTo (aInitiator);
235#else
236 mInitiator.queryInterfaceTo (aInitiator);
237#endif
238
239 return S_OK;
240}
241
242STDMETHODIMP ProgressBase::COMGETTER(Cancelable) (BOOL *aCancelable)
243{
244 CheckComArgOutPointerValid(aCancelable);
245
246 AutoCaller autoCaller (this);
247 CheckComRCReturnRC (autoCaller.rc());
248
249 AutoReadLock alock (this);
250
251 *aCancelable = mCancelable;
252
253 return S_OK;
254}
255
256STDMETHODIMP ProgressBase::COMGETTER(Percent) (LONG *aPercent)
257{
258 CheckComArgOutPointerValid(aPercent);
259
260 AutoCaller autoCaller (this);
261 CheckComRCReturnRC (autoCaller.rc());
262
263 AutoReadLock alock (this);
264
265 if (mCompleted && SUCCEEDED (mResultCode))
266 *aPercent = 100;
267 else
268 {
269 /* global percent =
270 * (100 / mOperationCount) * mOperation +
271 * ((100 / mOperationCount) / 100) * mOperationPercent */
272 *aPercent = (100 * mOperation + mOperationPercent) / mOperationCount;
273 }
274
275 return S_OK;
276}
277
278STDMETHODIMP ProgressBase::COMGETTER(Completed) (BOOL *aCompleted)
279{
280 CheckComArgOutPointerValid(aCompleted);
281
282 AutoCaller autoCaller (this);
283 CheckComRCReturnRC (autoCaller.rc());
284
285 AutoReadLock alock (this);
286
287 *aCompleted = mCompleted;
288
289 return S_OK;
290}
291
292STDMETHODIMP ProgressBase::COMGETTER(Canceled) (BOOL *aCanceled)
293{
294 CheckComArgOutPointerValid(aCanceled);
295
296 AutoCaller autoCaller (this);
297 CheckComRCReturnRC (autoCaller.rc());
298
299 AutoReadLock alock (this);
300
301 *aCanceled = mCanceled;
302
303 return S_OK;
304}
305
306STDMETHODIMP ProgressBase::COMGETTER(ResultCode) (HRESULT *aResultCode)
307{
308 CheckComArgOutPointerValid(aResultCode);
309
310 AutoCaller autoCaller (this);
311 CheckComRCReturnRC (autoCaller.rc());
312
313 AutoReadLock alock (this);
314
315 if (!mCompleted)
316 return setError (E_FAIL,
317 tr ("Result code is not available, operation is still in progress"));
318
319 *aResultCode = mResultCode;
320
321 return S_OK;
322}
323
324STDMETHODIMP ProgressBase::COMGETTER(ErrorInfo) (IVirtualBoxErrorInfo **aErrorInfo)
325{
326 CheckComArgOutPointerValid(aErrorInfo);
327
328 AutoCaller autoCaller (this);
329 CheckComRCReturnRC (autoCaller.rc());
330
331 AutoReadLock alock (this);
332
333 if (!mCompleted)
334 return setError (E_FAIL,
335 tr ("Error info is not available, operation is still in progress"));
336
337 mErrorInfo.queryInterfaceTo (aErrorInfo);
338
339 return S_OK;
340}
341
342STDMETHODIMP ProgressBase::COMGETTER(OperationCount) (ULONG *aOperationCount)
343{
344 CheckComArgOutPointerValid(aOperationCount);
345
346 AutoCaller autoCaller (this);
347 CheckComRCReturnRC (autoCaller.rc());
348
349 AutoReadLock alock (this);
350
351 *aOperationCount = mOperationCount;
352
353 return S_OK;
354}
355
356STDMETHODIMP ProgressBase::COMGETTER(Operation) (ULONG *aOperation)
357{
358 CheckComArgOutPointerValid(aOperation);
359
360 AutoCaller autoCaller (this);
361 CheckComRCReturnRC (autoCaller.rc());
362
363 AutoReadLock alock (this);
364
365 *aOperation = mOperation;
366
367 return S_OK;
368}
369
370STDMETHODIMP ProgressBase::COMGETTER(OperationDescription) (BSTR *aOperationDescription)
371{
372 CheckComArgOutPointerValid(aOperationDescription);
373
374 AutoCaller autoCaller (this);
375 CheckComRCReturnRC (autoCaller.rc());
376
377 AutoReadLock alock (this);
378
379 mOperationDescription.cloneTo (aOperationDescription);
380
381 return S_OK;
382}
383
384STDMETHODIMP ProgressBase::COMGETTER(OperationPercent) (LONG *aOperationPercent)
385{
386 CheckComArgOutPointerValid(aOperationPercent);
387
388 AutoCaller autoCaller (this);
389 CheckComRCReturnRC (autoCaller.rc());
390
391 AutoReadLock alock (this);
392
393 if (mCompleted && SUCCEEDED (mResultCode))
394 *aOperationPercent = 100;
395 else
396 *aOperationPercent = mOperationPercent;
397
398 return S_OK;
399}
400
401// public methods only for internal purposes
402////////////////////////////////////////////////////////////////////////////////
403
404/**
405 * Sets the error info stored in the given progress object as the error info on
406 * the current thread.
407 *
408 * This method is useful if some other COM method uses IProgress to wait for
409 * something and then wants to return a failed result of the operation it was
410 * waiting for as its own result retaining the extended error info.
411 *
412 * If the operation tracked by this progress object is completed successfully
413 * and returned S_OK, this method does nothing but returns S_OK. Otherwise, the
414 * failed warning or error result code specified at progress completion is
415 * returned and the extended error info object (if any) is set on the current
416 * thread.
417 *
418 * Note that the given progress object must be completed, otherwise this method
419 * will assert and fail.
420 */
421/* static */
422HRESULT ProgressBase::setErrorInfoOnThread (IProgress *aProgress)
423{
424 AssertReturn (aProgress != NULL, E_INVALIDARG);
425
426 HRESULT resultCode;
427 HRESULT rc = aProgress->COMGETTER(ResultCode) (&resultCode);
428 AssertComRCReturnRC (rc);
429
430 if (resultCode == S_OK)
431 return resultCode;
432
433 ComPtr <IVirtualBoxErrorInfo> errorInfo;
434 rc = aProgress->COMGETTER(ErrorInfo) (errorInfo.asOutParam());
435 AssertComRCReturnRC (rc);
436
437 if (!errorInfo.isNull())
438 setErrorInfo (errorInfo);
439
440 return resultCode;
441}
442
443////////////////////////////////////////////////////////////////////////////////
444// Progress class
445////////////////////////////////////////////////////////////////////////////////
446
447HRESULT Progress::FinalConstruct()
448{
449 HRESULT rc = ProgressBase::FinalConstruct();
450 CheckComRCReturnRC (rc);
451
452 mCompletedSem = NIL_RTSEMEVENTMULTI;
453 mWaitersCount = 0;
454
455 return S_OK;
456}
457
458void Progress::FinalRelease()
459{
460 uninit();
461}
462
463// public initializer/uninitializer for internal purposes only
464////////////////////////////////////////////////////////////////////////////////
465
466/**
467 * Initializes the normal progress object.
468 *
469 * @param aParent See ProgressBase::init().
470 * @param aInitiator See ProgressBase::init().
471 * @param aDescription See ProgressBase::init().
472 * @param aCancelable Flag whether the task maybe canceled.
473 * @param aOperationCount Number of operations within this task (at least 1).
474 * @param aOperationDescription Description of the first operation.
475 * @param aId See ProgressBase::init().
476 */
477HRESULT Progress::init (
478#if !defined (VBOX_COM_INPROC)
479 VirtualBox *aParent,
480#endif
481 IUnknown *aInitiator,
482 CBSTR aDescription, BOOL aCancelable,
483 ULONG aOperationCount, CBSTR aOperationDescription,
484 OUT_GUID aId /* = NULL */)
485{
486 LogFlowThisFunc (("aDescription=\"%ls\"\n", aDescription));
487
488 AssertReturn (aOperationDescription, E_INVALIDARG);
489 AssertReturn (aOperationCount >= 1, E_INVALIDARG);
490
491 /* Enclose the state transition NotReady->InInit->Ready */
492 AutoInitSpan autoInitSpan (this);
493 AssertReturn (autoInitSpan.isOk(), E_FAIL);
494
495 HRESULT rc = S_OK;
496
497 rc = ProgressBase::protectedInit (autoInitSpan,
498#if !defined (VBOX_COM_INPROC)
499 aParent,
500#endif
501 aInitiator, aDescription, aId);
502 CheckComRCReturnRC (rc);
503
504 mCancelable = aCancelable;
505
506 mOperationCount = aOperationCount;
507 mOperation = 0; /* the first operation */
508 mOperationDescription = aOperationDescription;
509
510 int vrc = RTSemEventMultiCreate (&mCompletedSem);
511 ComAssertRCRet (vrc, E_FAIL);
512
513 RTSemEventMultiReset (mCompletedSem);
514
515 /* Confirm a successful initialization when it's the case */
516 if (SUCCEEDED (rc))
517 autoInitSpan.setSucceeded();
518
519 return rc;
520}
521
522/**
523 * Initializes the sub-progress object that represents a specific operation of
524 * the whole task.
525 *
526 * Objects initialized with this method are then combined together into the
527 * single task using a CombinedProgress instance, so it doesn't require the
528 * parent, initiator, description and doesn't create an ID. Note that calling
529 * respective getter methods on an object initialized with this method is
530 * useless. Such objects are used only to provide a separate wait semaphore and
531 * store individual operation descriptions.
532 *
533 * @param aCancelable Flag whether the task maybe canceled.
534 * @param aOperationCount Number of sub-operations within this task (at least 1).
535 * @param aOperationDescription Description of the individual operation.
536 */
537HRESULT Progress::init (BOOL aCancelable, ULONG aOperationCount,
538 CBSTR aOperationDescription)
539{
540 LogFlowThisFunc (("aOperationDescription=\"%ls\"\n", aOperationDescription));
541
542 /* Enclose the state transition NotReady->InInit->Ready */
543 AutoInitSpan autoInitSpan (this);
544 AssertReturn (autoInitSpan.isOk(), E_FAIL);
545
546 HRESULT rc = S_OK;
547
548 rc = ProgressBase::protectedInit (autoInitSpan);
549 CheckComRCReturnRC (rc);
550
551 mCancelable = aCancelable;
552
553 mOperationCount = aOperationCount;
554 mOperation = 0; /* the first operation */
555 mOperationDescription = aOperationDescription;
556
557 int vrc = RTSemEventMultiCreate (&mCompletedSem);
558 ComAssertRCRet (vrc, E_FAIL);
559
560 RTSemEventMultiReset (mCompletedSem);
561
562 /* Confirm a successful initialization when it's the case */
563 if (SUCCEEDED (rc))
564 autoInitSpan.setSucceeded();
565
566 return rc;
567}
568
569/**
570 * Uninitializes the instance and sets the ready flag to FALSE.
571 *
572 * Called either from FinalRelease() or by the parent when it gets destroyed.
573 */
574void Progress::uninit()
575{
576 LogFlowThisFunc (("\n"));
577
578 /* Enclose the state transition Ready->InUninit->NotReady */
579 AutoUninitSpan autoUninitSpan (this);
580 if (autoUninitSpan.uninitDone())
581 return;
582
583 /* wake up all threads still waiting on occasion */
584 if (mWaitersCount > 0)
585 {
586 LogFlow (("WARNING: There are still %d threads waiting for '%ls' completion!\n",
587 mWaitersCount, mDescription.raw()));
588 RTSemEventMultiSignal (mCompletedSem);
589 }
590
591 RTSemEventMultiDestroy (mCompletedSem);
592
593 ProgressBase::protectedUninit (autoUninitSpan);
594}
595
596// IProgress properties
597/////////////////////////////////////////////////////////////////////////////
598
599// IProgress methods
600/////////////////////////////////////////////////////////////////////////////
601
602/**
603 * @note XPCOM: when this method is called not on the main XPCOM thread, it it
604 * simply blocks the thread until mCompletedSem is signalled. If the
605 * thread has its own event queue (hmm, what for?) that it must run, then
606 * calling this method will definitey freese event processing.
607 */
608STDMETHODIMP Progress::WaitForCompletion (LONG aTimeout)
609{
610 LogFlowThisFuncEnter();
611 LogFlowThisFunc (("aTimeout=%d\n", aTimeout));
612
613 AutoCaller autoCaller (this);
614 CheckComRCReturnRC (autoCaller.rc());
615
616 AutoWriteLock alock (this);
617
618 /* if we're already completed, take a shortcut */
619 if (!mCompleted)
620 {
621 RTTIMESPEC time;
622 RTTimeNow (&time);
623
624 int vrc = VINF_SUCCESS;
625 bool forever = aTimeout < 0;
626 int64_t timeLeft = aTimeout;
627 int64_t lastTime = RTTimeSpecGetMilli (&time);
628
629 while (!mCompleted && (forever || timeLeft > 0))
630 {
631 mWaitersCount ++;
632 alock.leave();
633 int vrc = RTSemEventMultiWait (mCompletedSem,
634 forever ? RT_INDEFINITE_WAIT
635 : (unsigned) timeLeft);
636 alock.enter();
637 mWaitersCount --;
638
639 /* the last waiter resets the semaphore */
640 if (mWaitersCount == 0)
641 RTSemEventMultiReset (mCompletedSem);
642
643 if (RT_FAILURE (vrc) && vrc != VERR_TIMEOUT)
644 break;
645
646 if (!forever)
647 {
648 RTTimeNow (&time);
649 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
650 lastTime = RTTimeSpecGetMilli (&time);
651 }
652 }
653
654 if (RT_FAILURE (vrc) && vrc != VERR_TIMEOUT)
655 return setError (E_FAIL,
656 tr ("Failed to wait for the task completion (%Rrc)"), vrc);
657 }
658
659 LogFlowThisFuncLeave();
660
661 return S_OK;
662}
663
664/**
665 * @note XPCOM: when this method is called not on the main XPCOM thread, it it
666 * simply blocks the thread until mCompletedSem is signalled. If the
667 * thread has its own event queue (hmm, what for?) that it must run, then
668 * calling this method will definitey freese event processing.
669 */
670STDMETHODIMP Progress::WaitForOperationCompletion (ULONG aOperation, LONG aTimeout)
671{
672 LogFlowThisFuncEnter();
673 LogFlowThisFunc (("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
674
675 AutoCaller autoCaller (this);
676 CheckComRCReturnRC (autoCaller.rc());
677
678 AutoWriteLock alock (this);
679
680 if (aOperation >= mOperationCount)
681 return setError (E_FAIL,
682 tr ("Operation number must be in range [0, %d]"), mOperation - 1);
683
684 /* if we're already completed or if the given operation is already done,
685 * then take a shortcut */
686 if (!mCompleted && aOperation >= mOperation)
687 {
688 RTTIMESPEC time;
689 RTTimeNow (&time);
690
691 int vrc = VINF_SUCCESS;
692 bool forever = aTimeout < 0;
693 int64_t timeLeft = aTimeout;
694 int64_t lastTime = RTTimeSpecGetMilli (&time);
695
696 while (!mCompleted && aOperation >= mOperation &&
697 (forever || timeLeft > 0))
698 {
699 mWaitersCount ++;
700 alock.leave();
701 int vrc = RTSemEventMultiWait (mCompletedSem,
702 forever ? RT_INDEFINITE_WAIT
703 : (unsigned) timeLeft);
704 alock.enter();
705 mWaitersCount --;
706
707 /* the last waiter resets the semaphore */
708 if (mWaitersCount == 0)
709 RTSemEventMultiReset (mCompletedSem);
710
711 if (RT_FAILURE (vrc) && vrc != VERR_TIMEOUT)
712 break;
713
714 if (!forever)
715 {
716 RTTimeNow (&time);
717 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
718 lastTime = RTTimeSpecGetMilli (&time);
719 }
720 }
721
722 if (RT_FAILURE (vrc) && vrc != VERR_TIMEOUT)
723 return setError (E_FAIL,
724 tr ("Failed to wait for the operation completion (%Rrc)"), vrc);
725 }
726
727 LogFlowThisFuncLeave();
728
729 return S_OK;
730}
731
732STDMETHODIMP Progress::Cancel()
733{
734 AutoCaller autoCaller (this);
735 CheckComRCReturnRC (autoCaller.rc());
736
737 AutoWriteLock alock (this);
738
739 if (!mCancelable)
740 return setError (E_FAIL, tr ("Operation cannot be canceled"));
741
742/// @todo (dmik): implement operation cancellation!
743// mCompleted = TRUE;
744// mCanceled = TRUE;
745// return S_OK;
746
747 ComAssertMsgFailed (("Not implemented!"));
748 ReturnComNotImplemented();
749}
750
751// public methods only for internal purposes
752/////////////////////////////////////////////////////////////////////////////
753
754/**
755 * Updates the percentage value of the current operation.
756 *
757 * @param aPercent New percentage value of the operation in progress
758 * (in range [0, 100]).
759 */
760HRESULT Progress::notifyProgress (LONG aPercent)
761{
762 AutoCaller autoCaller (this);
763 AssertComRCReturnRC (autoCaller.rc());
764
765 AutoWriteLock alock (this);
766
767 AssertReturn (!mCompleted && !mCanceled, E_FAIL);
768 AssertReturn (aPercent >= 0 && aPercent <= 100, E_INVALIDARG);
769
770 mOperationPercent = aPercent;
771
772 return S_OK;
773}
774
775/**
776 * Signals that the current operation is successfully completed and advances to
777 * the next operation. The operation percentage is reset to 0.
778 *
779 * @param aOperationDescription Description of the next operation.
780 *
781 * @note The current operation must not be the last one.
782 */
783HRESULT Progress::advanceOperation (CBSTR aOperationDescription)
784{
785 AssertReturn (aOperationDescription, E_INVALIDARG);
786
787 AutoCaller autoCaller (this);
788 AssertComRCReturnRC (autoCaller.rc());
789
790 AutoWriteLock alock (this);
791
792 AssertReturn (!mCompleted && !mCanceled, E_FAIL);
793 AssertReturn (mOperation + 1 < mOperationCount, E_FAIL);
794
795 mOperation ++;
796 mOperationDescription = aOperationDescription;
797 mOperationPercent = 0;
798
799 /* wake up all waiting threads */
800 if (mWaitersCount > 0)
801 RTSemEventMultiSignal (mCompletedSem);
802
803 return S_OK;
804}
805
806/**
807 * Marks the whole task as complete and sets the result code.
808 *
809 * If the result code indicates a failure (|FAILED (@a aResultCode)|) then this
810 * method will import the error info from the current thread and assign it to
811 * the errorInfo attribute (it will return an error if no info is available in
812 * such case).
813 *
814 * If the result code indicates a success (|SUCCEEDED (@a aResultCode)|) then
815 * the current operation is set to the last.
816 *
817 * Note that this method may be called only once for the given Progress object.
818 * Subsequent calls will assert.
819 *
820 * @param aResultCode Operation result code.
821 */
822HRESULT Progress::notifyComplete (HRESULT aResultCode)
823{
824 AutoCaller autoCaller (this);
825 AssertComRCReturnRC (autoCaller.rc());
826
827 AutoWriteLock alock (this);
828
829 AssertReturn (mCompleted == FALSE, E_FAIL);
830
831 mCompleted = TRUE;
832 mResultCode = aResultCode;
833
834 HRESULT rc = S_OK;
835
836 if (FAILED (aResultCode))
837 {
838 /* try to import error info from the current thread */
839
840#if !defined (VBOX_WITH_XPCOM)
841
842 ComPtr <IErrorInfo> err;
843 rc = ::GetErrorInfo (0, err.asOutParam());
844 if (rc == S_OK && err)
845 {
846 rc = err.queryInterfaceTo (mErrorInfo.asOutParam());
847 if (SUCCEEDED (rc) && !mErrorInfo)
848 rc = E_FAIL;
849 }
850
851#else /* !defined (VBOX_WITH_XPCOM) */
852
853 nsCOMPtr <nsIExceptionService> es;
854 es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
855 if (NS_SUCCEEDED (rc))
856 {
857 nsCOMPtr <nsIExceptionManager> em;
858 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
859 if (NS_SUCCEEDED (rc))
860 {
861 ComPtr <nsIException> ex;
862 rc = em->GetCurrentException (ex.asOutParam());
863 if (NS_SUCCEEDED (rc) && ex)
864 {
865 rc = ex.queryInterfaceTo (mErrorInfo.asOutParam());
866 if (NS_SUCCEEDED (rc) && !mErrorInfo)
867 rc = E_FAIL;
868 }
869 }
870 }
871#endif /* !defined (VBOX_WITH_XPCOM) */
872
873 AssertMsg (rc == S_OK, ("Couldn't get error info (rc=%08X) while trying "
874 "to set a failed result (%08X)!\n", rc, aResultCode));
875 }
876 else
877 {
878 mOperation = mOperationCount - 1; /* last operation */
879 mOperationPercent = 100;
880 }
881
882#if !defined VBOX_COM_INPROC
883 /* remove from the global collection of pending progress operations */
884 if (mParent)
885 mParent->removeProgress (mId);
886#endif
887
888 /* wake up all waiting threads */
889 if (mWaitersCount > 0)
890 RTSemEventMultiSignal (mCompletedSem);
891
892 return rc;
893}
894
895/**
896 * Marks the operation as complete and attaches full error info.
897 *
898 * See com::SupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t
899 * *, const char *, ...) for more info.
900 *
901 * @param aResultCode Operation result (error) code, must not be S_OK.
902 * @param aIID IID of the intrface that defines the error.
903 * @param aComponent Name of the component that generates the error.
904 * @param aText Error message (must not be null), an RTStrPrintf-like
905 * format string in UTF-8 encoding.
906 * @param ... List of arguments for the format string.
907 */
908HRESULT Progress::notifyComplete (HRESULT aResultCode, const GUID &aIID,
909 const Bstr &aComponent,
910 const char *aText, ...)
911{
912 va_list args;
913 va_start (args, aText);
914 Bstr text = Utf8StrFmtVA (aText, args);
915 va_end (args);
916
917 return notifyCompleteBstr (aResultCode, aIID, aComponent, text);
918}
919
920/**
921 * Marks the operation as complete and attaches full error info.
922 *
923 * See com::SupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t
924 * *, const char *, ...) for more info.
925 *
926 * This method is preferred iy you have a ready (translated and formatted) Bstr
927 * string, because it omits an extra conversion Utf8Str -> Bstr.
928 *
929 * @param aResultCode Operation result (error) code, must not be S_OK.
930 * @param aIID IID of the intrface that defines the error.
931 * @param aComponent Name of the component that generates the error.
932 * @param aText Error message (must not be null).
933 */
934HRESULT Progress::notifyCompleteBstr (HRESULT aResultCode, const GUID &aIID,
935 const Bstr &aComponent, const Bstr &aText)
936{
937 AutoCaller autoCaller (this);
938 AssertComRCReturnRC (autoCaller.rc());
939
940 AutoWriteLock alock (this);
941
942 mCompleted = TRUE;
943 mResultCode = aResultCode;
944
945 AssertReturn (FAILED (aResultCode), E_FAIL);
946
947 ComObjPtr <VirtualBoxErrorInfo> errorInfo;
948 HRESULT rc = errorInfo.createObject();
949 AssertComRC (rc);
950 if (SUCCEEDED (rc))
951 {
952 errorInfo->init (aResultCode, aIID, aComponent, aText);
953 errorInfo.queryInterfaceTo (mErrorInfo.asOutParam());
954 }
955
956#if !defined VBOX_COM_INPROC
957 /* remove from the global collection of pending progress operations */
958 if (mParent)
959 mParent->removeProgress (mId);
960#endif
961
962 /* wake up all waiting threads */
963 if (mWaitersCount > 0)
964 RTSemEventMultiSignal (mCompletedSem);
965
966 return rc;
967}
968
969////////////////////////////////////////////////////////////////////////////////
970// CombinedProgress class
971////////////////////////////////////////////////////////////////////////////////
972
973HRESULT CombinedProgress::FinalConstruct()
974{
975 HRESULT rc = ProgressBase::FinalConstruct();
976 CheckComRCReturnRC (rc);
977
978 mProgress = 0;
979 mCompletedOperations = 0;
980
981 return S_OK;
982}
983
984void CombinedProgress::FinalRelease()
985{
986 uninit();
987}
988
989// public initializer/uninitializer for internal purposes only
990////////////////////////////////////////////////////////////////////////////////
991
992/**
993 * Initializes this object based on individual combined progresses.
994 * Must be called only from #init()!
995 *
996 * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass.
997 * @param aParent See ProgressBase::init().
998 * @param aInitiator See ProgressBase::init().
999 * @param aDescription See ProgressBase::init().
1000 * @param aId See ProgressBase::init().
1001 */
1002HRESULT CombinedProgress::protectedInit (AutoInitSpan &aAutoInitSpan,
1003#if !defined (VBOX_COM_INPROC)
1004 VirtualBox *aParent,
1005#endif
1006 IUnknown *aInitiator,
1007 CBSTR aDescription, OUT_GUID aId)
1008{
1009 LogFlowThisFunc (("aDescription={%ls} mProgresses.size()=%d\n",
1010 aDescription, mProgresses.size()));
1011
1012 HRESULT rc = S_OK;
1013
1014 rc = ProgressBase::protectedInit (aAutoInitSpan,
1015#if !defined (VBOX_COM_INPROC)
1016 aParent,
1017#endif
1018 aInitiator, aDescription, aId);
1019 CheckComRCReturnRC (rc);
1020
1021 mProgress = 0; /* the first object */
1022 mCompletedOperations = 0;
1023
1024 mCompleted = FALSE;
1025 mCancelable = TRUE; /* until any progress returns FALSE */
1026 mCanceled = FALSE;
1027
1028 mOperationCount = 0; /* will be calculated later */
1029
1030 mOperation = 0;
1031 rc = mProgresses [0]->COMGETTER(OperationDescription) (
1032 mOperationDescription.asOutParam());
1033 CheckComRCReturnRC (rc);
1034
1035 for (size_t i = 0; i < mProgresses.size(); i ++)
1036 {
1037 if (mCancelable)
1038 {
1039 BOOL cancelable = FALSE;
1040 rc = mProgresses [i]->COMGETTER(Cancelable) (&cancelable);
1041 CheckComRCReturnRC (rc);
1042
1043 if (!cancelable)
1044 mCancelable = FALSE;
1045 }
1046
1047 {
1048 ULONG opCount = 0;
1049 rc = mProgresses [i]->COMGETTER(OperationCount) (&opCount);
1050 CheckComRCReturnRC (rc);
1051
1052 mOperationCount += opCount;
1053 }
1054 }
1055
1056 rc = checkProgress();
1057 CheckComRCReturnRC (rc);
1058
1059 return rc;
1060}
1061
1062/**
1063 * Initializes the combined progress object given two normal progress
1064 * objects.
1065 *
1066 * @param aParent See ProgressBase::init().
1067 * @param aInitiator See ProgressBase::init().
1068 * @param aDescription See ProgressBase::init().
1069 * @param aProgress1 First normal progress object.
1070 * @param aProgress2 Second normal progress object.
1071 * @param aId See ProgressBase::init().
1072 */
1073HRESULT CombinedProgress::init (
1074#if !defined (VBOX_COM_INPROC)
1075 VirtualBox *aParent,
1076#endif
1077 IUnknown *aInitiator,
1078 CBSTR aDescription,
1079 IProgress *aProgress1, IProgress *aProgress2,
1080 OUT_GUID aId /* = NULL */)
1081{
1082 /* Enclose the state transition NotReady->InInit->Ready */
1083 AutoInitSpan autoInitSpan (this);
1084 AssertReturn (autoInitSpan.isOk(), E_FAIL);
1085
1086 mProgresses.resize (2);
1087 mProgresses [0] = aProgress1;
1088 mProgresses [1] = aProgress2;
1089
1090 HRESULT rc = protectedInit (autoInitSpan,
1091#if !defined (VBOX_COM_INPROC)
1092 aParent,
1093#endif
1094 aInitiator, aDescription, aId);
1095
1096 /* Confirm a successful initialization when it's the case */
1097 if (SUCCEEDED (rc))
1098 autoInitSpan.setSucceeded();
1099
1100 return rc;
1101}
1102
1103/**
1104 * Uninitializes the instance and sets the ready flag to FALSE.
1105 *
1106 * Called either from FinalRelease() or by the parent when it gets destroyed.
1107 */
1108void CombinedProgress::uninit()
1109{
1110 LogFlowThisFunc (("\n"));
1111
1112 /* Enclose the state transition Ready->InUninit->NotReady */
1113 AutoUninitSpan autoUninitSpan (this);
1114 if (autoUninitSpan.uninitDone())
1115 return;
1116
1117 mProgress = 0;
1118 mProgresses.clear();
1119
1120 ProgressBase::protectedUninit (autoUninitSpan);
1121}
1122
1123// IProgress properties
1124////////////////////////////////////////////////////////////////////////////////
1125
1126STDMETHODIMP CombinedProgress::COMGETTER(Percent) (LONG *aPercent)
1127{
1128 CheckComArgOutPointerValid(aPercent);
1129
1130 AutoCaller autoCaller (this);
1131 CheckComRCReturnRC (autoCaller.rc());
1132
1133 /* checkProgress needs a write lock */
1134 AutoWriteLock alock (this);
1135
1136 if (mCompleted && SUCCEEDED (mResultCode))
1137 *aPercent = 100;
1138 else
1139 {
1140 HRESULT rc = checkProgress();
1141 CheckComRCReturnRC (rc);
1142
1143 /* global percent =
1144 * (100 / mOperationCount) * mOperation +
1145 * ((100 / mOperationCount) / 100) * mOperationPercent */
1146 *aPercent = (100 * mOperation + mOperationPercent) / mOperationCount;
1147 }
1148
1149 return S_OK;
1150}
1151
1152STDMETHODIMP CombinedProgress::COMGETTER(Completed) (BOOL *aCompleted)
1153{
1154 CheckComArgOutPointerValid(aCompleted);
1155
1156 AutoCaller autoCaller (this);
1157 CheckComRCReturnRC (autoCaller.rc());
1158
1159 /* checkProgress needs a write lock */
1160 AutoWriteLock alock (this);
1161
1162 HRESULT rc = checkProgress();
1163 CheckComRCReturnRC (rc);
1164
1165 return ProgressBase::COMGETTER(Completed) (aCompleted);
1166}
1167
1168STDMETHODIMP CombinedProgress::COMGETTER(Canceled) (BOOL *aCanceled)
1169{
1170 CheckComArgOutPointerValid(aCanceled);
1171
1172 AutoCaller autoCaller (this);
1173 CheckComRCReturnRC (autoCaller.rc());
1174
1175 /* checkProgress needs a write lock */
1176 AutoWriteLock alock (this);
1177
1178 HRESULT rc = checkProgress();
1179 CheckComRCReturnRC (rc);
1180
1181 return ProgressBase::COMGETTER(Canceled) (aCanceled);
1182}
1183
1184STDMETHODIMP CombinedProgress::COMGETTER(ResultCode) (HRESULT *aResultCode)
1185{
1186 CheckComArgOutPointerValid(aResultCode);
1187
1188 AutoCaller autoCaller (this);
1189 CheckComRCReturnRC (autoCaller.rc());
1190
1191 /* checkProgress needs a write lock */
1192 AutoWriteLock alock (this);
1193
1194 HRESULT rc = checkProgress();
1195 CheckComRCReturnRC (rc);
1196
1197 return ProgressBase::COMGETTER(ResultCode) (aResultCode);
1198}
1199
1200STDMETHODIMP CombinedProgress::COMGETTER(ErrorInfo) (IVirtualBoxErrorInfo **aErrorInfo)
1201{
1202 CheckComArgOutPointerValid(aErrorInfo);
1203
1204 AutoCaller autoCaller (this);
1205 CheckComRCReturnRC (autoCaller.rc());
1206
1207 /* checkProgress needs a write lock */
1208 AutoWriteLock alock (this);
1209
1210 HRESULT rc = checkProgress();
1211 CheckComRCReturnRC (rc);
1212
1213 return ProgressBase::COMGETTER(ErrorInfo) (aErrorInfo);
1214}
1215
1216STDMETHODIMP CombinedProgress::COMGETTER(Operation) (ULONG *aOperation)
1217{
1218 CheckComArgOutPointerValid(aOperation);
1219
1220 AutoCaller autoCaller (this);
1221 CheckComRCReturnRC (autoCaller.rc());
1222
1223 /* checkProgress needs a write lock */
1224 AutoWriteLock alock (this);
1225
1226 HRESULT rc = checkProgress();
1227 CheckComRCReturnRC (rc);
1228
1229 return ProgressBase::COMGETTER(Operation) (aOperation);
1230}
1231
1232STDMETHODIMP CombinedProgress::COMGETTER(OperationDescription) (BSTR *aOperationDescription)
1233{
1234 CheckComArgOutPointerValid(aOperationDescription);
1235
1236 AutoCaller autoCaller (this);
1237 CheckComRCReturnRC (autoCaller.rc());
1238
1239 /* checkProgress needs a write lock */
1240 AutoWriteLock alock (this);
1241
1242 HRESULT rc = checkProgress();
1243 CheckComRCReturnRC (rc);
1244
1245 return ProgressBase::COMGETTER(OperationDescription) (aOperationDescription);
1246}
1247
1248STDMETHODIMP CombinedProgress::COMGETTER(OperationPercent) (LONG *aOperationPercent)
1249{
1250 CheckComArgOutPointerValid(aOperationPercent);
1251
1252 AutoCaller autoCaller (this);
1253 CheckComRCReturnRC (autoCaller.rc());
1254
1255 /* checkProgress needs a write lock */
1256 AutoWriteLock alock (this);
1257
1258 HRESULT rc = checkProgress();
1259 CheckComRCReturnRC (rc);
1260
1261 return ProgressBase::COMGETTER(OperationPercent) (aOperationPercent);
1262}
1263
1264// IProgress methods
1265/////////////////////////////////////////////////////////////////////////////
1266
1267/**
1268 * @note XPCOM: when this method is called not on the main XPCOM thread, it it
1269 * simply blocks the thread until mCompletedSem is signalled. If the
1270 * thread has its own event queue (hmm, what for?) that it must run, then
1271 * calling this method will definitey freese event processing.
1272 */
1273STDMETHODIMP CombinedProgress::WaitForCompletion (LONG aTimeout)
1274{
1275 LogFlowThisFuncEnter();
1276 LogFlowThisFunc (("aTtimeout=%d\n", aTimeout));
1277
1278 AutoCaller autoCaller (this);
1279 CheckComRCReturnRC (autoCaller.rc());
1280
1281 AutoWriteLock alock (this);
1282
1283 /* if we're already completed, take a shortcut */
1284 if (!mCompleted)
1285 {
1286 RTTIMESPEC time;
1287 RTTimeNow (&time);
1288
1289 HRESULT rc = S_OK;
1290 bool forever = aTimeout < 0;
1291 int64_t timeLeft = aTimeout;
1292 int64_t lastTime = RTTimeSpecGetMilli (&time);
1293
1294 while (!mCompleted && (forever || timeLeft > 0))
1295 {
1296 alock.leave();
1297 rc = mProgresses.back()->WaitForCompletion (
1298 forever ? -1 : (LONG) timeLeft);
1299 alock.enter();
1300
1301 if (SUCCEEDED (rc))
1302 rc = checkProgress();
1303
1304 CheckComRCBreakRC (rc);
1305
1306 if (!forever)
1307 {
1308 RTTimeNow (&time);
1309 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
1310 lastTime = RTTimeSpecGetMilli (&time);
1311 }
1312 }
1313
1314 CheckComRCReturnRC (rc);
1315 }
1316
1317 LogFlowThisFuncLeave();
1318
1319 return S_OK;
1320}
1321
1322/**
1323 * @note XPCOM: when this method is called not on the main XPCOM thread, it it
1324 * simply blocks the thread until mCompletedSem is signalled. If the
1325 * thread has its own event queue (hmm, what for?) that it must run, then
1326 * calling this method will definitey freese event processing.
1327 */
1328STDMETHODIMP CombinedProgress::WaitForOperationCompletion (ULONG aOperation, LONG aTimeout)
1329{
1330 LogFlowThisFuncEnter();
1331 LogFlowThisFunc (("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
1332
1333 AutoCaller autoCaller (this);
1334 CheckComRCReturnRC (autoCaller.rc());
1335
1336 AutoWriteLock alock (this);
1337
1338 if (aOperation >= mOperationCount)
1339 return setError (E_FAIL,
1340 tr ("Operation number must be in range [0, %d]"), mOperation - 1);
1341
1342 /* if we're already completed or if the given operation is already done,
1343 * then take a shortcut */
1344 if (!mCompleted && aOperation >= mOperation)
1345 {
1346 HRESULT rc = S_OK;
1347
1348 /* find the right progress object to wait for */
1349 size_t progress = mProgress;
1350 ULONG operation = 0, completedOps = mCompletedOperations;
1351 do
1352 {
1353 ULONG opCount = 0;
1354 rc = mProgresses [progress]->COMGETTER(OperationCount) (&opCount);
1355 if (FAILED (rc))
1356 return rc;
1357
1358 if (completedOps + opCount > aOperation)
1359 {
1360 /* found the right progress object */
1361 operation = aOperation - completedOps;
1362 break;
1363 }
1364
1365 completedOps += opCount;
1366 progress ++;
1367 ComAssertRet (progress < mProgresses.size(), E_FAIL);
1368 }
1369 while (1);
1370
1371 LogFlowThisFunc (("will wait for mProgresses [%d] (%d)\n",
1372 progress, operation));
1373
1374 RTTIMESPEC time;
1375 RTTimeNow (&time);
1376
1377 bool forever = aTimeout < 0;
1378 int64_t timeLeft = aTimeout;
1379 int64_t lastTime = RTTimeSpecGetMilli (&time);
1380
1381 while (!mCompleted && aOperation >= mOperation &&
1382 (forever || timeLeft > 0))
1383 {
1384 alock.leave();
1385 /* wait for the appropriate progress operation completion */
1386 rc = mProgresses [progress]-> WaitForOperationCompletion (
1387 operation, forever ? -1 : (LONG) timeLeft);
1388 alock.enter();
1389
1390 if (SUCCEEDED (rc))
1391 rc = checkProgress();
1392
1393 CheckComRCBreakRC (rc);
1394
1395 if (!forever)
1396 {
1397 RTTimeNow (&time);
1398 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
1399 lastTime = RTTimeSpecGetMilli (&time);
1400 }
1401 }
1402
1403 CheckComRCReturnRC (rc);
1404 }
1405
1406 LogFlowThisFuncLeave();
1407
1408 return S_OK;
1409}
1410
1411STDMETHODIMP CombinedProgress::Cancel()
1412{
1413 AutoCaller autoCaller (this);
1414 CheckComRCReturnRC (autoCaller.rc());
1415
1416 AutoWriteLock alock (this);
1417
1418 if (!mCancelable)
1419 return setError (E_FAIL, tr ("Operation cannot be cancelled"));
1420
1421/// @todo (dmik): implement operation cancellation!
1422// mCompleted = TRUE;
1423// mCanceled = TRUE;
1424// return S_OK;
1425
1426 ComAssertMsgFailed (("Not implemented!"));
1427 ReturnComNotImplemented();
1428}
1429
1430// private methods
1431////////////////////////////////////////////////////////////////////////////////
1432
1433/**
1434 * Fetches the properties of the current progress object and, if it is
1435 * successfully completed, advances to the next uncompleted or unsucessfully
1436 * completed object in the vector of combined progress objects.
1437 *
1438 * @note Must be called from under this object's write lock!
1439 */
1440HRESULT CombinedProgress::checkProgress()
1441{
1442 /* do nothing if we're already marked ourselves as completed */
1443 if (mCompleted)
1444 return S_OK;
1445
1446 AssertReturn (mProgress < mProgresses.size(), E_FAIL);
1447
1448 ComPtr <IProgress> progress = mProgresses [mProgress];
1449 ComAssertRet (!progress.isNull(), E_FAIL);
1450
1451 HRESULT rc = S_OK;
1452 BOOL completed = FALSE;
1453
1454 do
1455 {
1456 rc = progress->COMGETTER(Completed) (&completed);
1457 if (FAILED (rc))
1458 return rc;
1459
1460 if (completed)
1461 {
1462 rc = progress->COMGETTER(Canceled) (&mCanceled);
1463 if (FAILED (rc))
1464 return rc;
1465
1466 rc = progress->COMGETTER(ResultCode) (&mResultCode);
1467 if (FAILED (rc))
1468 return rc;
1469
1470 if (FAILED (mResultCode))
1471 {
1472 rc = progress->COMGETTER(ErrorInfo) (mErrorInfo.asOutParam());
1473 if (FAILED (rc))
1474 return rc;
1475 }
1476
1477 if (FAILED (mResultCode) || mCanceled)
1478 {
1479 mCompleted = TRUE;
1480 }
1481 else
1482 {
1483 ULONG opCount = 0;
1484 rc = progress->COMGETTER(OperationCount) (&opCount);
1485 if (FAILED (rc))
1486 return rc;
1487
1488 mCompletedOperations += opCount;
1489 mProgress ++;
1490
1491 if (mProgress < mProgresses.size())
1492 progress = mProgresses [mProgress];
1493 else
1494 mCompleted = TRUE;
1495 }
1496 }
1497 }
1498 while (completed && !mCompleted);
1499
1500 rc = progress->COMGETTER(OperationPercent) (&mOperationPercent);
1501 if (SUCCEEDED (rc))
1502 {
1503 ULONG operation = 0;
1504 rc = progress->COMGETTER(Operation) (&operation);
1505 if (SUCCEEDED (rc) && mCompletedOperations + operation > mOperation)
1506 {
1507 mOperation = mCompletedOperations + operation;
1508 rc = progress->COMGETTER(OperationDescription) (
1509 mOperationDescription.asOutParam());
1510 }
1511 }
1512
1513 return rc;
1514}
1515/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use