В этой статье рассмотрим принцип создания самораспаковывающегося ZIP архива, который может быть защищен паролем. Эта статья является продолжением статьи о ZIP архивах. Как и в предыдущей статье, для работы с ZIP архивами, будем использовать библиотеку PureZIP.
Самораспаковывающийся архив состоит из модуля распаковки, находящегося в начале файла и архива, расположенного после модуля распаковки. По большому счету, это просто обычный исполняемый файл (EXE программа) в конце которого находится архив. Другими словами, для того чтобы создать самораспаковывающийся архив нужно написать небольшую программу распаковки, скомпилировать ее (создать исполняемый файл) и в ее конец, дописать заранее созданный архив.
Итак, создадим простенький распаковщик для ZIP архива.
Программа позволяет выбрать место распаковки и распаковать архив. Если он защищен паролем, то появится окно ввода пароля архива.
Код:
Enumeration #Window_0 EndEnumeration Enumeration #Text_0 #String_PathExtract #Button_Browse #Button_Extract #Text_2 #ProgressBar_0 #Text_3 #Text_ExtractFiles EndEnumeration Global DoneFiles, ProgName.s=ProgramFilename() Procedure Open_Window_0() If OpenWindow(#Window_0, 334, 311, 334, 120,"Самораспаковывающийся ZIP архив",#PB_Window_MinimizeGadget|#PB_Window_TitleBar|#PB_Window_Invisible|#PB_Window_ScreenCentered) TextGadget(#Text_0, 5, 5, 315, 15,"Куда распаковать файлы:") StringGadget(#String_PathExtract, 5, 20, 290, 20,"",#PB_String_ReadOnly) ButtonGadget(#Button_Browse, 300, 20, 24, 20,"...") ButtonGadget(#Button_Extract, 240, 90, 90, 25,"Распаковать") TextGadget(#Text_2, 5, 90, 235, 30,"Пример создан в среде PureBasic 4.51 с использованием библиотеки PureZIP.") ProgressBarGadget(#ProgressBar_0, 5, 65, 325, 15, 0, 100,#PB_ProgressBar_Smooth) TextGadget(#Text_3, 5, 50, 70, 15,"Распаковка:") TextGadget(#Text_ExtractFiles, 80, 50, 250, 15,"") EndIf EndProcedure Procedure PureSFXCallback(File.s, PerCent); Отображение в окне общего прогресса распаковки файлов Result=0 SetGadgetText(#Text_ExtractFiles, File) SetGadgetState(#ProgressBar_0, DoneFiles*100/PerCent) DoneFiles+1 Repeat Event=WindowEvent() If Event=#PB_Event_CloseWindow If EventWindow()=#Window_0 Select MessageRequester(GetFilePart(ProgName),"Закрыть окно с остановкой распаковки файлов?",#MB_YESNOCANCEL|#MB_ICONQUESTION|#MB_DEFBUTTON2) Case #IDYES End Case #IDCANCEL Result = 1 EndSelect EndIf EndIf Until Event = 0 ProcedureReturn Result EndProcedure Procedure ExtractArchive(ExtractPath.s); Распаковка архива в папку ExtractPath GlobalInfo.unz_global_info FileInfo.PureZIP_FileInfo PureZIP_SetArchivePassword("") CountFiles=PureZIP_GetFileCount(ProgName); Число файлов в архиве For i=0 To CountFiles-1 ; Выясняем запараролен ли архив If PureZIP_GetFileInfo(ProgName,i,@FileInfo)=#True If FileInfo\flag & 1 = 1 Pass.s=InputRequester(GetFilePart(ProgName)+" - Архив защищён паролем!","Введите пароль архива","") PureZIP_SetArchivePassword(Pass) Break EndIf EndIf Next i If PureZIP_Archive_Read(ProgName) ; Открываем архив (исполняемый файл распаковщика) DoneFiles=1 PureZIP_Archive_GlobalInfo(@GlobalInfo) NrOfCompressed.l = GlobalInfo\number_entry ReturnValue.l = PureZIP_Archive_FindFirst() While ReturnValue =#UNZ_OK If PureZIP_Archive_FileInfo(@FileInfo)=#UNZ_OK; Данные о файле (размер, дата, степень сжатия, CRC и т. д.) If PureZIP_Archive_Extract(ExtractPath,#True)=#UNZ_OK ; Распаковываем файлы If PureSFXCallback(FileInfo\FileName, NrOfCompressed)= 1 Break ; Распаковка прервана пользователем. EndIf ReturnValue = PureZIP_Archive_FindNext(); Подготовка к распаковке следующего файла Else MessageRequester("","Произошла ошибка при распаковке файла"+Chr(10)+FileInfo\FileName,#MB_OK|#MB_ICONWARNING) Break EndIf EndIf Wend PureZIP_Archive_Close() ; Закрытие архива If ReturnValue =#UNZ_END_OF_LIST_OF_FILE SetGadgetText(#Text_ExtractFiles,"Архив успешно распакован") MessageRequester(GetFilePart(ProgName),"Архив успешно распакован.",#MB_OK|#MB_ICONINFORMATION) EndIf EndIf EndProcedure Open_Window_0() SetGadgetText(#String_PathExtract, GetPathPart(ProgramFilename())) HideWindow(#Window_0,0) Repeat ; Главный цикл Repeat - Until Event = WaitWindowEvent() ; Идентификатор события If Event =#PB_Event_Gadget Select EventGadget() ; Идентификатор гаджета по которому кликнули Case #Button_Browse Path.s=PathRequester("Укажите путь к месту распаковки файлов","") If FileSize(Path)=-2 ; Папка существует SetGadgetText(#String_PathExtract, Path) EndIf Case #Button_Extract Path.s=GetGadgetText(#String_PathExtract) If FileSize(Path)=-2 ; Папка существует ExtractArchive(Path) Else MessageRequester("","Укажите путь к месту распаковки файлов!",#MB_OK|#MB_ICONWARNING) EndIf EndSelect EndIf Until Event =#PB_Event_CloseWindow
При клике по кнопке "Распаковать" будет вызвана процедура ExtractArchive. Ее код сначала посчитает файлы в архиве, а затем просмотрит все файлы чтобы выяснить есть ли среди них зашифрованные. В случае, если таковые будут обнаружены, будет создано окно с запросом пароля архива. Затем, открывается архив для чтения функцией PureZIP_Archive_Read. В цикле While - Wend, файлы извлекаются из архива функцией PureZIP_Archive_Extract. Когда все файлы будут извлечены, архив закрывается функцией PureZIP_Archive_Close.
Вот собственно и все - самораспаковывающиеся архивы довольно просто устроены.
Как отмечалось выше, самораспаковывающийся архив состоит из исполняемого файла (EXE программы) и архива. Теперь рассмотрим как правильно дописать архив в конец скомпилированного исполняемого файла распаковщика. Конечно же, вручную делать это утомительно и легко ошибиться! Для этого, была немного доработана заготовка программы упаковщика программ. Была модифицирована процедура, выполняемая при щелчке по экранной кнопке в окне программы.
Код:
Procedure Button_Archive(Directory.s, File.s) *Mem= AllocateMemory(100000); Память (100 тысяч байт) для копирования данных из временного архива в самораспаковывающийся. If *Mem TempDir.s=GetTemporaryDirectory(); Определение пути к временной папке If FileSize(TempDir)=-2 Repeat ; Генерация имени временного файла архива ArcFile.s = TempDir +"$$"+Str(Random(100000))+"xx.tmp" Until FileSize(ArcFile)=-1 ; Упаковка файлов If PureZIP_AddFiles(ArcFile, Directory+"*.*",#PureZIP_StorePathRelative,#PureZIP_Recursive)> 0 If ReadFile(0,ArcFile); Открыли временный файл архива ArchiveSize=Lof(0); Размер этого файла If CreateFile(1,File); Создаем файл самораспаковывающегося архива WriteData(1,?SFX_File,?SFX_File_End-?SFX_File); В начало файла - модуль распаковки архива. While Eof(0)= 0 ; Копирование данных из временного в самораспаковывающийся архив CountBytes = ReadData(0,*Mem,100000) If CountBytes>0 WriteData(1,*Mem,CountBytes) EndIf Wend WriteLong(1,ArchiveSize); Дописывание в конец самораспаковывающегося архива, размер архива. CloseFile(1) SetGadgetText(#Text_CurrentFiles,"Самораспаковывающийся архив создан") MessageRequester("","Самораспаковывающийся архив создан.",#MB_OK|#MB_ICONINFORMATION) Else MessageRequester("","Не удалось создать файл!",#MB_OK|#MB_ICONERROR) EndIf CloseFile(0) Else MessageRequester("","Не удалось открыть архив для чтения!",#MB_OK|#MB_ICONERROR) EndIf Else MessageRequester("","Ошибка при упаковке файлов!",#MB_OK|#MB_ICONERROR) EndIf DeleteFile(ArcFile); Удаление временного файла ZIP архива. Else MessageRequester("","Не обнаружена временная папка Windows!",#MB_OK|#MB_ICONERROR) EndIf Else MessageRequester("","Не удалось выделить память!",#MB_OK|#MB_ICONERROR) EndIf EndProcedure
В этой процедуре в создается временный файл под архив, а затем, в него добавляются файлы из выбранной папки. Потом, создается пустой файл под самораспаковывающийся архив и в его начало записывается тело исполняемого файла (строка "WriteData(1, ?SFX_File, ?SFX_File_End-?SFX_File)" ), а затем, в его конец, копируется содержимое архива из временного файла, после чего, файлы закрываются и удаляется временный файл с архивом.
Здесь был рассмотрен относительно простой пример, самораспаковывающегося архива, но в зависимости от реализуемой задачи его можно изменить (не зря же исходники выкладываю), скажем, вообще не отображать окно распаковщика, что позволит распаковывать файлы вскрытом режиме сразу после щелчка по архиву.