5 #include "Common/IntToString.h"
\r
6 #include "Common/MyInitGuid.h"
\r
7 #include "Common/StringConvert.h"
\r
9 #include "Windows/DLL.h"
\r
10 #include "Windows/FileDir.h"
\r
11 #include "Windows/FileFind.h"
\r
12 #include "Windows/FileName.h"
\r
13 #include "Windows/NtCheck.h"
\r
14 #include "Windows/PropVariant.h"
\r
15 #include "Windows/PropVariantConversions.h"
\r
17 #include "../../Common/FileStreams.h"
\r
19 #include "../../Archive/IArchive.h"
\r
21 #include "../../IPassword.h"
\r
22 #include "../../MyVersion.h"
\r
24 // use another CLSIDs, if you want to support other formats (zip, rar, ...).
\r
25 // {23170F69-40C1-278A-1000-000110070000}
\r
26 DEFINE_GUID(CLSID_CFormat7z,
\r
27 0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00);
\r
29 using namespace NWindows;
\r
31 #define kDllName "7z.dll"
\r
33 static const char *kCopyrightString = MY_7ZIP_VERSION
\r
34 " (" kDllName " client) "
\r
35 MY_COPYRIGHT " " MY_DATE;
\r
37 static const char *kHelpString =
\r
38 "Usage: Client7z.exe [a | l | x ] archive.7z [fileName ...]\n"
\r
40 " Client7z.exe a archive.7z f1.txt f2.txt : compress two files to archive.7z\n"
\r
41 " Client7z.exe l archive.7z : List contents of archive.7z\n"
\r
42 " Client7z.exe x archive.7z : eXtract files from archive.7z\n";
\r
45 typedef UINT32 (WINAPI * CreateObjectFunc)(
\r
47 const GUID *interfaceID,
\r
51 void PrintString(const UString &s)
\r
53 printf("%s", (LPCSTR)GetOemString(s));
\r
56 void PrintString(const AString &s)
\r
58 printf("%s", (LPCSTR)s);
\r
66 void PrintStringLn(const AString &s)
\r
72 void PrintError(const AString &s)
\r
79 static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
\r
81 NCOM::CPropVariant prop;
\r
82 RINOK(archive->GetProperty(index, propID, &prop));
\r
83 if (prop.vt == VT_BOOL)
\r
84 result = VARIANT_BOOLToBool(prop.boolVal);
\r
85 else if (prop.vt == VT_EMPTY)
\r
92 static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
\r
94 return IsArchiveItemProp(archive, index, kpidIsDir, result);
\r
98 static const wchar_t *kEmptyFileAlias = L"[Content]";
\r
101 //////////////////////////////////////////////////////////////
\r
102 // Archive Open callback class
\r
105 class CArchiveOpenCallback:
\r
106 public IArchiveOpenCallback,
\r
107 public ICryptoGetTextPassword,
\r
108 public CMyUnknownImp
\r
111 MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
\r
113 STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes);
\r
114 STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes);
\r
116 STDMETHOD(CryptoGetTextPassword)(BSTR *password);
\r
118 bool PasswordIsDefined;
\r
121 CArchiveOpenCallback() : PasswordIsDefined(false) {}
\r
124 STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
\r
129 STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */)
\r
134 STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password)
\r
136 if (!PasswordIsDefined)
\r
138 // You can ask real password here from user
\r
139 // Password = GetPassword(OutStream);
\r
140 // PasswordIsDefined = true;
\r
141 PrintError("Password is not defined");
\r
144 return StringToBstr(Password, password);
\r
148 //////////////////////////////////////////////////////////////
\r
149 // Archive Extracting callback class
\r
151 static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
\r
153 static const char *kTestingString = "Testing ";
\r
154 static const char *kExtractingString = "Extracting ";
\r
155 static const char *kSkippingString = "Skipping ";
\r
157 static const char *kUnsupportedMethod = "Unsupported Method";
\r
158 static const char *kCRCFailed = "CRC Failed";
\r
159 static const char *kDataError = "Data Error";
\r
160 static const char *kUnknownError = "Unknown Error";
\r
162 class CArchiveExtractCallback:
\r
163 public IArchiveExtractCallback,
\r
164 public ICryptoGetTextPassword,
\r
165 public CMyUnknownImp
\r
168 MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
\r
171 STDMETHOD(SetTotal)(UInt64 size);
\r
172 STDMETHOD(SetCompleted)(const UInt64 *completeValue);
\r
174 // IArchiveExtractCallback
\r
175 STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode);
\r
176 STDMETHOD(PrepareOperation)(Int32 askExtractMode);
\r
177 STDMETHOD(SetOperationResult)(Int32 resultEOperationResult);
\r
179 // ICryptoGetTextPassword
\r
180 STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword);
\r
183 CMyComPtr<IInArchive> _archiveHandler;
\r
184 UString _directoryPath; // Output directory
\r
185 UString _filePath; // name inside arcvhive
\r
186 UString _diskFilePath; // full path to file on disk
\r
188 struct CProcessedFileInfo
\r
193 bool AttribDefined;
\r
195 } _processedFileInfo;
\r
197 COutFileStream *_outFileStreamSpec;
\r
198 CMyComPtr<ISequentialOutStream> _outFileStream;
\r
201 void Init(IInArchive *archiveHandler, const UString &directoryPath);
\r
204 bool PasswordIsDefined;
\r
207 CArchiveExtractCallback() : PasswordIsDefined(false) {}
\r
210 void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const UString &directoryPath)
\r
213 _archiveHandler = archiveHandler;
\r
214 _directoryPath = directoryPath;
\r
215 NFile::NName::NormalizeDirPathPrefix(_directoryPath);
\r
218 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */)
\r
223 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */)
\r
228 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index,
\r
229 ISequentialOutStream **outStream, Int32 askExtractMode)
\r
232 _outFileStream.Release();
\r
236 NCOM::CPropVariant prop;
\r
237 RINOK(_archiveHandler->GetProperty(index, kpidPath, &prop));
\r
240 if (prop.vt == VT_EMPTY)
\r
241 fullPath = kEmptyFileAlias;
\r
244 if (prop.vt != VT_BSTR)
\r
246 fullPath = prop.bstrVal;
\r
248 _filePath = fullPath;
\r
251 if (askExtractMode != NArchive::NExtract::NAskMode::kExtract)
\r
256 NCOM::CPropVariant prop;
\r
257 RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop));
\r
258 if (prop.vt == VT_EMPTY)
\r
260 _processedFileInfo.Attrib = 0;
\r
261 _processedFileInfo.AttribDefined = false;
\r
265 if (prop.vt != VT_UI4)
\r
267 _processedFileInfo.Attrib = prop.ulVal;
\r
268 _processedFileInfo.AttribDefined = true;
\r
272 RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.isDir));
\r
275 // Get Modified Time
\r
276 NCOM::CPropVariant prop;
\r
277 RINOK(_archiveHandler->GetProperty(index, kpidMTime, &prop));
\r
278 _processedFileInfo.MTimeDefined = false;
\r
282 // _processedFileInfo.MTime = _utcMTimeDefault;
\r
285 _processedFileInfo.MTime = prop.filetime;
\r
286 _processedFileInfo.MTimeDefined = true;
\r
295 NCOM::CPropVariant prop;
\r
296 RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop));
\r
297 bool newFileSizeDefined = (prop.vt != VT_EMPTY);
\r
298 UInt64 newFileSize;
\r
299 if (newFileSizeDefined)
\r
300 newFileSize = ConvertPropVariantToUInt64(prop);
\r
305 // Create folders for file
\r
306 int slashPos = _filePath.ReverseFind(WCHAR_PATH_SEPARATOR);
\r
308 NFile::NDirectory::CreateComplexDirectory(_directoryPath + _filePath.Left(slashPos));
\r
311 UString fullProcessedPath = _directoryPath + _filePath;
\r
312 _diskFilePath = fullProcessedPath;
\r
314 if (_processedFileInfo.isDir)
\r
316 NFile::NDirectory::CreateComplexDirectory(fullProcessedPath);
\r
320 NFile::NFind::CFileInfoW fi;
\r
321 if (fi.Find(fullProcessedPath))
\r
323 if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
\r
325 PrintString(UString(kCantDeleteOutputFile) + fullProcessedPath);
\r
330 _outFileStreamSpec = new COutFileStream;
\r
331 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
\r
332 if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS))
\r
334 PrintString((UString)L"can not open output file " + fullProcessedPath);
\r
337 _outFileStream = outStreamLoc;
\r
338 *outStream = outStreamLoc.Detach();
\r
343 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
\r
345 _extractMode = false;
\r
346 switch (askExtractMode)
\r
348 case NArchive::NExtract::NAskMode::kExtract: _extractMode = true; break;
\r
350 switch (askExtractMode)
\r
352 case NArchive::NExtract::NAskMode::kExtract: PrintString(kExtractingString); break;
\r
353 case NArchive::NExtract::NAskMode::kTest: PrintString(kTestingString); break;
\r
354 case NArchive::NExtract::NAskMode::kSkip: PrintString(kSkippingString); break;
\r
356 PrintString(_filePath);
\r
360 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
\r
362 switch(operationResult)
\r
364 case NArchive::NExtract::NOperationResult::kOK:
\r
370 switch(operationResult)
\r
372 case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
\r
373 PrintString(kUnsupportedMethod);
\r
375 case NArchive::NExtract::NOperationResult::kCRCError:
\r
376 PrintString(kCRCFailed);
\r
378 case NArchive::NExtract::NOperationResult::kDataError:
\r
379 PrintString(kDataError);
\r
382 PrintString(kUnknownError);
\r
387 if (_outFileStream != NULL)
\r
389 if (_processedFileInfo.MTimeDefined)
\r
390 _outFileStreamSpec->SetMTime(&_processedFileInfo.MTime);
\r
391 RINOK(_outFileStreamSpec->Close());
\r
393 _outFileStream.Release();
\r
394 if (_extractMode && _processedFileInfo.AttribDefined)
\r
395 NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attrib);
\r
401 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
\r
403 if (!PasswordIsDefined)
\r
405 // You can ask real password here from user
\r
406 // Password = GetPassword(OutStream);
\r
407 // PasswordIsDefined = true;
\r
408 PrintError("Password is not defined");
\r
411 return StringToBstr(Password, password);
\r
416 //////////////////////////////////////////////////////////////
\r
417 // Archive Creating callback class
\r
429 bool isDir() const { return (Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0 ; }
\r
432 class CArchiveUpdateCallback:
\r
433 public IArchiveUpdateCallback2,
\r
434 public ICryptoGetTextPassword2,
\r
435 public CMyUnknownImp
\r
438 MY_UNKNOWN_IMP2(IArchiveUpdateCallback2, ICryptoGetTextPassword2)
\r
441 STDMETHOD(SetTotal)(UInt64 size);
\r
442 STDMETHOD(SetCompleted)(const UInt64 *completeValue);
\r
444 // IUpdateCallback2
\r
445 STDMETHOD(EnumProperties)(IEnumSTATPROPSTG **enumerator);
\r
446 STDMETHOD(GetUpdateItemInfo)(UInt32 index,
\r
447 Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive);
\r
448 STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
\r
449 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream);
\r
450 STDMETHOD(SetOperationResult)(Int32 operationResult);
\r
451 STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size);
\r
452 STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream);
\r
454 STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);
\r
457 CRecordVector<UInt64> VolumesSizes;
\r
462 const CObjectVector<CDirItem> *DirItems;
\r
464 bool PasswordIsDefined;
\r
468 bool m_NeedBeClosed;
\r
470 UStringVector FailedFiles;
\r
471 CRecordVector<HRESULT> FailedCodes;
\r
473 CArchiveUpdateCallback(): PasswordIsDefined(false), AskPassword(false), DirItems(0) {};
\r
475 ~CArchiveUpdateCallback() { Finilize(); }
\r
476 HRESULT Finilize();
\r
478 void Init(const CObjectVector<CDirItem> *dirItems)
\r
480 DirItems = dirItems;
\r
481 m_NeedBeClosed = false;
\r
482 FailedFiles.Clear();
\r
483 FailedCodes.Clear();
\r
487 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */)
\r
492 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */)
\r
498 STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG ** /* enumerator */)
\r
503 STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */,
\r
504 Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive)
\r
506 if (newData != NULL)
\r
507 *newData = BoolToInt(true);
\r
508 if (newProperties != NULL)
\r
509 *newProperties = BoolToInt(true);
\r
510 if (indexInArchive != NULL)
\r
511 *indexInArchive = (UInt32)-1;
\r
515 STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
\r
517 NWindows::NCOM::CPropVariant prop;
\r
519 if (propID == kpidIsAnti)
\r
522 prop.Detach(value);
\r
527 const CDirItem &dirItem = (*DirItems)[index];
\r
530 case kpidPath: prop = dirItem.Name; break;
\r
531 case kpidIsDir: prop = dirItem.isDir(); break;
\r
532 case kpidSize: prop = dirItem.Size; break;
\r
533 case kpidAttrib: prop = dirItem.Attrib; break;
\r
534 case kpidCTime: prop = dirItem.CTime; break;
\r
535 case kpidATime: prop = dirItem.ATime; break;
\r
536 case kpidMTime: prop = dirItem.MTime; break;
\r
539 prop.Detach(value);
\r
543 HRESULT CArchiveUpdateCallback::Finilize()
\r
545 if (m_NeedBeClosed)
\r
548 m_NeedBeClosed = false;
\r
553 static void GetStream2(const wchar_t *name)
\r
555 PrintString("Compressing ");
\r
557 name = kEmptyFileAlias;
\r
561 STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
\r
565 const CDirItem &dirItem = (*DirItems)[index];
\r
566 GetStream2(dirItem.Name);
\r
568 if (dirItem.isDir())
\r
572 CInFileStream *inStreamSpec = new CInFileStream;
\r
573 CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
\r
574 UString path = DirPrefix + dirItem.FullPath;
\r
575 if (!inStreamSpec->Open(path))
\r
577 DWORD sysError = ::GetLastError();
\r
578 FailedCodes.Add(sysError);
\r
579 FailedFiles.Add(path);
\r
580 // if (systemError == ERROR_SHARING_VIOLATION)
\r
583 PrintError("WARNING: can't open file");
\r
584 // PrintString(NError::MyFormatMessageW(systemError));
\r
587 // return sysError;
\r
589 *inStream = inStreamLoc.Detach();
\r
594 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */)
\r
596 m_NeedBeClosed = true;
\r
600 STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
\r
602 if (VolumesSizes.Size() == 0)
\r
604 if (index >= (UInt32)VolumesSizes.Size())
\r
605 index = VolumesSizes.Size() - 1;
\r
606 *size = VolumesSizes[index];
\r
610 STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
\r
613 ConvertUInt32ToString(index + 1, temp);
\r
614 UString res = temp;
\r
615 while (res.Length() < 2)
\r
616 res = UString(L'0') + res;
\r
617 UString fileName = VolName;
\r
620 fileName += VolExt;
\r
621 COutFileStream *streamSpec = new COutFileStream;
\r
622 CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
\r
623 if (!streamSpec->Create(fileName, false))
\r
624 return ::GetLastError();
\r
625 *volumeStream = streamLoc.Detach();
\r
629 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
\r
631 if (!PasswordIsDefined)
\r
635 // You can ask real password here from user
\r
636 // Password = GetPassword(OutStream);
\r
637 // PasswordIsDefined = true;
\r
638 PrintError("Password is not defined");
\r
642 *passwordIsDefined = BoolToInt(PasswordIsDefined);
\r
643 return StringToBstr(Password, password);
\r
648 //////////////////////////////////////////////////////////////////////////
\r
651 #define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1;
\r
653 int MY_CDECL main(int numArgs, const char *args[])
\r
657 PrintStringLn(kCopyrightString);
\r
661 PrintStringLn(kHelpString);
\r
664 NWindows::NDLL::CLibrary lib;
\r
665 if (!lib.Load(TEXT(kDllName)))
\r
667 PrintError("Can not load 7-zip library");
\r
670 CreateObjectFunc createObjectFunc = (CreateObjectFunc)lib.GetProc("CreateObject");
\r
671 if (createObjectFunc == 0)
\r
673 PrintError("Can not get CreateObject");
\r
679 AString command = args[1];
\r
680 if (command.Length() != 1)
\r
682 PrintError("incorrect command");
\r
685 c = MyCharLower(command[0]);
\r
687 UString archiveName = GetUnicodeString(args[2]);
\r
690 // create archive command
\r
693 PrintStringLn(kHelpString);
\r
696 CObjectVector<CDirItem> dirItems;
\r
698 for (i = 3; i < numArgs; i++)
\r
701 UString name = GetUnicodeString(args[i]);
\r
703 NFile::NFind::CFileInfoW fi;
\r
704 if (!fi.Find(name))
\r
706 PrintString(UString(L"Can't find file") + name);
\r
710 di.Attrib = fi.Attrib;
\r
712 di.CTime = fi.CTime;
\r
713 di.ATime = fi.ATime;
\r
714 di.MTime = fi.MTime;
\r
716 di.FullPath = name;
\r
719 COutFileStream *outFileStreamSpec = new COutFileStream;
\r
720 CMyComPtr<IOutStream> outFileStream = outFileStreamSpec;
\r
721 if (!outFileStreamSpec->Create(archiveName, false))
\r
723 PrintError("can't create archive file");
\r
727 CMyComPtr<IOutArchive> outArchive;
\r
728 if (createObjectFunc(&CLSID_CFormat7z, &IID_IOutArchive, (void **)&outArchive) != S_OK)
\r
730 PrintError("Can not get class object");
\r
734 CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
\r
735 CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
\r
736 updateCallbackSpec->Init(&dirItems);
\r
737 // updateCallbackSpec->PasswordIsDefined = true;
\r
738 // updateCallbackSpec->Password = L"1";
\r
742 const wchar_t *names[] =
\r
747 const int kNumProps = sizeof(names) / sizeof(names[0]);
\r
748 NWindows::NCOM::CPropVariant values[kNumProps] =
\r
750 false, // solid mode OFF
\r
751 (UInt32)9 // compression level = 9 - ultra
\r
753 CMyComPtr<ISetProperties> setProperties;
\r
754 outArchive->QueryInterface(IID_ISetProperties, (void **)&setProperties);
\r
755 if (!setProperties)
\r
757 PrintError("ISetProperties unsupported");
\r
760 RINOK(setProperties->SetProperties(names, values, kNumProps));
\r
764 HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback);
\r
765 updateCallbackSpec->Finilize();
\r
766 if (result != S_OK)
\r
768 PrintError("Update Error");
\r
771 for (i = 0; i < updateCallbackSpec->FailedFiles.Size(); i++)
\r
774 PrintString((UString)L"Error for file: " + updateCallbackSpec->FailedFiles[i]);
\r
776 if (updateCallbackSpec->FailedFiles.Size() != 0)
\r
783 PrintStringLn(kHelpString);
\r
789 listCommand = true;
\r
791 listCommand = false;
\r
794 PrintError("incorrect command");
\r
798 CMyComPtr<IInArchive> archive;
\r
799 if (createObjectFunc(&CLSID_CFormat7z, &IID_IInArchive, (void **)&archive) != S_OK)
\r
801 PrintError("Can not get class object");
\r
805 CInFileStream *fileSpec = new CInFileStream;
\r
806 CMyComPtr<IInStream> file = fileSpec;
\r
808 if (!fileSpec->Open(archiveName))
\r
810 PrintError("Can not open archive file");
\r
815 CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback;
\r
816 CMyComPtr<IArchiveOpenCallback> openCallback(openCallbackSpec);
\r
817 openCallbackSpec->PasswordIsDefined = false;
\r
818 // openCallbackSpec->PasswordIsDefined = true;
\r
819 // openCallbackSpec->Password = L"1";
\r
821 if (archive->Open(file, 0, openCallback) != S_OK)
\r
823 PrintError("Can not open archive");
\r
831 UInt32 numItems = 0;
\r
832 archive->GetNumberOfItems(&numItems);
\r
833 for (UInt32 i = 0; i < numItems; i++)
\r
836 // Get uncompressed size of file
\r
837 NWindows::NCOM::CPropVariant prop;
\r
838 archive->GetProperty(i, kpidSize, &prop);
\r
839 UString s = ConvertPropVariantToString(prop);
\r
844 // Get name of file
\r
845 NWindows::NCOM::CPropVariant prop;
\r
846 archive->GetProperty(i, kpidPath, &prop);
\r
847 UString s = ConvertPropVariantToString(prop);
\r
856 CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
\r
857 CMyComPtr<IArchiveExtractCallback> extractCallback(extractCallbackSpec);
\r
858 extractCallbackSpec->Init(archive, L""); // second parameter is output folder path
\r
859 extractCallbackSpec->PasswordIsDefined = false;
\r
860 // extractCallbackSpec->PasswordIsDefined = true;
\r
861 // extractCallbackSpec->Password = L"1";
\r
862 HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback);
\r
863 if (result != S_OK)
\r
865 PrintError("Extract Error");
\r