VirtualBox

source: vbox/trunk/src/VBox/Main/VFSExplorerImpl.cpp@ 25860

Last change on this file since 25860 was 25860, checked in by vboxsync, 14 years ago

Main: cleanup: get rid of VirtualBoxBaseProto, move AutoCaller*/*Span* classes out of VirtualBoxBaseProto class scope and into separate header; move CombinedProgress into separate header (it's only used by Console any more)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.5 KB
Line 
1/* $Id: VFSExplorerImpl.cpp 25860 2010-01-15 13:27:26Z vboxsync $ */
2/** @file
3 *
4 * IVFSExplorer COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2009 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/dir.h>
24#include <iprt/path.h>
25#include <iprt/file.h>
26#include <iprt/s3.h>
27
28#include <VBox/com/array.h>
29
30#include <VBox/param.h>
31#include <VBox/version.h>
32
33#include "VFSExplorerImpl.h"
34#include "VirtualBoxImpl.h"
35#include "ProgressImpl.h"
36
37#include "AutoCaller.h"
38#include "Logging.h"
39
40////////////////////////////////////////////////////////////////////////////////
41//
42// VFSExplorer definitions
43//
44////////////////////////////////////////////////////////////////////////////////
45
46/* opaque private instance data of VFSExplorer class */
47struct VFSExplorer::Data
48{
49 struct DirEntry
50 {
51 DirEntry(Utf8Str aName, VFSFileType_T aType)
52 : name(aName)
53 , type(aType) {}
54
55 Utf8Str name;
56 VFSFileType_T type;
57 };
58
59 VFSType_T storageType;
60 Utf8Str strUsername;
61 Utf8Str strPassword;
62 Utf8Str strHostname;
63 Utf8Str strPath;
64 Utf8Str strBucket;
65
66 std::list<DirEntry> entryList;
67};
68
69DEFINE_EMPTY_CTOR_DTOR(VFSExplorer)
70
71/**
72 * VFSExplorer COM initializer.
73 * @param
74 * @return
75 */
76HRESULT VFSExplorer::init(VFSType_T aType, Utf8Str aFilePath, Utf8Str aHostname, Utf8Str aUsername, Utf8Str aPassword, VirtualBox *aVirtualBox)
77{
78 /* Enclose the state transition NotReady->InInit->Ready */
79 AutoInitSpan autoInitSpan(this);
80 AssertReturn(autoInitSpan.isOk(), E_FAIL);
81
82 /* Weak reference to a VirtualBox object */
83 unconst(mVirtualBox) = aVirtualBox;
84
85 /* initialize data */
86 m = new Data;
87
88 m->storageType = aType;
89 m->strPath = aFilePath;
90 m->strHostname = aHostname;
91 m->strUsername = aUsername;
92 m->strPassword = aPassword;
93
94 if (m->storageType == VFSType_S3)
95 {
96 size_t bpos = aFilePath.find("/", 1);
97 if (bpos != Utf8Str::npos)
98 {
99 m->strBucket = aFilePath.substr(1, bpos - 1); /* The bucket without any slashes */
100 aFilePath = aFilePath.substr(bpos); /* The rest of the file path */
101 }
102 }
103
104 /* Confirm a successful initialization */
105 autoInitSpan.setSucceeded();
106
107 return S_OK;
108}
109
110/**
111 * VFSExplorer COM uninitializer.
112 * @return
113 */
114void VFSExplorer::uninit()
115{
116 delete m;
117 m = NULL;
118}
119
120/**
121 * Public method implementation.
122 * @param
123 * @return
124 */
125STDMETHODIMP VFSExplorer::COMGETTER(Path)(BSTR *aPath)
126{
127 if (!aPath)
128 return E_POINTER;
129
130 AutoCaller autoCaller(this);
131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
132
133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
134
135 Bstr bstrPath(m->strPath);
136 bstrPath.cloneTo(aPath);
137
138 return S_OK;
139}
140
141STDMETHODIMP VFSExplorer::COMGETTER(Type)(VFSType_T *aType)
142{
143 if (!aType)
144 return E_POINTER;
145
146 AutoCaller autoCaller(this);
147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
148
149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
150
151 *aType = m->storageType;
152
153 return S_OK;
154}
155
156struct VFSExplorer::TaskVFSExplorer
157{
158 enum TaskType
159 {
160 Update,
161 Delete
162 };
163
164 TaskVFSExplorer(TaskType aTaskType, VFSExplorer *aThat, Progress *aProgress)
165 : taskType(aTaskType),
166 pVFSExplorer(aThat),
167 progress(aProgress),
168 rc(S_OK)
169 {}
170 ~TaskVFSExplorer() {}
171
172 int startThread();
173 static int taskThread(RTTHREAD aThread, void *pvUser);
174 static int uploadProgress(unsigned uPercent, void *pvUser);
175
176 TaskType taskType;
177 VFSExplorer *pVFSExplorer;
178 ComObjPtr<Progress> progress;
179 HRESULT rc;
180
181 /* task data */
182 std::list<Utf8Str> filenames;
183};
184
185int VFSExplorer::TaskVFSExplorer::startThread()
186{
187 int vrc = RTThreadCreate(NULL, VFSExplorer::TaskVFSExplorer::taskThread, this,
188 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
189 "Explorer::Task");
190
191 ComAssertMsgRCRet(vrc,
192 ("Could not create taskThreadVFS (%Rrc)\n", vrc), E_FAIL);
193
194 return vrc;
195}
196
197/* static */
198DECLCALLBACK(int) VFSExplorer::TaskVFSExplorer::taskThread(RTTHREAD /* aThread */, void *pvUser)
199{
200 std::auto_ptr<TaskVFSExplorer> task(static_cast<TaskVFSExplorer*>(pvUser));
201 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
202
203 VFSExplorer *pVFSExplorer = task->pVFSExplorer;
204
205 LogFlowFuncEnter();
206 LogFlowFunc(("VFSExplorer %p\n", pVFSExplorer));
207
208 HRESULT rc = S_OK;
209
210 switch(task->taskType)
211 {
212 case TaskVFSExplorer::Update:
213 {
214 if (pVFSExplorer->m->storageType == VFSType_File)
215 rc = pVFSExplorer->updateFS(task.get());
216 else if (pVFSExplorer->m->storageType == VFSType_S3)
217 rc = pVFSExplorer->updateS3(task.get());
218 break;
219 }
220 case TaskVFSExplorer::Delete:
221 {
222 if (pVFSExplorer->m->storageType == VFSType_File)
223 rc = pVFSExplorer->deleteFS(task.get());
224 else if (pVFSExplorer->m->storageType == VFSType_S3)
225 rc = pVFSExplorer->deleteS3(task.get());
226 break;
227 }
228 }
229
230 LogFlowFunc(("rc=%Rhrc\n", rc));
231 LogFlowFuncLeave();
232
233 return VINF_SUCCESS;
234}
235
236/* static */
237int VFSExplorer::TaskVFSExplorer::uploadProgress(unsigned uPercent, void *pvUser)
238{
239 VFSExplorer::TaskVFSExplorer* pTask = *(VFSExplorer::TaskVFSExplorer**)pvUser;
240
241 if (pTask &&
242 !pTask->progress.isNull())
243 {
244 BOOL fCanceled;
245 pTask->progress->COMGETTER(Canceled)(&fCanceled);
246 if (fCanceled)
247 return -1;
248 pTask->progress->SetCurrentOperationProgress(uPercent);
249 }
250 return VINF_SUCCESS;
251}
252
253VFSFileType_T VFSExplorer::RTToVFSFileType(int aType) const
254{
255 VFSFileType_T t;
256 switch(aType)
257 {
258 default:
259 case RTDIRENTRYTYPE_UNKNOWN: t = VFSFileType_Unknown; break;
260 case RTDIRENTRYTYPE_FIFO: t = VFSFileType_Fifo; break;
261 case RTDIRENTRYTYPE_DEV_CHAR: t = VFSFileType_DevChar; break;
262 case RTDIRENTRYTYPE_DIRECTORY: t = VFSFileType_Directory; break;
263 case RTDIRENTRYTYPE_DEV_BLOCK: t = VFSFileType_DevBlock; break;
264 case RTDIRENTRYTYPE_FILE: t = VFSFileType_File; break;
265 case RTDIRENTRYTYPE_SYMLINK: t = VFSFileType_SymLink; break;
266 case RTDIRENTRYTYPE_SOCKET: t = VFSFileType_Socket; break;
267 case RTDIRENTRYTYPE_WHITEOUT: t = VFSFileType_WhiteOut; break;
268 }
269 return t;
270}
271
272HRESULT VFSExplorer::updateFS(TaskVFSExplorer *aTask)
273{
274 LogFlowFuncEnter();
275
276 AutoCaller autoCaller(this);
277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
278
279 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
280
281 HRESULT rc = S_OK;
282
283 std::list<VFSExplorer::Data::DirEntry> fileList;
284 char *pszPath = NULL;
285 PRTDIR pDir = NULL;
286 try
287 {
288 pszPath = RTStrDup(m->strPath.c_str());
289 RTPathStripFilename(pszPath);
290 int vrc = RTDirOpen(&pDir, pszPath);
291 if (RT_FAILURE(vrc))
292 throw setError(VBOX_E_FILE_ERROR, tr ("Can't open directory '%s' (%Rrc)"), pszPath, vrc);
293
294 if(aTask->progress)
295 aTask->progress->SetCurrentOperationProgress(33);
296 RTDIRENTRY entry;
297 while (RT_SUCCESS(vrc))
298 {
299 vrc = RTDirRead(pDir, &entry, NULL);
300 if (RT_SUCCESS(vrc))
301 {
302 Utf8Str name(entry.szName);
303 if (name != "." &&
304 name != "..")
305 fileList.push_back(VFSExplorer::Data::DirEntry(name, RTToVFSFileType(entry.enmType)));
306 }
307 }
308 if(aTask->progress)
309 aTask->progress->SetCurrentOperationProgress(66);
310 }
311 catch(HRESULT aRC)
312 {
313 rc = aRC;
314 }
315
316 /* Clean up */
317 if (pszPath)
318 RTStrFree(pszPath);
319 if (pDir)
320 RTDirClose(pDir);
321
322 if(aTask->progress)
323 aTask->progress->SetCurrentOperationProgress(99);
324
325 /* Assign the result on success (this clears the old list) */
326 if (rc == S_OK)
327 m->entryList.assign(fileList.begin(), fileList.end());
328
329 aTask->rc = rc;
330
331 if (!aTask->progress.isNull())
332 aTask->progress->notifyComplete(rc);
333
334 LogFlowFunc(("rc=%Rhrc\n", rc));
335 LogFlowFuncLeave();
336
337 return VINF_SUCCESS;
338}
339
340HRESULT VFSExplorer::deleteFS(TaskVFSExplorer *aTask)
341{
342 LogFlowFuncEnter();
343
344 AutoCaller autoCaller(this);
345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
346
347 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
348
349 HRESULT rc = S_OK;
350
351 float fPercentStep = 100.0f / aTask->filenames.size();
352 try
353 {
354 char szPath[RTPATH_MAX];
355 std::list<Utf8Str>::const_iterator it;
356 size_t i = 0;
357 for (it = aTask->filenames.begin();
358 it != aTask->filenames.end();
359 ++it, ++i)
360 {
361 memcpy(szPath, m->strPath.c_str(), strlen(m->strPath.c_str()) + 1);
362 RTPathStripFilename(szPath);
363 RTPathAppend(szPath, sizeof(szPath), (*it).c_str());
364 int vrc = RTFileDelete(szPath);
365 if (RT_FAILURE(vrc))
366 throw setError(VBOX_E_FILE_ERROR, tr ("Can't delete file '%s' (%Rrc)"), szPath, vrc);
367 if(aTask->progress)
368 aTask->progress->SetCurrentOperationProgress((ULONG)(fPercentStep * i));
369 }
370 }
371 catch(HRESULT aRC)
372 {
373 rc = aRC;
374 }
375
376 aTask->rc = rc;
377
378 if (!aTask->progress.isNull())
379 aTask->progress->notifyComplete(rc);
380
381 LogFlowFunc(("rc=%Rhrc\n", rc));
382 LogFlowFuncLeave();
383
384 return VINF_SUCCESS;
385}
386
387HRESULT VFSExplorer::updateS3(TaskVFSExplorer *aTask)
388{
389 LogFlowFuncEnter();
390
391 AutoCaller autoCaller(this);
392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
393
394 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
395
396 HRESULT rc = S_OK;
397
398 RTS3 hS3 = NULL;
399 std::list<VFSExplorer::Data::DirEntry> fileList;
400 try
401 {
402 int vrc = RTS3Create(&hS3, m->strUsername.c_str(), m->strPassword.c_str(), m->strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
403 if (RT_FAILURE(vrc))
404 throw setError(E_FAIL, tr ("Can't open S3 storage service (%Rrc)"), vrc);
405
406 RTS3SetProgressCallback(hS3, VFSExplorer::TaskVFSExplorer::uploadProgress, &aTask);
407 /* Do we need the list of buckets or keys? */
408 if (m->strBucket.isEmpty())
409 {
410 PCRTS3BUCKETENTRY pBuckets = NULL;
411 vrc = RTS3GetBuckets(hS3, &pBuckets);
412 if (RT_FAILURE(vrc))
413 throw setError(E_FAIL, tr ("Can't get buckets (%Rrc)"), vrc);
414
415 PCRTS3BUCKETENTRY pTmpBuckets = pBuckets;
416 while (pBuckets)
417 {
418 fileList.push_back(VFSExplorer::Data::DirEntry(pBuckets->pszName, VFSFileType_Directory));
419 pBuckets = pBuckets->pNext;
420 }
421 RTS3BucketsDestroy(pTmpBuckets);
422 }
423 else
424 {
425 PCRTS3KEYENTRY pKeys = NULL;
426 vrc = RTS3GetBucketKeys(hS3, m->strBucket.c_str(), &pKeys);
427 if (RT_FAILURE(vrc))
428 throw setError(E_FAIL, tr ("Can't get keys for bucket (%Rrc)"), vrc);
429
430 PCRTS3KEYENTRY pTmpKeys = pKeys;
431 while (pKeys)
432 {
433 Utf8Str name(pKeys->pszName);
434 fileList.push_back(VFSExplorer::Data::DirEntry(pKeys->pszName, VFSFileType_File));
435 pKeys = pKeys->pNext;
436 }
437 RTS3KeysDestroy(pTmpKeys);
438 }
439 }
440 catch(HRESULT aRC)
441 {
442 rc = aRC;
443 }
444
445 if (hS3 != NULL)
446 RTS3Destroy(hS3);
447
448 /* Assign the result on success (this clears the old list) */
449 if (rc == S_OK)
450 m->entryList.assign(fileList.begin(), fileList.end());
451
452 aTask->rc = rc;
453
454 if (!aTask->progress.isNull())
455 aTask->progress->notifyComplete(rc);
456
457 LogFlowFunc(("rc=%Rhrc\n", rc));
458 LogFlowFuncLeave();
459
460 return VINF_SUCCESS;
461}
462
463HRESULT VFSExplorer::deleteS3(TaskVFSExplorer *aTask)
464{
465 LogFlowFuncEnter();
466
467 AutoCaller autoCaller(this);
468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
469
470 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
471
472 HRESULT rc = S_OK;
473
474 RTS3 hS3 = NULL;
475 float fPercentStep = 100.0f / aTask->filenames.size();
476 try
477 {
478 int vrc = RTS3Create(&hS3, m->strUsername.c_str(), m->strPassword.c_str(), m->strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
479 if (RT_FAILURE(vrc))
480 throw setError(E_FAIL, tr ("Can't open S3 storage service (%Rrc)"), vrc);
481
482 RTS3SetProgressCallback(hS3, VFSExplorer::TaskVFSExplorer::uploadProgress, &aTask);
483
484 std::list<Utf8Str>::const_iterator it;
485 size_t i = 0;
486 for (it = aTask->filenames.begin();
487 it != aTask->filenames.end();
488 ++it, ++i)
489 {
490 vrc = RTS3DeleteKey(hS3, m->strBucket.c_str(), (*it).c_str());
491 if (RT_FAILURE(vrc))
492 throw setError(VBOX_E_FILE_ERROR, tr ("Can't delete file '%s' (%Rrc)"), (*it).c_str(), vrc);
493 if(aTask->progress)
494 aTask->progress->SetCurrentOperationProgress((ULONG)(fPercentStep * i));
495 }
496 }
497 catch(HRESULT aRC)
498 {
499 rc = aRC;
500 }
501
502 aTask->rc = rc;
503
504 if (hS3 != NULL)
505 RTS3Destroy(hS3);
506
507 if (!aTask->progress.isNull())
508 aTask->progress->notifyComplete(rc);
509
510 LogFlowFunc(("rc=%Rhrc\n", rc));
511 LogFlowFuncLeave();
512
513 return VINF_SUCCESS;
514}
515
516STDMETHODIMP VFSExplorer::Update(IProgress **aProgress)
517{
518 CheckComArgOutPointerValid(aProgress);
519
520 AutoCaller autoCaller(this);
521 if (FAILED(autoCaller.rc())) return autoCaller.rc();
522
523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
524
525 HRESULT rc = S_OK;
526
527 ComObjPtr<Progress> progress;
528 try
529 {
530 Bstr progressDesc = BstrFmt(tr("Update directory info for '%s'"),
531 m->strPath.raw());
532 /* Create the progress object */
533 progress.createObject();
534
535 rc = progress->init(mVirtualBox, static_cast<IVFSExplorer*>(this),
536 progressDesc,
537 TRUE /* aCancelable */);
538 if (FAILED(rc)) throw rc;
539
540 /* Initialize our worker task */
541 std::auto_ptr<TaskVFSExplorer> task(new TaskVFSExplorer(TaskVFSExplorer::Update, this, progress));
542
543 rc = task->startThread();
544 if (FAILED(rc)) throw rc;
545
546 /* Don't destruct on success */
547 task.release();
548 }
549 catch (HRESULT aRC)
550 {
551 rc = aRC;
552 }
553
554 if (SUCCEEDED(rc))
555 /* Return progress to the caller */
556 progress.queryInterfaceTo(aProgress);
557
558 return rc;
559}
560
561STDMETHODIMP VFSExplorer::Cd(IN_BSTR aDir, IProgress **aProgress)
562{
563 CheckComArgNotNull(aDir);
564 CheckComArgOutPointerValid(aProgress);
565
566 return E_NOTIMPL;
567}
568
569STDMETHODIMP VFSExplorer::CdUp(IProgress **aProgress)
570{
571 CheckComArgOutPointerValid(aProgress);
572
573 return E_NOTIMPL;
574}
575
576STDMETHODIMP VFSExplorer::EntryList(ComSafeArrayOut(BSTR, aNames), ComSafeArrayOut(VFSFileType_T, aTypes))
577{
578 if (ComSafeArrayOutIsNull(aNames) ||
579 ComSafeArrayOutIsNull(aTypes))
580 return E_POINTER;
581
582 AutoCaller autoCaller(this);
583 if (FAILED(autoCaller.rc())) return autoCaller.rc();
584
585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
586
587 com::SafeArray<BSTR> sfaNames((ULONG)m->entryList.size());
588 com::SafeArray<ULONG> sfaTypes((VFSFileType_T)m->entryList.size());
589
590 std::list<VFSExplorer::Data::DirEntry>::const_iterator it;
591 size_t i = 0;
592 for (it = m->entryList.begin();
593 it != m->entryList.end();
594 ++it, ++i)
595 {
596 const VFSExplorer::Data::DirEntry &entry = (*it);
597 Bstr bstr(entry.name);
598 bstr.cloneTo(&sfaNames[i]);
599 sfaTypes[i] = entry.type;
600 }
601
602 sfaNames.detachTo(ComSafeArrayOutArg(aNames));
603 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
604
605 return S_OK;
606}
607
608STDMETHODIMP VFSExplorer::Exists(ComSafeArrayIn(IN_BSTR, aNames), ComSafeArrayOut(BSTR, aExists))
609{
610 CheckComArgSafeArrayNotNull(aNames);
611
612 AutoCaller autoCaller(this);
613 if (FAILED(autoCaller.rc())) return autoCaller.rc();
614
615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
616
617 com::SafeArray<IN_BSTR> sfaNames(ComSafeArrayInArg(aNames));
618 std::list<BSTR> listExists;
619
620 std::list<VFSExplorer::Data::DirEntry>::const_iterator it;
621 for (it = m->entryList.begin();
622 it != m->entryList.end();
623 ++it)
624 {
625 const VFSExplorer::Data::DirEntry &entry = (*it);
626 for (size_t a=0; a < sfaNames.size(); ++a)
627 {
628 if (entry.name == RTPathFilename(Utf8Str(sfaNames[a]).c_str()))
629 {
630 BSTR name;
631 Bstr tmp(sfaNames[a]); /* gcc-3.3 cruft */
632 tmp.cloneTo(&name);
633 listExists.push_back(name);
634 }
635 }
636 }
637
638 com::SafeArray<BSTR> sfaExists(listExists);
639 sfaExists.detachTo(ComSafeArrayOutArg(aExists));
640
641 return S_OK;
642}
643
644STDMETHODIMP VFSExplorer::Remove(ComSafeArrayIn(IN_BSTR, aNames), IProgress **aProgress)
645{
646 CheckComArgSafeArrayNotNull(aNames);
647 CheckComArgOutPointerValid(aProgress);
648
649 AutoCaller autoCaller(this);
650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
651
652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
653
654 HRESULT rc = S_OK;
655
656 com::SafeArray<IN_BSTR> sfaNames(ComSafeArrayInArg(aNames));
657
658 ComObjPtr<Progress> progress;
659 try
660 {
661 /* Create the progress object */
662 progress.createObject();
663
664 rc = progress->init(mVirtualBox, static_cast<IVFSExplorer*>(this),
665 Bstr(tr("Delete files")),
666 TRUE /* aCancelable */);
667 if (FAILED(rc)) throw rc;
668
669 /* Initialize our worker task */
670 std::auto_ptr<TaskVFSExplorer> task(new TaskVFSExplorer(TaskVFSExplorer::Delete, this, progress));
671
672 /* Add all filenames to delete as task data */
673 for (size_t a=0; a < sfaNames.size(); ++a)
674 task->filenames.push_back(Utf8Str(sfaNames[a]));
675
676 rc = task->startThread();
677 if (FAILED(rc)) throw rc;
678
679 /* Don't destruct on success */
680 task.release();
681 }
682 catch (HRESULT aRC)
683 {
684 rc = aRC;
685 }
686
687 if (SUCCEEDED(rc))
688 /* Return progress to the caller */
689 progress.queryInterfaceTo(aProgress);
690
691 return rc;
692}
693
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use