VirtualBox

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

Last change on this file since 18120 was 18120, checked in by vboxsync, 15 years ago

Main/Progress: implement cancelling of activities associated with progress objects

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.8 KB
Line 
1/* $Id: ProgressImpl.cpp 18120 2009-03-20 14:38:43Z 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 : (unsigned) timeLeft);
635 alock.enter();
636 mWaitersCount --;
637
638 /* the last waiter resets the semaphore */
639 if (mWaitersCount == 0)
640 RTSemEventMultiReset (mCompletedSem);
641
642 if (RT_FAILURE (vrc) && vrc != VERR_TIMEOUT)
643 break;
644
645 if (!forever)
646 {
647 RTTimeNow (&time);
648 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
649 lastTime = RTTimeSpecGetMilli (&time);
650 }
651 }
652
653 if (RT_FAILURE (vrc) && vrc != VERR_TIMEOUT)
654 return setError (VBOX_E_IPRT_ERROR,
655 tr ("Failed to wait for the task completion (%Rrc)"), vrc);
656 }
657
658 LogFlowThisFuncLeave();
659
660 return S_OK;
661}
662
663/**
664 * @note XPCOM: when this method is called not on the main XPCOM thread, it it
665 * simply blocks the thread until mCompletedSem is signalled. If the
666 * thread has its own event queue (hmm, what for?) that it must run, then
667 * calling this method will definitey freese event processing.
668 */
669STDMETHODIMP Progress::WaitForOperationCompletion (ULONG aOperation, LONG aTimeout)
670{
671 LogFlowThisFuncEnter();
672 LogFlowThisFunc (("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
673
674 AutoCaller autoCaller (this);
675 CheckComRCReturnRC (autoCaller.rc());
676
677 AutoWriteLock alock (this);
678
679 CheckComArgExpr(aOperation, aOperation < mOperationCount);
680
681 /* if we're already completed or if the given operation is already done,
682 * then take a shortcut */
683 if (!mCompleted && aOperation >= mOperation)
684 {
685 RTTIMESPEC time;
686 RTTimeNow (&time);
687
688 int vrc = VINF_SUCCESS;
689 bool forever = aTimeout < 0;
690 int64_t timeLeft = aTimeout;
691 int64_t lastTime = RTTimeSpecGetMilli (&time);
692
693 while (!mCompleted && aOperation >= mOperation &&
694 (forever || timeLeft > 0))
695 {
696 mWaitersCount ++;
697 alock.leave();
698 int vrc = RTSemEventMultiWait (mCompletedSem,
699 forever ? RT_INDEFINITE_WAIT
700 : (unsigned) timeLeft);
701 alock.enter();
702 mWaitersCount --;
703
704 /* the last waiter resets the semaphore */
705 if (mWaitersCount == 0)
706 RTSemEventMultiReset (mCompletedSem);
707
708 if (RT_FAILURE (vrc) && vrc != VERR_TIMEOUT)
709 break;
710
711 if (!forever)
712 {
713 RTTimeNow (&time);
714 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
715 lastTime = RTTimeSpecGetMilli (&time);
716 }
717 }
718
719 if (RT_FAILURE (vrc) && vrc != VERR_TIMEOUT)
720 return setError (E_FAIL,
721 tr ("Failed to wait for the operation completion (%Rrc)"), vrc);
722 }
723
724 LogFlowThisFuncLeave();
725
726 return S_OK;
727}
728
729STDMETHODIMP Progress::Cancel()
730{
731 AutoCaller autoCaller (this);
732 CheckComRCReturnRC (autoCaller.rc());
733
734 AutoWriteLock alock (this);
735
736 if (!mCancelable)
737 return setError (VBOX_E_INVALID_OBJECT_STATE,
738 tr ("Operation cannot be canceled"));
739
740 mCanceled = TRUE;
741 return S_OK;
742}
743
744// public methods only for internal purposes
745/////////////////////////////////////////////////////////////////////////////
746
747/**
748 * Updates the percentage value of the current operation.
749 *
750 * @param aPercent New percentage value of the operation in progress
751 * (in range [0, 100]).
752 */
753HRESULT Progress::notifyProgress (LONG aPercent)
754{
755 AutoCaller autoCaller (this);
756 AssertComRCReturnRC (autoCaller.rc());
757
758 AutoWriteLock alock (this);
759
760 AssertReturn (aPercent >= 0 && aPercent <= 100, E_INVALIDARG);
761
762 if (mCancelable && mCanceled)
763 {
764 Assert(!mCompleted);
765 return E_FAIL;
766 }
767 else
768 AssertReturn (!mCompleted && !mCanceled, E_FAIL);
769
770
771 mOperationPercent = aPercent;
772
773 return S_OK;
774}
775
776/**
777 * Signals that the current operation is successfully completed and advances to
778 * the next operation. The operation percentage is reset to 0.
779 *
780 * @param aOperationDescription Description of the next operation.
781 *
782 * @note The current operation must not be the last one.
783 */
784HRESULT Progress::advanceOperation (CBSTR aOperationDescription)
785{
786 AssertReturn (aOperationDescription, E_INVALIDARG);
787
788 AutoCaller autoCaller (this);
789 AssertComRCReturnRC (autoCaller.rc());
790
791 AutoWriteLock alock (this);
792
793 AssertReturn (!mCompleted && !mCanceled, E_FAIL);
794 AssertReturn (mOperation + 1 < mOperationCount, E_FAIL);
795
796 mOperation ++;
797 mOperationDescription = aOperationDescription;
798 mOperationPercent = 0;
799
800 /* wake up all waiting threads */
801 if (mWaitersCount > 0)
802 RTSemEventMultiSignal (mCompletedSem);
803
804 return S_OK;
805}
806
807/**
808 * Marks the whole task as complete and sets the result code.
809 *
810 * If the result code indicates a failure (|FAILED (@a aResultCode)|) then this
811 * method will import the error info from the current thread and assign it to
812 * the errorInfo attribute (it will return an error if no info is available in
813 * such case).
814 *
815 * If the result code indicates a success (|SUCCEEDED (@a aResultCode)|) then
816 * the current operation is set to the last.
817 *
818 * Note that this method may be called only once for the given Progress object.
819 * Subsequent calls will assert.
820 *
821 * @param aResultCode Operation result code.
822 */
823HRESULT Progress::notifyComplete (HRESULT aResultCode)
824{
825 AutoCaller autoCaller (this);
826 AssertComRCReturnRC (autoCaller.rc());
827
828 AutoWriteLock alock (this);
829
830 AssertReturn (mCompleted == FALSE, E_FAIL);
831
832 if (mCanceled && SUCCEEDED(aResultCode))
833 aResultCode = E_FAIL;
834
835 mCompleted = TRUE;
836 mResultCode = aResultCode;
837
838 HRESULT rc = S_OK;
839
840 if (FAILED (aResultCode))
841 {
842 /* try to import error info from the current thread */
843
844#if !defined (VBOX_WITH_XPCOM)
845
846 ComPtr <IErrorInfo> err;
847 rc = ::GetErrorInfo (0, err.asOutParam());
848 if (rc == S_OK && err)
849 {
850 rc = err.queryInterfaceTo (mErrorInfo.asOutParam());
851 if (SUCCEEDED (rc) && !mErrorInfo)
852 rc = E_FAIL;
853 }
854
855#else /* !defined (VBOX_WITH_XPCOM) */
856
857 nsCOMPtr <nsIExceptionService> es;
858 es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
859 if (NS_SUCCEEDED (rc))
860 {
861 nsCOMPtr <nsIExceptionManager> em;
862 rc = es->GetCurrentExceptionManager (getter_AddRefs (em));
863 if (NS_SUCCEEDED (rc))
864 {
865 ComPtr <nsIException> ex;
866 rc = em->GetCurrentException (ex.asOutParam());
867 if (NS_SUCCEEDED (rc) && ex)
868 {
869 rc = ex.queryInterfaceTo (mErrorInfo.asOutParam());
870 if (NS_SUCCEEDED (rc) && !mErrorInfo)
871 rc = E_FAIL;
872 }
873 }
874 }
875#endif /* !defined (VBOX_WITH_XPCOM) */
876
877 AssertMsg (rc == S_OK, ("Couldn't get error info (rc=%08X) while trying "
878 "to set a failed result (%08X)!\n", rc, aResultCode));
879 }
880 else
881 {
882 mOperation = mOperationCount - 1; /* last operation */
883 mOperationPercent = 100;
884 }
885
886#if !defined VBOX_COM_INPROC
887 /* remove from the global collection of pending progress operations */
888 if (mParent)
889 mParent->removeProgress (mId);
890#endif
891
892 /* wake up all waiting threads */
893 if (mWaitersCount > 0)
894 RTSemEventMultiSignal (mCompletedSem);
895
896 return rc;
897}
898
899/**
900 * Marks the operation as complete and attaches full error info.
901 *
902 * See com::SupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t
903 * *, const char *, ...) for more info.
904 *
905 * @param aResultCode Operation result (error) code, must not be S_OK.
906 * @param aIID IID of the intrface that defines the error.
907 * @param aComponent Name of the component that generates the error.
908 * @param aText Error message (must not be null), an RTStrPrintf-like
909 * format string in UTF-8 encoding.
910 * @param ... List of arguments for the format string.
911 */
912HRESULT Progress::notifyComplete (HRESULT aResultCode, const GUID &aIID,
913 const Bstr &aComponent,
914 const char *aText, ...)
915{
916 va_list args;
917 va_start (args, aText);
918 Bstr text = Utf8StrFmtVA (aText, args);
919 va_end (args);
920
921 return notifyCompleteBstr (aResultCode, aIID, aComponent, text);
922}
923
924/**
925 * Marks the operation as complete and attaches full error info.
926 *
927 * See com::SupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t
928 * *, const char *, ...) for more info.
929 *
930 * This method is preferred iy you have a ready (translated and formatted) Bstr
931 * string, because it omits an extra conversion Utf8Str -> Bstr.
932 *
933 * @param aResultCode Operation result (error) code, must not be S_OK.
934 * @param aIID IID of the intrface that defines the error.
935 * @param aComponent Name of the component that generates the error.
936 * @param aText Error message (must not be null).
937 */
938HRESULT Progress::notifyCompleteBstr (HRESULT aResultCode, const GUID &aIID,
939 const Bstr &aComponent, const Bstr &aText)
940{
941 AutoCaller autoCaller (this);
942 AssertComRCReturnRC (autoCaller.rc());
943
944 AutoWriteLock alock (this);
945
946 AssertReturn (mCompleted == FALSE, E_FAIL);
947
948 if (mCanceled && SUCCEEDED(aResultCode))
949 aResultCode = E_FAIL;
950
951 mCompleted = TRUE;
952 mResultCode = aResultCode;
953
954 AssertReturn (FAILED (aResultCode), E_FAIL);
955
956 ComObjPtr <VirtualBoxErrorInfo> errorInfo;
957 HRESULT rc = errorInfo.createObject();
958 AssertComRC (rc);
959 if (SUCCEEDED (rc))
960 {
961 errorInfo->init (aResultCode, aIID, aComponent, aText);
962 errorInfo.queryInterfaceTo (mErrorInfo.asOutParam());
963 }
964
965#if !defined VBOX_COM_INPROC
966 /* remove from the global collection of pending progress operations */
967 if (mParent)
968 mParent->removeProgress (mId);
969#endif
970
971 /* wake up all waiting threads */
972 if (mWaitersCount > 0)
973 RTSemEventMultiSignal (mCompletedSem);
974
975 return rc;
976}
977
978////////////////////////////////////////////////////////////////////////////////
979// CombinedProgress class
980////////////////////////////////////////////////////////////////////////////////
981
982HRESULT CombinedProgress::FinalConstruct()
983{
984 HRESULT rc = ProgressBase::FinalConstruct();
985 CheckComRCReturnRC (rc);
986
987 mProgress = 0;
988 mCompletedOperations = 0;
989
990 return S_OK;
991}
992
993void CombinedProgress::FinalRelease()
994{
995 uninit();
996}
997
998// public initializer/uninitializer for internal purposes only
999////////////////////////////////////////////////////////////////////////////////
1000
1001/**
1002 * Initializes this object based on individual combined progresses.
1003 * Must be called only from #init()!
1004 *
1005 * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass.
1006 * @param aParent See ProgressBase::init().
1007 * @param aInitiator See ProgressBase::init().
1008 * @param aDescription See ProgressBase::init().
1009 * @param aId See ProgressBase::init().
1010 */
1011HRESULT CombinedProgress::protectedInit (AutoInitSpan &aAutoInitSpan,
1012#if !defined (VBOX_COM_INPROC)
1013 VirtualBox *aParent,
1014#endif
1015 IUnknown *aInitiator,
1016 CBSTR aDescription, OUT_GUID aId)
1017{
1018 LogFlowThisFunc (("aDescription={%ls} mProgresses.size()=%d\n",
1019 aDescription, mProgresses.size()));
1020
1021 HRESULT rc = S_OK;
1022
1023 rc = ProgressBase::protectedInit (aAutoInitSpan,
1024#if !defined (VBOX_COM_INPROC)
1025 aParent,
1026#endif
1027 aInitiator, aDescription, aId);
1028 CheckComRCReturnRC (rc);
1029
1030 mProgress = 0; /* the first object */
1031 mCompletedOperations = 0;
1032
1033 mCompleted = FALSE;
1034 mCancelable = TRUE; /* until any progress returns FALSE */
1035 mCanceled = FALSE;
1036
1037 mOperationCount = 0; /* will be calculated later */
1038
1039 mOperation = 0;
1040 rc = mProgresses [0]->COMGETTER(OperationDescription) (
1041 mOperationDescription.asOutParam());
1042 CheckComRCReturnRC (rc);
1043
1044 for (size_t i = 0; i < mProgresses.size(); i ++)
1045 {
1046 if (mCancelable)
1047 {
1048 BOOL cancelable = FALSE;
1049 rc = mProgresses [i]->COMGETTER(Cancelable) (&cancelable);
1050 CheckComRCReturnRC (rc);
1051
1052 if (!cancelable)
1053 mCancelable = FALSE;
1054 }
1055
1056 {
1057 ULONG opCount = 0;
1058 rc = mProgresses [i]->COMGETTER(OperationCount) (&opCount);
1059 CheckComRCReturnRC (rc);
1060
1061 mOperationCount += opCount;
1062 }
1063 }
1064
1065 rc = checkProgress();
1066 CheckComRCReturnRC (rc);
1067
1068 return rc;
1069}
1070
1071/**
1072 * Initializes the combined progress object given two normal progress
1073 * objects.
1074 *
1075 * @param aParent See ProgressBase::init().
1076 * @param aInitiator See ProgressBase::init().
1077 * @param aDescription See ProgressBase::init().
1078 * @param aProgress1 First normal progress object.
1079 * @param aProgress2 Second normal progress object.
1080 * @param aId See ProgressBase::init().
1081 */
1082HRESULT CombinedProgress::init (
1083#if !defined (VBOX_COM_INPROC)
1084 VirtualBox *aParent,
1085#endif
1086 IUnknown *aInitiator,
1087 CBSTR aDescription,
1088 IProgress *aProgress1, IProgress *aProgress2,
1089 OUT_GUID aId /* = NULL */)
1090{
1091 /* Enclose the state transition NotReady->InInit->Ready */
1092 AutoInitSpan autoInitSpan (this);
1093 AssertReturn (autoInitSpan.isOk(), E_FAIL);
1094
1095 mProgresses.resize (2);
1096 mProgresses [0] = aProgress1;
1097 mProgresses [1] = aProgress2;
1098
1099 HRESULT rc = protectedInit (autoInitSpan,
1100#if !defined (VBOX_COM_INPROC)
1101 aParent,
1102#endif
1103 aInitiator, aDescription, aId);
1104
1105 /* Confirm a successful initialization when it's the case */
1106 if (SUCCEEDED (rc))
1107 autoInitSpan.setSucceeded();
1108
1109 return rc;
1110}
1111
1112/**
1113 * Uninitializes the instance and sets the ready flag to FALSE.
1114 *
1115 * Called either from FinalRelease() or by the parent when it gets destroyed.
1116 */
1117void CombinedProgress::uninit()
1118{
1119 LogFlowThisFunc (("\n"));
1120
1121 /* Enclose the state transition Ready->InUninit->NotReady */
1122 AutoUninitSpan autoUninitSpan (this);
1123 if (autoUninitSpan.uninitDone())
1124 return;
1125
1126 mProgress = 0;
1127 mProgresses.clear();
1128
1129 ProgressBase::protectedUninit (autoUninitSpan);
1130}
1131
1132// IProgress properties
1133////////////////////////////////////////////////////////////////////////////////
1134
1135STDMETHODIMP CombinedProgress::COMGETTER(Percent) (LONG *aPercent)
1136{
1137 CheckComArgOutPointerValid(aPercent);
1138
1139 AutoCaller autoCaller (this);
1140 CheckComRCReturnRC (autoCaller.rc());
1141
1142 /* checkProgress needs a write lock */
1143 AutoWriteLock alock (this);
1144
1145 if (mCompleted && SUCCEEDED (mResultCode))
1146 *aPercent = 100;
1147 else
1148 {
1149 HRESULT rc = checkProgress();
1150 CheckComRCReturnRC (rc);
1151
1152 /* global percent =
1153 * (100 / mOperationCount) * mOperation +
1154 * ((100 / mOperationCount) / 100) * mOperationPercent */
1155 *aPercent = (100 * mOperation + mOperationPercent) / mOperationCount;
1156 }
1157
1158 return S_OK;
1159}
1160
1161STDMETHODIMP CombinedProgress::COMGETTER(Completed) (BOOL *aCompleted)
1162{
1163 CheckComArgOutPointerValid(aCompleted);
1164
1165 AutoCaller autoCaller (this);
1166 CheckComRCReturnRC (autoCaller.rc());
1167
1168 /* checkProgress needs a write lock */
1169 AutoWriteLock alock (this);
1170
1171 HRESULT rc = checkProgress();
1172 CheckComRCReturnRC (rc);
1173
1174 return ProgressBase::COMGETTER(Completed) (aCompleted);
1175}
1176
1177STDMETHODIMP CombinedProgress::COMGETTER(Canceled) (BOOL *aCanceled)
1178{
1179 CheckComArgOutPointerValid(aCanceled);
1180
1181 AutoCaller autoCaller (this);
1182 CheckComRCReturnRC (autoCaller.rc());
1183
1184 /* checkProgress needs a write lock */
1185 AutoWriteLock alock (this);
1186
1187 HRESULT rc = checkProgress();
1188 CheckComRCReturnRC (rc);
1189
1190 return ProgressBase::COMGETTER(Canceled) (aCanceled);
1191}
1192
1193STDMETHODIMP CombinedProgress::COMGETTER(ResultCode) (HRESULT *aResultCode)
1194{
1195 CheckComArgOutPointerValid(aResultCode);
1196
1197 AutoCaller autoCaller (this);
1198 CheckComRCReturnRC (autoCaller.rc());
1199
1200 /* checkProgress needs a write lock */
1201 AutoWriteLock alock (this);
1202
1203 HRESULT rc = checkProgress();
1204 CheckComRCReturnRC (rc);
1205
1206 return ProgressBase::COMGETTER(ResultCode) (aResultCode);
1207}
1208
1209STDMETHODIMP CombinedProgress::COMGETTER(ErrorInfo) (IVirtualBoxErrorInfo **aErrorInfo)
1210{
1211 CheckComArgOutPointerValid(aErrorInfo);
1212
1213 AutoCaller autoCaller (this);
1214 CheckComRCReturnRC (autoCaller.rc());
1215
1216 /* checkProgress needs a write lock */
1217 AutoWriteLock alock (this);
1218
1219 HRESULT rc = checkProgress();
1220 CheckComRCReturnRC (rc);
1221
1222 return ProgressBase::COMGETTER(ErrorInfo) (aErrorInfo);
1223}
1224
1225STDMETHODIMP CombinedProgress::COMGETTER(Operation) (ULONG *aOperation)
1226{
1227 CheckComArgOutPointerValid(aOperation);
1228
1229 AutoCaller autoCaller (this);
1230 CheckComRCReturnRC (autoCaller.rc());
1231
1232 /* checkProgress needs a write lock */
1233 AutoWriteLock alock (this);
1234
1235 HRESULT rc = checkProgress();
1236 CheckComRCReturnRC (rc);
1237
1238 return ProgressBase::COMGETTER(Operation) (aOperation);
1239}
1240
1241STDMETHODIMP CombinedProgress::COMGETTER(OperationDescription) (BSTR *aOperationDescription)
1242{
1243 CheckComArgOutPointerValid(aOperationDescription);
1244
1245 AutoCaller autoCaller (this);
1246 CheckComRCReturnRC (autoCaller.rc());
1247
1248 /* checkProgress needs a write lock */
1249 AutoWriteLock alock (this);
1250
1251 HRESULT rc = checkProgress();
1252 CheckComRCReturnRC (rc);
1253
1254 return ProgressBase::COMGETTER(OperationDescription) (aOperationDescription);
1255}
1256
1257STDMETHODIMP CombinedProgress::COMGETTER(OperationPercent) (LONG *aOperationPercent)
1258{
1259 CheckComArgOutPointerValid(aOperationPercent);
1260
1261 AutoCaller autoCaller (this);
1262 CheckComRCReturnRC (autoCaller.rc());
1263
1264 /* checkProgress needs a write lock */
1265 AutoWriteLock alock (this);
1266
1267 HRESULT rc = checkProgress();
1268 CheckComRCReturnRC (rc);
1269
1270 return ProgressBase::COMGETTER(OperationPercent) (aOperationPercent);
1271}
1272
1273// IProgress methods
1274/////////////////////////////////////////////////////////////////////////////
1275
1276/**
1277 * @note XPCOM: when this method is called not on the main XPCOM thread, it it
1278 * simply blocks the thread until mCompletedSem is signalled. If the
1279 * thread has its own event queue (hmm, what for?) that it must run, then
1280 * calling this method will definitey freese event processing.
1281 */
1282STDMETHODIMP CombinedProgress::WaitForCompletion (LONG aTimeout)
1283{
1284 LogFlowThisFuncEnter();
1285 LogFlowThisFunc (("aTtimeout=%d\n", aTimeout));
1286
1287 AutoCaller autoCaller (this);
1288 CheckComRCReturnRC (autoCaller.rc());
1289
1290 AutoWriteLock alock (this);
1291
1292 /* if we're already completed, take a shortcut */
1293 if (!mCompleted)
1294 {
1295 RTTIMESPEC time;
1296 RTTimeNow (&time);
1297
1298 HRESULT rc = S_OK;
1299 bool forever = aTimeout < 0;
1300 int64_t timeLeft = aTimeout;
1301 int64_t lastTime = RTTimeSpecGetMilli (&time);
1302
1303 while (!mCompleted && (forever || timeLeft > 0))
1304 {
1305 alock.leave();
1306 rc = mProgresses.back()->WaitForCompletion (
1307 forever ? -1 : (LONG) timeLeft);
1308 alock.enter();
1309
1310 if (SUCCEEDED (rc))
1311 rc = checkProgress();
1312
1313 CheckComRCBreakRC (rc);
1314
1315 if (!forever)
1316 {
1317 RTTimeNow (&time);
1318 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
1319 lastTime = RTTimeSpecGetMilli (&time);
1320 }
1321 }
1322
1323 CheckComRCReturnRC (rc);
1324 }
1325
1326 LogFlowThisFuncLeave();
1327
1328 return S_OK;
1329}
1330
1331/**
1332 * @note XPCOM: when this method is called not on the main XPCOM thread, it it
1333 * simply blocks the thread until mCompletedSem is signalled. If the
1334 * thread has its own event queue (hmm, what for?) that it must run, then
1335 * calling this method will definitey freese event processing.
1336 */
1337STDMETHODIMP CombinedProgress::WaitForOperationCompletion (ULONG aOperation, LONG aTimeout)
1338{
1339 LogFlowThisFuncEnter();
1340 LogFlowThisFunc (("aOperation=%d, aTimeout=%d\n", aOperation, aTimeout));
1341
1342 AutoCaller autoCaller (this);
1343 CheckComRCReturnRC (autoCaller.rc());
1344
1345 AutoWriteLock alock (this);
1346
1347 if (aOperation >= mOperationCount)
1348 return setError (E_FAIL,
1349 tr ("Operation number must be in range [0, %d]"), mOperation - 1);
1350
1351 /* if we're already completed or if the given operation is already done,
1352 * then take a shortcut */
1353 if (!mCompleted && aOperation >= mOperation)
1354 {
1355 HRESULT rc = S_OK;
1356
1357 /* find the right progress object to wait for */
1358 size_t progress = mProgress;
1359 ULONG operation = 0, completedOps = mCompletedOperations;
1360 do
1361 {
1362 ULONG opCount = 0;
1363 rc = mProgresses [progress]->COMGETTER(OperationCount) (&opCount);
1364 if (FAILED (rc))
1365 return rc;
1366
1367 if (completedOps + opCount > aOperation)
1368 {
1369 /* found the right progress object */
1370 operation = aOperation - completedOps;
1371 break;
1372 }
1373
1374 completedOps += opCount;
1375 progress ++;
1376 ComAssertRet (progress < mProgresses.size(), E_FAIL);
1377 }
1378 while (1);
1379
1380 LogFlowThisFunc (("will wait for mProgresses [%d] (%d)\n",
1381 progress, operation));
1382
1383 RTTIMESPEC time;
1384 RTTimeNow (&time);
1385
1386 bool forever = aTimeout < 0;
1387 int64_t timeLeft = aTimeout;
1388 int64_t lastTime = RTTimeSpecGetMilli (&time);
1389
1390 while (!mCompleted && aOperation >= mOperation &&
1391 (forever || timeLeft > 0))
1392 {
1393 alock.leave();
1394 /* wait for the appropriate progress operation completion */
1395 rc = mProgresses [progress]-> WaitForOperationCompletion (
1396 operation, forever ? -1 : (LONG) timeLeft);
1397 alock.enter();
1398
1399 if (SUCCEEDED (rc))
1400 rc = checkProgress();
1401
1402 CheckComRCBreakRC (rc);
1403
1404 if (!forever)
1405 {
1406 RTTimeNow (&time);
1407 timeLeft -= RTTimeSpecGetMilli (&time) - lastTime;
1408 lastTime = RTTimeSpecGetMilli (&time);
1409 }
1410 }
1411
1412 CheckComRCReturnRC (rc);
1413 }
1414
1415 LogFlowThisFuncLeave();
1416
1417 return S_OK;
1418}
1419
1420STDMETHODIMP CombinedProgress::Cancel()
1421{
1422 AutoCaller autoCaller (this);
1423 CheckComRCReturnRC (autoCaller.rc());
1424
1425 AutoWriteLock alock (this);
1426
1427 if (!mCancelable)
1428 return setError (E_FAIL, tr ("Operation cannot be cancelled"));
1429
1430 mCanceled = TRUE;
1431 return S_OK;
1432}
1433
1434// private methods
1435////////////////////////////////////////////////////////////////////////////////
1436
1437/**
1438 * Fetches the properties of the current progress object and, if it is
1439 * successfully completed, advances to the next uncompleted or unsucessfully
1440 * completed object in the vector of combined progress objects.
1441 *
1442 * @note Must be called from under this object's write lock!
1443 */
1444HRESULT CombinedProgress::checkProgress()
1445{
1446 /* do nothing if we're already marked ourselves as completed */
1447 if (mCompleted)
1448 return S_OK;
1449
1450 AssertReturn (mProgress < mProgresses.size(), E_FAIL);
1451
1452 ComPtr <IProgress> progress = mProgresses [mProgress];
1453 ComAssertRet (!progress.isNull(), E_FAIL);
1454
1455 HRESULT rc = S_OK;
1456 BOOL completed = FALSE;
1457
1458 do
1459 {
1460 rc = progress->COMGETTER(Completed) (&completed);
1461 if (FAILED (rc))
1462 return rc;
1463
1464 if (completed)
1465 {
1466 rc = progress->COMGETTER(Canceled) (&mCanceled);
1467 if (FAILED (rc))
1468 return rc;
1469
1470 rc = progress->COMGETTER(ResultCode) (&mResultCode);
1471 if (FAILED (rc))
1472 return rc;
1473
1474 if (FAILED (mResultCode))
1475 {
1476 rc = progress->COMGETTER(ErrorInfo) (mErrorInfo.asOutParam());
1477 if (FAILED (rc))
1478 return rc;
1479 }
1480
1481 if (FAILED (mResultCode) || mCanceled)
1482 {
1483 mCompleted = TRUE;
1484 }
1485 else
1486 {
1487 ULONG opCount = 0;
1488 rc = progress->COMGETTER(OperationCount) (&opCount);
1489 if (FAILED (rc))
1490 return rc;
1491
1492 mCompletedOperations += opCount;
1493 mProgress ++;
1494
1495 if (mProgress < mProgresses.size())
1496 progress = mProgresses [mProgress];
1497 else
1498 mCompleted = TRUE;
1499 }
1500 }
1501 }
1502 while (completed && !mCompleted);
1503
1504 rc = progress->COMGETTER(OperationPercent) (&mOperationPercent);
1505 if (SUCCEEDED (rc))
1506 {
1507 ULONG operation = 0;
1508 rc = progress->COMGETTER(Operation) (&operation);
1509 if (SUCCEEDED (rc) && mCompletedOperations + operation > mOperation)
1510 {
1511 mOperation = mCompletedOperations + operation;
1512 rc = progress->COMGETTER(OperationDescription) (
1513 mOperationDescription.asOutParam());
1514 }
1515 }
1516
1517 return rc;
1518}
1519/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use