summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/ProgressProxyImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/ProgressProxyImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/ProgressProxyImpl.cpp709
1 files changed, 709 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/ProgressProxyImpl.cpp b/src/VBox/Main/src-server/ProgressProxyImpl.cpp
new file mode 100644
index 00000000..79c42b70
--- /dev/null
+++ b/src/VBox/Main/src-server/ProgressProxyImpl.cpp
@@ -0,0 +1,709 @@
+/* $Id: ProgressProxyImpl.cpp $ */
+/** @file
+ * IProgress implementation for Machine::openRemoteSession in VBoxSVC.
+ */
+
+/*
+ * Copyright (C) 2010-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_PROGRESS
+#include <iprt/types.h>
+
+#include "ProgressProxyImpl.h"
+
+#include "VirtualBoxImpl.h"
+#include "VirtualBoxErrorInfoImpl.h"
+
+#include "LoggingNew.h"
+
+#include <iprt/time.h>
+#include <iprt/semaphore.h>
+#include <iprt/errcore.h>
+
+////////////////////////////////////////////////////////////////////////////////
+// ProgressProxy class
+////////////////////////////////////////////////////////////////////////////////
+
+// constructor / destructor / uninitializer
+////////////////////////////////////////////////////////////////////////////////
+
+
+HRESULT ProgressProxy::FinalConstruct()
+{
+ mfMultiOperation = false;
+ muOtherProgressStartWeight = 0;
+ muOtherProgressWeight = 0;
+ muOtherProgressStartOperation = 0;
+
+ HRESULT rc = Progress::FinalConstruct();
+ return rc;
+}
+
+/**
+ * Initialize it as a one operation Progress object.
+ *
+ * This is used by SessionMachine::OnSessionEnd.
+ */
+HRESULT ProgressProxy::init(
+#if !defined (VBOX_COM_INPROC)
+ VirtualBox *pParent,
+#endif
+ IUnknown *pInitiator,
+ Utf8Str strDescription,
+ BOOL fCancelable)
+{
+ mfMultiOperation = false;
+ muOtherProgressStartWeight = 1;
+ muOtherProgressWeight = 1;
+ muOtherProgressStartOperation = 1;
+
+ return Progress::init(
+#if !defined (VBOX_COM_INPROC)
+ pParent,
+#endif
+ pInitiator,
+ strDescription,
+ fCancelable,
+ 1 /* cOperations */,
+ 1 /* ulTotalOperationsWeight */,
+ strDescription /* strFirstOperationDescription */,
+ 1 /* ulFirstOperationWeight */);
+}
+
+/**
+ * Initialize for proxying one other progress object.
+ *
+ * This is tailored explicitly for the openRemoteSession code, so we start out
+ * with one operation where we don't have any remote object (powerUp). Then a
+ * remote object is added and stays with us till the end.
+ *
+ * The user must do normal completion notification or risk leave the threads
+ * waiting forever!
+ */
+HRESULT ProgressProxy::init(
+#if !defined (VBOX_COM_INPROC)
+ VirtualBox *pParent,
+#endif
+ IUnknown *pInitiator,
+ Utf8Str strDescription,
+ BOOL fCancelable,
+ ULONG uTotalOperationsWeight,
+ Utf8Str strFirstOperationDescription,
+ ULONG uFirstOperationWeight,
+ ULONG cOtherProgressObjectOperations)
+{
+ mfMultiOperation = false;
+ muOtherProgressStartWeight = uFirstOperationWeight;
+ muOtherProgressWeight = uTotalOperationsWeight - uFirstOperationWeight;
+ muOtherProgressStartOperation = 1;
+
+ return Progress::init(
+#if !defined (VBOX_COM_INPROC)
+ pParent,
+#endif
+ pInitiator,
+ strDescription,
+ fCancelable,
+ 1 + cOtherProgressObjectOperations /* cOperations */,
+ uTotalOperationsWeight,
+ strFirstOperationDescription,
+ uFirstOperationWeight);
+}
+
+void ProgressProxy::FinalRelease()
+{
+ uninit();
+ mfMultiOperation = false;
+ muOtherProgressStartWeight = 0;
+ muOtherProgressWeight = 0;
+ muOtherProgressStartOperation = 0;
+
+ BaseFinalRelease();
+}
+
+void ProgressProxy::uninit()
+{
+ LogFlowThisFunc(("\n"));
+
+ mptrOtherProgress.setNull();
+ Progress::uninit();
+}
+
+// Public methods
+////////////////////////////////////////////////////////////////////////////////
+
+/** Just a wrapper so we can automatically do the handover before setting
+ * the result locally. */
+HRESULT ProgressProxy::notifyComplete(HRESULT aResultCode)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ clearOtherProgressObjectInternal(true /* fEarly */);
+ HRESULT hrc = S_OK;
+ if (!mCompleted)
+ hrc = Progress::i_notifyComplete(aResultCode);
+ return hrc;
+}
+
+/** Just a wrapper so we can automatically do the handover before setting
+ * the result locally. */
+HRESULT ProgressProxy::notifyComplete(HRESULT aResultCode,
+ const GUID &aIID,
+ const char *pcszComponent,
+ const char *aText,
+ ...)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ clearOtherProgressObjectInternal(true /* fEarly */);
+
+ HRESULT hrc = S_OK;
+ if (!mCompleted)
+ {
+ va_list va;
+ va_start(va, aText);
+ hrc = Progress::i_notifyCompleteV(aResultCode, aIID, pcszComponent, aText, va);
+ va_end(va);
+ }
+ return hrc;
+}
+
+/**
+ * Sets the other progress object unless the operation has been completed /
+ * canceled already.
+ *
+ * @returns false if failed/canceled, true if not.
+ * @param pOtherProgress The other progress object. Must not be NULL.
+ */
+bool ProgressProxy::setOtherProgressObject(IProgress *pOtherProgress)
+{
+ LogFlowThisFunc(("setOtherProgressObject: %p\n", pOtherProgress));
+ ComPtr<IProgress> ptrOtherProgress = pOtherProgress;
+
+ /*
+ * Query information from the other progress object before we grab the
+ * lock.
+ */
+ ULONG cOperations;
+ HRESULT hrc = pOtherProgress->COMGETTER(OperationCount)(&cOperations);
+ if (FAILED(hrc))
+ cOperations = 1;
+
+ Bstr bstrOperationDescription;
+ hrc = pOtherProgress->COMGETTER(Description)(bstrOperationDescription.asOutParam());
+ if (FAILED(hrc))
+ bstrOperationDescription = "oops";
+
+
+ /*
+ * Take the lock and check for cancelation, cancel the other object if
+ * we've been canceled already.
+ */
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ BOOL fCompletedOrCanceled = mCompleted || mCanceled;
+ if (!fCompletedOrCanceled)
+ {
+ /*
+ * Advance to the next object and operation. If the other object has
+ * more operations than anticipated, adjust our internal count.
+ */
+ mptrOtherProgress = ptrOtherProgress;
+ mfMultiOperation = cOperations > 1;
+
+ muOtherProgressStartWeight = m_ulOperationsCompletedWeight + m_ulCurrentOperationWeight;
+ muOtherProgressWeight = m_ulTotalOperationsWeight - muOtherProgressStartWeight;
+ Progress::SetNextOperation(bstrOperationDescription.raw(), muOtherProgressWeight);
+
+ muOtherProgressStartOperation = m_ulCurrentOperation;
+ m_cOperations = cOperations + m_ulCurrentOperation;
+
+ /*
+ * Check for cancelation and completion.
+ */
+ BOOL f;
+ hrc = ptrOtherProgress->COMGETTER(Completed)(&f);
+ fCompletedOrCanceled = FAILED(hrc) || f;
+
+ if (!fCompletedOrCanceled)
+ {
+ hrc = ptrOtherProgress->COMGETTER(Canceled)(&f);
+ fCompletedOrCanceled = SUCCEEDED(hrc) && f;
+ }
+
+ if (fCompletedOrCanceled)
+ {
+ LogFlowThisFunc(("Other object completed or canceled, clearing...\n"));
+ clearOtherProgressObjectInternal(false /*fEarly*/);
+ }
+ else
+ {
+ /*
+ * Finally, mirror the cancelable property.
+ * Note! Note necessary if we do passthru!
+ */
+ if (mCancelable)
+ {
+ hrc = ptrOtherProgress->COMGETTER(Cancelable)(&f);
+ if (SUCCEEDED(hrc) && !f)
+ {
+ LogFlowThisFunc(("The other progress object is not cancelable\n"));
+ mCancelable = FALSE;
+ }
+ }
+ }
+ }
+ else
+ {
+ LogFlowThisFunc(("mCompleted=%RTbool mCanceled=%RTbool - Canceling the other progress object!\n",
+ mCompleted, mCanceled));
+ hrc = ptrOtherProgress->Cancel();
+ LogFlowThisFunc(("Cancel -> %Rhrc", hrc));
+ }
+
+ LogFlowThisFunc(("Returns %RTbool\n", !fCompletedOrCanceled));
+ return !fCompletedOrCanceled;
+}
+
+// Internal methods.
+////////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Clear the other progress object reference, first copying over its state.
+ *
+ * This is used internally when completion is signalled one way or another.
+ *
+ * @param fEarly Early clearing or not.
+ */
+void ProgressProxy::clearOtherProgressObjectInternal(bool fEarly)
+{
+ if (!mptrOtherProgress.isNull())
+ {
+ ComPtr<IProgress> ptrOtherProgress = mptrOtherProgress;
+ mptrOtherProgress.setNull();
+ copyProgressInfo(ptrOtherProgress, fEarly);
+ }
+}
+
+/**
+ * Called to copy over the progress information from @a pOtherProgress.
+ *
+ * @param pOtherProgress The source of the information.
+ * @param fEarly Early copy.
+ *
+ * @note The caller owns the write lock and as cleared mptrOtherProgress
+ * already (or we might recurse forever)!
+ */
+void ProgressProxy::copyProgressInfo(IProgress *pOtherProgress, bool fEarly)
+{
+ HRESULT hrc;
+ LogFlowThisFunc(("\n"));
+
+ NOREF(fEarly);
+
+ /*
+ * No point in doing this if the progress object was canceled already.
+ */
+ if (!mCanceled)
+ {
+ /* Detect if the other progress object was canceled. */
+ BOOL fCanceled;
+ hrc = pOtherProgress->COMGETTER(Canceled)(&fCanceled);
+ if (FAILED(hrc))
+ fCanceled = FALSE;
+ if (fCanceled)
+ {
+ LogFlowThisFunc(("Canceled\n"));
+ mCanceled = TRUE;
+ if (m_pfnCancelCallback)
+ m_pfnCancelCallback(m_pvCancelUserArg);
+ }
+ else
+ {
+ /* Has it completed? */
+ BOOL fCompleted;
+ hrc = pOtherProgress->COMGETTER(Completed)(&fCompleted);
+ if (FAILED(hrc))
+ fCompleted = TRUE;
+ Assert(fCompleted || fEarly);
+ if (fCompleted)
+ {
+ /* Check the result. */
+ LONG lResult;
+ hrc = pOtherProgress->COMGETTER(ResultCode)(&lResult);
+ if (FAILED(hrc))
+ lResult = (LONG)hrc;
+ if (SUCCEEDED((HRESULT)lResult))
+ LogFlowThisFunc(("Succeeded\n"));
+ else
+ {
+ /* Get the error information. */
+ ComPtr<IVirtualBoxErrorInfo> ptrErrorInfo;
+ hrc = pOtherProgress->COMGETTER(ErrorInfo)(ptrErrorInfo.asOutParam());
+ if (SUCCEEDED(hrc) && !ptrErrorInfo.isNull())
+ {
+ Bstr bstrIID;
+ hrc = ptrErrorInfo->COMGETTER(InterfaceID)(bstrIID.asOutParam()); AssertComRC(hrc);
+ if (FAILED(hrc))
+ bstrIID.setNull();
+
+ Bstr bstrComponent;
+ hrc = ptrErrorInfo->COMGETTER(Component)(bstrComponent.asOutParam()); AssertComRC(hrc);
+ if (FAILED(hrc))
+ bstrComponent = "failed";
+
+ Bstr bstrText;
+ hrc = ptrErrorInfo->COMGETTER(Text)(bstrText.asOutParam()); AssertComRC(hrc);
+ if (FAILED(hrc))
+ bstrText = "<failed>";
+
+ Utf8Str strText(bstrText);
+ LogFlowThisFunc(("Got ErrorInfo(%s); hrcResult=%Rhrc\n", strText.c_str(), (HRESULT)lResult));
+ Progress::i_notifyComplete((HRESULT)lResult,
+ Guid(bstrIID).ref(),
+ Utf8Str(bstrComponent).c_str(),
+ "%s", strText.c_str());
+ }
+ else
+ {
+ LogFlowThisFunc(("ErrorInfo failed with hrc=%Rhrc; hrcResult=%Rhrc\n", hrc, (HRESULT)lResult));
+ Progress::i_notifyComplete((HRESULT)lResult,
+ COM_IIDOF(IProgress),
+ "ProgressProxy",
+ tr("No error info"));
+ }
+ }
+ }
+ else
+ LogFlowThisFunc(("Not completed\n"));
+ }
+ }
+ else
+ LogFlowThisFunc(("Already canceled\n"));
+
+ /*
+ * Did cancelable state change (point of no return)?
+ */
+ if (mCancelable && !mCompleted && !mCanceled)
+ {
+ BOOL fCancelable;
+ hrc = pOtherProgress->COMGETTER(Cancelable)(&fCancelable); AssertComRC(hrc);
+ if (SUCCEEDED(hrc) && !fCancelable)
+ {
+ LogFlowThisFunc(("point-of-no-return reached\n"));
+ mCancelable = FALSE;
+ }
+ }
+}
+
+
+// IProgress properties
+////////////////////////////////////////////////////////////////////////////////
+
+STDMETHODIMP ProgressProxy::COMGETTER(Cancelable)(BOOL *aCancelable)
+{
+ CheckComArgOutPointerValid(aCancelable);
+
+ AutoCaller autoCaller(this);
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /* ASSUME: The cancelable property can only change to FALSE. */
+ if (!mCancelable || mptrOtherProgress.isNull())
+ *aCancelable = mCancelable;
+ else
+ {
+ hrc = mptrOtherProgress->COMGETTER(Cancelable)(aCancelable);
+ if (SUCCEEDED(hrc) && !*aCancelable)
+ {
+ LogFlowThisFunc(("point-of-no-return reached\n"));
+ mCancelable = FALSE;
+ }
+ }
+ }
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(Percent)(ULONG *aPercent)
+{
+ CheckComArgOutPointerValid(aPercent);
+
+ AutoCaller autoCaller(this);
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (mptrOtherProgress.isNull())
+ hrc = Progress::COMGETTER(Percent)(aPercent);
+ else
+ {
+ /*
+ * Get the overall percent of the other object and adjust it with
+ * the weighting given to the period before proxying started.
+ */
+ ULONG uPct;
+ hrc = mptrOtherProgress->COMGETTER(Percent)(&uPct);
+ if (SUCCEEDED(hrc))
+ {
+ double rdPercent = ((double)uPct / 100 * muOtherProgressWeight + muOtherProgressStartWeight)
+ / m_ulTotalOperationsWeight * 100;
+ *aPercent = RT_MIN((ULONG)rdPercent, 99); /* mptrOtherProgress is cleared when its completed,
+ so we can never return 100%. */
+ }
+ }
+ }
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(TimeRemaining)(LONG *aTimeRemaining)
+{
+ CheckComArgOutPointerValid(aTimeRemaining);
+
+ AutoCaller autoCaller(this);
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (mptrOtherProgress.isNull())
+ hrc = Progress::COMGETTER(TimeRemaining)(aTimeRemaining);
+ else
+ hrc = mptrOtherProgress->COMGETTER(TimeRemaining)(aTimeRemaining);
+ }
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(Completed)(BOOL *aCompleted)
+{
+ /* Not proxied since we EXPECT a normal completion notification call. */
+ return Progress::COMGETTER(Completed)(aCompleted);
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(Canceled)(BOOL *aCanceled)
+{
+ CheckComArgOutPointerValid(aCanceled);
+
+ AutoCaller autoCaller(this);
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ /* Check the local data first, then the other object. */
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ hrc = Progress::COMGETTER(Canceled)(aCanceled);
+ if ( SUCCEEDED(hrc)
+ && !*aCanceled
+ && !mptrOtherProgress.isNull()
+ && mCancelable)
+ {
+ hrc = mptrOtherProgress->COMGETTER(Canceled)(aCanceled);
+ if (SUCCEEDED(hrc) && *aCanceled)
+ /* This will not complete the object, only mark it as canceled. */
+ clearOtherProgressObjectInternal(false /*fEarly*/);
+ }
+ }
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(ResultCode)(LONG *aResultCode)
+{
+ /* Not proxied since we EXPECT a normal completion notification call. */
+ return Progress::COMGETTER(ResultCode)(aResultCode);
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(ErrorInfo)(IVirtualBoxErrorInfo **aErrorInfo)
+{
+ /* Not proxied since we EXPECT a normal completion notification call. */
+ return Progress::COMGETTER(ErrorInfo)(aErrorInfo);
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(Operation)(ULONG *aOperation)
+{
+ CheckComArgOutPointerValid(aOperation);
+
+ AutoCaller autoCaller(this);
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (mptrOtherProgress.isNull())
+ hrc = Progress::COMGETTER(Operation)(aOperation);
+ else
+ {
+ ULONG uCurOtherOperation;
+ hrc = mptrOtherProgress->COMGETTER(Operation)(&uCurOtherOperation);
+ if (SUCCEEDED(hrc))
+ *aOperation = uCurOtherOperation + muOtherProgressStartOperation;
+ }
+ }
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(OperationDescription)(BSTR *aOperationDescription)
+{
+ CheckComArgOutPointerValid(aOperationDescription);
+
+ AutoCaller autoCaller(this);
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (mptrOtherProgress.isNull() || !mfMultiOperation)
+ hrc = Progress::COMGETTER(OperationDescription)(aOperationDescription);
+ else
+ hrc = mptrOtherProgress->COMGETTER(OperationDescription)(aOperationDescription);
+ }
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(OperationPercent)(ULONG *aOperationPercent)
+{
+ CheckComArgOutPointerValid(aOperationPercent);
+
+ AutoCaller autoCaller(this);
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (mptrOtherProgress.isNull() || !mfMultiOperation)
+ hrc = Progress::COMGETTER(OperationPercent)(aOperationPercent);
+ else
+ hrc = mptrOtherProgress->COMGETTER(OperationPercent)(aOperationPercent);
+ }
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::COMSETTER(Timeout)(ULONG aTimeout)
+{
+ /* Not currently supported. */
+ NOREF(aTimeout);
+ AssertFailed();
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP ProgressProxy::COMGETTER(Timeout)(ULONG *aTimeout)
+{
+ /* Not currently supported. */
+ CheckComArgOutPointerValid(aTimeout);
+
+ AssertFailed();
+ return E_NOTIMPL;
+}
+
+// IProgress methods
+/////////////////////////////////////////////////////////////////////////////
+
+STDMETHODIMP ProgressProxy::WaitForCompletion(LONG aTimeout)
+{
+ HRESULT hrc;
+ LogFlowThisFuncEnter();
+ LogFlowThisFunc(("aTimeout=%d\n", aTimeout));
+
+ /* No need to wait on the proxied object for these since we'll get the
+ normal completion notifications. */
+ hrc = Progress::WaitForCompletion(aTimeout);
+
+ LogFlowThisFuncLeave();
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::WaitForOperationCompletion(ULONG aOperation, LONG aTimeout)
+{
+ LogFlowThisFuncEnter();
+ LogFlowThisFunc(("aOperation=%d aTimeout=%d\n", aOperation, aTimeout));
+
+ AutoCaller autoCaller(this);
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ CheckComArgExpr(aOperation, aOperation < m_cOperations);
+
+ /*
+ * Check if we can wait locally.
+ */
+ if ( aOperation + 1 == m_cOperations /* final operation */
+ || mptrOtherProgress.isNull())
+ {
+ /* ASSUMES that Progress::WaitForOperationCompletion is using
+ AutoWriteLock::leave() as it saves us from duplicating the code! */
+ hrc = Progress::WaitForOperationCompletion(aOperation, aTimeout);
+ }
+ else
+ {
+ LogFlowThisFunc(("calling the other object...\n"));
+ ComPtr<IProgress> ptrOtherProgress = mptrOtherProgress;
+ alock.release();
+
+ hrc = ptrOtherProgress->WaitForOperationCompletion(aOperation, aTimeout);
+ }
+ }
+
+ LogFlowThisFuncLeave();
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::Cancel()
+{
+ LogFlowThisFunc(("\n"));
+ AutoCaller autoCaller(this);
+ HRESULT hrc = autoCaller.rc();
+ if (SUCCEEDED(hrc))
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (mptrOtherProgress.isNull() || !mCancelable)
+ hrc = Progress::Cancel();
+ else
+ {
+ hrc = mptrOtherProgress->Cancel();
+ if (SUCCEEDED(hrc))
+ clearOtherProgressObjectInternal(false /*fEarly*/);
+ }
+ }
+
+ LogFlowThisFunc(("returns %Rhrc\n", hrc));
+ return hrc;
+}
+
+STDMETHODIMP ProgressProxy::SetCurrentOperationProgress(ULONG aPercent)
+{
+ /* Not supported - why do we actually expose this? */
+ NOREF(aPercent);
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP ProgressProxy::SetNextOperation(IN_BSTR bstrNextOperationDescription, ULONG ulNextOperationsWeight)
+{
+ /* Not supported - why do we actually expose this? */
+ NOREF(bstrNextOperationDescription);
+ NOREF(ulNextOperationsWeight);
+ return E_NOTIMPL;
+}
+
+#ifdef VBOX_WITH_XPCOM
+NS_DECL_CLASSINFO(ProgressProxy)
+NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProgressProxy, IProgress)
+#endif
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */