VirtualBox

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

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

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

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