VirtualBox

source: vbox/trunk/src/VBox/Main/ProgressProxyImpl.cpp@ 29940

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

Main: Reapplied r62200 with bug fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.6 KB
Line 
1/* $Id: ProgressProxyImpl.cpp 29937 2010-06-01 08:41:32Z vboxsync $ */
2/** @file
3 * IProgress implementation for Machine::openRemoteSession in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/types.h>
19
20#if defined (VBOX_WITH_XPCOM)
21#include <nsIServiceManager.h>
22#include <nsIExceptionService.h>
23#include <nsCOMPtr.h>
24#endif /* defined (VBOX_WITH_XPCOM) */
25
26#include "ProgressProxyImpl.h"
27
28#include "VirtualBoxImpl.h"
29#include "VirtualBoxErrorInfoImpl.h"
30
31#include "Logging.h"
32
33#include <iprt/time.h>
34#include <iprt/semaphore.h>
35
36#include <VBox/err.h>
37
38////////////////////////////////////////////////////////////////////////////////
39// ProgressProxy class
40////////////////////////////////////////////////////////////////////////////////
41
42// constructor / destructor / uninitializer
43////////////////////////////////////////////////////////////////////////////////
44
45
46HRESULT ProgressProxy::FinalConstruct()
47{
48 mfMultiOperation = false;
49 muOtherProgressStartWeight = 0;
50 muOtherProgressWeight = 0;
51 muOtherProgressStartOperation = 0;
52
53 HRESULT rc = Progress::FinalConstruct();
54 return rc;
55}
56
57/**
58 * Initalize it as a one operation Progress object.
59 *
60 * This is used by SessionMachine::OnSessionEnd.
61 */
62HRESULT ProgressProxy::init(
63#if !defined (VBOX_COM_INPROC)
64 VirtualBox *pParent,
65#endif
66 IUnknown *pInitiator,
67 CBSTR bstrDescription,
68 BOOL fCancelable)
69{
70 mfMultiOperation = false;
71 muOtherProgressStartWeight = 1;
72 muOtherProgressWeight = 1;
73 muOtherProgressStartOperation = 1;
74
75 return Progress::init(
76#if !defined (VBOX_COM_INPROC)
77 pParent,
78#endif
79 pInitiator,
80 bstrDescription,
81 fCancelable,
82 1 /* cOperations */,
83 1 /* ulTotalOperationsWeight */,
84 bstrDescription /* bstrFirstOperationDescription */,
85 1 /* ulFirstOperationWeight */,
86 NULL /* pId */);
87}
88
89/**
90 * Initialize for proxying one other progress object.
91 *
92 * This is tailored explicitly for the openRemoteSession code, so we start out
93 * with one operation where we don't have any remote object (powerUp). Then a
94 * remote object is added and stays with us till the end.
95 *
96 * The user must do normal completion notification or risk leave the threads
97 * waiting forever!
98 */
99HRESULT ProgressProxy::init(
100#if !defined (VBOX_COM_INPROC)
101 VirtualBox *pParent,
102#endif
103 IUnknown *pInitiator,
104 CBSTR bstrDescription,
105 BOOL fCancelable,
106 ULONG uTotalOperationsWeight,
107 CBSTR bstrFirstOperationDescription,
108 ULONG uFirstOperationWeight,
109 ULONG cOtherProgressObjectOperations)
110{
111 mfMultiOperation = false;
112 muOtherProgressStartWeight = uFirstOperationWeight;
113 muOtherProgressWeight = uTotalOperationsWeight - uFirstOperationWeight;
114 muOtherProgressStartOperation = 1;
115
116 return Progress::init(
117#if !defined (VBOX_COM_INPROC)
118 pParent,
119#endif
120 pInitiator,
121 bstrDescription,
122 fCancelable,
123 1 + cOtherProgressObjectOperations /* cOperations */,
124 uTotalOperationsWeight,
125 bstrFirstOperationDescription,
126 uFirstOperationWeight,
127 NULL);
128}
129
130void ProgressProxy::FinalRelease()
131{
132 uninit();
133 mfMultiOperation = false;
134 muOtherProgressStartWeight = 0;
135 muOtherProgressWeight = 0;
136 muOtherProgressStartOperation = 0;
137}
138
139void ProgressProxy::uninit()
140{
141 LogFlowThisFunc(("\n"));
142
143 mptrOtherProgress.setNull();
144 Progress::uninit();
145}
146
147// Public methods
148////////////////////////////////////////////////////////////////////////////////
149
150/** Just a wrapper so we can automatically do the handover before setting
151 * the result locally. */
152HRESULT ProgressProxy::notifyComplete(HRESULT aResultCode)
153{
154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
155 clearOtherProgressObjectInternal(true /* fEarly */);
156 HRESULT hrc = S_OK;
157 if (!mCompleted)
158 hrc = Progress::notifyComplete(aResultCode);
159 return hrc;
160}
161
162/** Just a wrapper so we can automatically do the handover before setting
163 * the result locally. */
164HRESULT ProgressProxy::notifyComplete(HRESULT aResultCode,
165 const GUID &aIID,
166 const Bstr &aComponent,
167 const char *aText,
168 ...)
169{
170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
171 clearOtherProgressObjectInternal(true /* fEarly */);
172
173 HRESULT hrc = S_OK;
174 if (!mCompleted)
175 {
176 va_list va;
177 va_start(va, aText);
178 HRESULT hrc = Progress::notifyCompleteV(aResultCode, aIID, aComponent, aText, va);
179 va_end(va);
180 }
181 return hrc;
182}
183
184/**
185 * Sets the other progress object unless the operation has been completed /
186 * canceled already.
187 *
188 * @returns false if failed/canceled, true if not.
189 * @param pOtherProgress The other progress object. Must not be NULL.
190 */
191bool ProgressProxy::setOtherProgressObject(IProgress *pOtherProgress)
192{
193 LogFlowThisFunc(("setOtherProgressObject: %p\n", pOtherProgress));
194 ComPtr<IProgress> ptrOtherProgress = pOtherProgress;
195
196 /*
197 * Query information from the other progress object before we grab the
198 * lock.
199 */
200 ULONG cOperations;
201 HRESULT hrc = pOtherProgress->COMGETTER(OperationCount)(&cOperations);
202 if (FAILED(hrc))
203 cOperations = 1;
204
205 Bstr bstrOperationDescription;
206 hrc = pOtherProgress->COMGETTER(Description)(bstrOperationDescription.asOutParam());
207 if (FAILED(hrc))
208 bstrOperationDescription = "oops";
209
210
211 /*
212 * Take the lock and check for cancelation, cancel the other object if
213 * we've been canceled already.
214 */
215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
216
217 BOOL fCompletedOrCanceled = mCompleted || mCanceled;
218 if (!fCompletedOrCanceled)
219 {
220 /*
221 * Advance to the next object and operation. If the other object has
222 * more operations than anticipated, adjust our internal count.
223 */
224 mptrOtherProgress = ptrOtherProgress;
225 mfMultiOperation = cOperations > 1;
226
227 muOtherProgressStartWeight = m_ulOperationsCompletedWeight + m_ulCurrentOperationWeight;
228 muOtherProgressWeight = m_ulTotalOperationsWeight - muOtherProgressStartWeight;
229 Progress::SetNextOperation(bstrOperationDescription, muOtherProgressWeight);
230
231 muOtherProgressStartOperation = m_ulCurrentOperation;
232 m_cOperations = cOperations + m_ulCurrentOperation;
233
234 /*
235 * Check for cancelation and completion.
236 */
237 BOOL f;
238 hrc = ptrOtherProgress->COMGETTER(Completed)(&f);
239 fCompletedOrCanceled = FAILED(hrc) || f;
240
241 if (!fCompletedOrCanceled)
242 {
243 hrc = ptrOtherProgress->COMGETTER(Canceled)(&f);
244 fCompletedOrCanceled = SUCCEEDED(hrc) && f;
245 }
246
247 if (fCompletedOrCanceled)
248 {
249 LogFlowThisFunc(("Other object completed or canceled, clearing...\n"));
250 clearOtherProgressObjectInternal(false /*fEarly*/);
251 }
252 else
253 {
254 /*
255 * Finally, mirror the cancelable property.
256 * Note! Note necessary if we do passthru!
257 */
258 if (mCancelable)
259 {
260 hrc = ptrOtherProgress->COMGETTER(Cancelable)(&f);
261 if (SUCCEEDED(hrc) && !f)
262 {
263 LogFlowThisFunc(("The other progress object is not cancelable\n"));
264 mCancelable = FALSE;
265 }
266 }
267 }
268 }
269 else
270 {
271 LogFlowThisFunc(("mCompleted=%RTbool mCanceled=%RTbool - Canceling the other progress object!\n",
272 mCompleted, mCanceled));
273 hrc = ptrOtherProgress->Cancel();
274 LogFlowThisFunc(("Cancel -> %Rhrc", hrc));
275 }
276
277 LogFlowThisFunc(("Returns %RTbool\n", !fCompletedOrCanceled));
278 return !fCompletedOrCanceled;
279}
280
281// Internal methods.
282////////////////////////////////////////////////////////////////////////////////
283
284
285/**
286 * Clear the other progress object reference, first copying over its state.
287 *
288 * This is used internally when completion is signalled one way or another.
289 *
290 * @param fEarly Early clearing or not.
291 */
292void ProgressProxy::clearOtherProgressObjectInternal(bool fEarly)
293{
294 if (mptrOtherProgress.isNotNull())
295 {
296 ComPtr<IProgress> ptrOtherProgress = mptrOtherProgress;
297 mptrOtherProgress.setNull();
298 copyProgressInfo(ptrOtherProgress, fEarly);
299 }
300}
301
302/**
303 * Called to copy over the progress information from @a pOtherProgress.
304 *
305 * @param pOtherProgress The source of the information.
306 * @param fEarly Early copy.
307 *
308 * @note The caller owns the write lock and as cleared mptrOtherProgress
309 * already (or we might recurse forever)!
310 */
311void ProgressProxy::copyProgressInfo(IProgress *pOtherProgress, bool fEarly)
312{
313 HRESULT hrc;
314 LogFlowThisFunc(("\n"));
315
316 /*
317 * No point in doing this if the progress object was canceled already.
318 */
319 if (!mCanceled)
320 {
321 /* Detect if the other progress object was canceled. */
322 BOOL fCanceled;
323 hrc = pOtherProgress->COMGETTER(Canceled)(&fCanceled); AssertComRC(hrc);
324 if (FAILED(hrc))
325 fCanceled = FALSE;
326 if (fCanceled)
327 {
328 LogFlowThisFunc(("Canceled\n"));
329 mCanceled = TRUE;
330 if (m_pfnCancelCallback)
331 m_pfnCancelCallback(m_pvCancelUserArg);
332 }
333 else
334 {
335 /* Has it completed? */
336 BOOL fCompleted;
337 hrc = pOtherProgress->COMGETTER(Completed)(&fCompleted); AssertComRC(hrc);
338 if (FAILED(hrc))
339 fCompleted = TRUE;
340 Assert(fCompleted || fEarly);
341 if (fCompleted)
342 {
343 /* Check the result. */
344 LONG hrcResult;
345 hrc = pOtherProgress->COMGETTER(ResultCode)(&hrcResult); AssertComRC(hrc);
346 if (FAILED(hrc))
347 hrcResult = hrc;
348 if (SUCCEEDED((HRESULT)hrcResult))
349 LogFlowThisFunc(("Succeeded\n"));
350 else
351 {
352 /* Get the error information. */
353 ComPtr<IVirtualBoxErrorInfo> ptrErrorInfo;
354 hrc = pOtherProgress->COMGETTER(ErrorInfo)(ptrErrorInfo.asOutParam());
355 if (SUCCEEDED(hrc))
356 {
357 Bstr bstrIID;
358 hrc = ptrErrorInfo->COMGETTER(InterfaceID)(bstrIID.asOutParam());
359 if (FAILED(hrc))
360 bstrIID.setNull();
361
362 Bstr bstrComponent;
363 hrc = ptrErrorInfo->COMGETTER(Component)(bstrComponent.asOutParam());
364 if (FAILED(hrc))
365 bstrComponent = "failed";
366
367 Bstr bstrText;
368 hrc = ptrErrorInfo->COMGETTER(Text)(bstrText.asOutParam());
369 if (FAILED(hrc))
370 bstrText = "<failed>";
371
372 Utf8Str strText(bstrText);
373 LogFlowThisFunc(("Got ErrorInfo(%s); hrcResult=%Rhrc\n", strText.c_str(), hrcResult));
374 Progress::notifyComplete((HRESULT)hrcResult, Guid(bstrIID), bstrComponent, "%s", strText.c_str());
375 }
376 else
377 {
378 LogFlowThisFunc(("ErrorInfo failed with hrc=%Rhrc; hrcResult=%Rhrc\n", hrc, hrcResult));
379 Progress::notifyComplete((HRESULT)hrcResult);
380 }
381 }
382 }
383 else
384 LogFlowThisFunc(("Not completed\n"));
385 }
386 }
387 else
388 LogFlowThisFunc(("Already canceled\n"));
389
390 /*
391 * Did cancelable state change (point of no return)?
392 */
393 if (mCancelable)
394 {
395 BOOL fCancelable;
396 hrc = pOtherProgress->COMGETTER(Cancelable)(&fCancelable); AssertComRC(hrc);
397 if (SUCCEEDED(hrc) && !fCancelable)
398 {
399 LogFlowThisFunc(("point-of-no-return reached\n"));
400 mCancelable = FALSE;
401 }
402 }
403}
404
405
406// IProgress properties
407////////////////////////////////////////////////////////////////////////////////
408
409STDMETHODIMP ProgressProxy::COMGETTER(Cancelable)(BOOL *aCancelable)
410{
411 CheckComArgOutPointerValid(aCancelable);
412
413 AutoCaller autoCaller(this);
414 HRESULT hrc = autoCaller.rc();
415 if (SUCCEEDED(hrc))
416 {
417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
418
419 /* ASSUME: The cancelable property can only change to FALSE. */
420 if (!mCancelable || mptrOtherProgress.isNull())
421 *aCancelable = mCancelable;
422 else
423 {
424 hrc = mptrOtherProgress->COMGETTER(Cancelable)(aCancelable);
425 if (SUCCEEDED(hrc) && !*aCancelable)
426 {
427 LogFlowThisFunc(("point-of-no-return reached\n"));
428 mCancelable = FALSE;
429 }
430 }
431 }
432 return hrc;
433}
434
435STDMETHODIMP ProgressProxy::COMGETTER(Percent)(ULONG *aPercent)
436{
437 CheckComArgOutPointerValid(aPercent);
438
439 AutoCaller autoCaller(this);
440 HRESULT hrc = autoCaller.rc();
441 if (SUCCEEDED(hrc))
442 {
443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
444
445 if (mptrOtherProgress.isNull())
446 hrc = Progress::COMGETTER(Percent)(aPercent);
447 else
448 {
449 /*
450 * Get the overall percent of the other object and adjust it with
451 * the weighting given to the period before proxying started.
452 */
453 ULONG uPct;
454 hrc = mptrOtherProgress->COMGETTER(Percent)(&uPct);
455 if (SUCCEEDED(hrc))
456 {
457 double rdPercent = ((double)uPct / 100 * muOtherProgressWeight + muOtherProgressStartWeight)
458 / m_ulTotalOperationsWeight * 100;
459 *aPercent = RT_MIN((ULONG)rdPercent, 99); /* mptrOtherProgress is cleared when its completed, so we can never return 100%. */
460 }
461 }
462 }
463 return hrc;
464}
465
466STDMETHODIMP ProgressProxy::COMGETTER(TimeRemaining)(LONG *aTimeRemaining)
467{
468 CheckComArgOutPointerValid(aTimeRemaining);
469
470 AutoCaller autoCaller(this);
471 HRESULT hrc = autoCaller.rc();
472 if (SUCCEEDED(hrc))
473 {
474 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
475
476 if (mptrOtherProgress.isNull())
477 hrc = Progress::COMGETTER(TimeRemaining)(aTimeRemaining);
478 else
479 hrc = mptrOtherProgress->COMGETTER(TimeRemaining)(aTimeRemaining);
480 }
481 return hrc;
482}
483
484STDMETHODIMP ProgressProxy::COMGETTER(Completed)(BOOL *aCompleted)
485{
486 /* Not proxied since we EXPECT a normal completion notification call. */
487 return Progress::COMGETTER(Completed)(aCompleted);
488}
489
490STDMETHODIMP ProgressProxy::COMGETTER(Canceled)(BOOL *aCanceled)
491{
492 CheckComArgOutPointerValid(aCanceled);
493
494 AutoCaller autoCaller(this);
495 HRESULT hrc = autoCaller.rc();
496 if (SUCCEEDED(hrc))
497 {
498 /* Check the local data first, then the other object. */
499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
500 hrc = Progress::COMGETTER(Canceled)(aCanceled);
501 if ( SUCCEEDED(hrc)
502 && !*aCanceled
503 && mptrOtherProgress.isNotNull()
504 && mCancelable)
505 {
506 hrc = mptrOtherProgress->COMGETTER(Canceled)(aCanceled);
507 if (SUCCEEDED(hrc) && *aCanceled)
508 /* This will not complete the object, only mark it as canceled. */
509 clearOtherProgressObjectInternal(false /*fEarly*/);
510 }
511 }
512 return hrc;
513}
514
515STDMETHODIMP ProgressProxy::COMGETTER(ResultCode)(LONG *aResultCode)
516{
517 /* Not proxied since we EXPECT a normal completion notification call. */
518 return Progress::COMGETTER(ResultCode)(aResultCode);
519}
520
521STDMETHODIMP ProgressProxy::COMGETTER(ErrorInfo)(IVirtualBoxErrorInfo **aErrorInfo)
522{
523 /* Not proxied since we EXPECT a normal completion notification call. */
524 return Progress::COMGETTER(ErrorInfo)(aErrorInfo);
525}
526
527STDMETHODIMP ProgressProxy::COMGETTER(Operation)(ULONG *aOperation)
528{
529 CheckComArgOutPointerValid(aOperation);
530
531 AutoCaller autoCaller(this);
532 HRESULT hrc = autoCaller.rc();
533 if (SUCCEEDED(hrc))
534 {
535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
536 if (mptrOtherProgress.isNull())
537 hrc = Progress::COMGETTER(Operation)(aOperation);
538 else
539 {
540 ULONG uCurOtherOperation;
541 hrc = mptrOtherProgress->COMGETTER(Operation)(&uCurOtherOperation);
542 if (SUCCEEDED(hrc))
543 *aOperation = uCurOtherOperation + muOtherProgressStartOperation;
544 }
545 }
546 return hrc;
547}
548
549STDMETHODIMP ProgressProxy::COMGETTER(OperationDescription)(BSTR *aOperationDescription)
550{
551 CheckComArgOutPointerValid(aOperationDescription);
552
553 AutoCaller autoCaller(this);
554 HRESULT hrc = autoCaller.rc();
555 if (SUCCEEDED(hrc))
556 {
557 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
558 if (mptrOtherProgress.isNull() || !mfMultiOperation)
559 hrc = Progress::COMGETTER(OperationDescription)(aOperationDescription);
560 else
561 hrc = mptrOtherProgress->COMGETTER(OperationDescription)(aOperationDescription);
562 }
563 return hrc;
564}
565
566STDMETHODIMP ProgressProxy::COMGETTER(OperationPercent)(ULONG *aOperationPercent)
567{
568 CheckComArgOutPointerValid(aOperationPercent);
569
570 AutoCaller autoCaller(this);
571 HRESULT hrc = autoCaller.rc();
572 if (SUCCEEDED(hrc))
573 {
574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
575 if (mptrOtherProgress.isNull() || !mfMultiOperation)
576 hrc = Progress::COMGETTER(OperationPercent)(aOperationPercent);
577 else
578 hrc = mptrOtherProgress->COMGETTER(OperationPercent)(aOperationPercent);
579 }
580 return hrc;
581}
582
583STDMETHODIMP ProgressProxy::COMSETTER(Timeout)(ULONG aTimeout)
584{
585 /* Not currently supported. */
586 NOREF(aTimeout);
587 AssertFailed();
588 return E_NOTIMPL;
589}
590
591STDMETHODIMP ProgressProxy::COMGETTER(Timeout)(ULONG *aTimeout)
592{
593 /* Not currently supported. */
594 CheckComArgOutPointerValid(aTimeout);
595
596 AssertFailed();
597 return E_NOTIMPL;
598}
599
600// IProgress methods
601/////////////////////////////////////////////////////////////////////////////
602
603STDMETHODIMP ProgressProxy::WaitForCompletion(LONG aTimeout)
604{
605 HRESULT hrc;
606 LogFlowThisFuncEnter();
607 LogFlowThisFunc(("aTimeout=%d\n", aTimeout));
608
609 /* No need to wait on the proxied object for these since we'll get the
610 normal completion notifications. */
611 hrc = Progress::WaitForCompletion(aTimeout);
612
613 LogFlowThisFuncLeave();
614 return hrc;
615}
616
617STDMETHODIMP ProgressProxy::WaitForOperationCompletion(ULONG aOperation, LONG aTimeout)
618{
619 LogFlowThisFuncEnter();
620 LogFlowThisFunc(("aOperation=%d aTimeout=%d\n", aOperation, aTimeout));
621
622 AutoCaller autoCaller(this);
623 HRESULT hrc = autoCaller.rc();
624 if (SUCCEEDED(hrc))
625 {
626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
627
628 CheckComArgExpr(aOperation, aOperation < m_cOperations);
629
630 /*
631 * Check if we can wait locally.
632 */
633 if ( aOperation + 1 == m_cOperations /* final operation */
634 || mptrOtherProgress.isNull())
635 {
636 /* ASSUMES that Progress::WaitForOperationCompletion is using
637 AutoWriteLock::leave() as it saves us from duplicating the code! */
638 hrc = Progress::WaitForOperationCompletion(aOperation, aTimeout);
639 }
640 else
641 {
642 LogFlowThisFunc(("calling the other object...\n"));
643 ComPtr<IProgress> ptrOtherProgress = mptrOtherProgress;
644 alock.release();
645
646 hrc = ptrOtherProgress->WaitForOperationCompletion(aOperation, aTimeout);
647 }
648 }
649
650 LogFlowThisFuncLeave();
651 return hrc;
652}
653
654STDMETHODIMP ProgressProxy::Cancel()
655{
656 LogFlowThisFunc(("\n"));
657 AutoCaller autoCaller(this);
658 HRESULT hrc = autoCaller.rc();
659 if (SUCCEEDED(hrc))
660 {
661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
662 if (mptrOtherProgress.isNull() || !mCancelable)
663 hrc = Progress::Cancel();
664 else
665 {
666 hrc = mptrOtherProgress->Cancel();
667 if (SUCCEEDED(hrc))
668 clearOtherProgressObjectInternal(false /*fEarly*/);
669 }
670 }
671
672 LogFlowThisFunc(("returns %Rhrc\n", hrc));
673 return hrc;
674}
675
676STDMETHODIMP ProgressProxy::SetCurrentOperationProgress(ULONG aPercent)
677{
678 /* Not supported - why do we actually expose this? */
679 NOREF(aPercent);
680 return E_NOTIMPL;
681}
682
683STDMETHODIMP ProgressProxy::SetNextOperation(IN_BSTR bstrNextOperationDescription, ULONG ulNextOperationsWeight)
684{
685 /* Not supported - why do we actually expose this? */
686 NOREF(bstrNextOperationDescription);
687 NOREF(ulNextOperationsWeight);
688 return E_NOTIMPL;
689}
690
691/* vi: set tabstop=4 shiftwidth=4 expandtab: */
692
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