VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/VFSExplorerImpl.cpp@ 50213

Last change on this file since 50213 was 49871, checked in by vboxsync, 11 years ago

6813 - User server side API wrapper code in all interfaces.. stage 4 rev 1

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