audiofeel

main

Jul 29th, 2025
114
0
10 hours
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 11.97 KB | Source Code | 0 0
  1. unit Main;
  2.  
  3. interface
  4.  
  5. uses
  6.   // 0) RTL
  7.   System.SysUtils,
  8.   System.Types,
  9.   System.Classes,
  10.   System.UITypes,
  11.   System.Variants,
  12.  
  13.   // 1) Winapi
  14.   Winapi.Windows,
  15.   Winapi.ShlObj,
  16.   Winapi.ActiveX,
  17.  
  18.   // 2) FMX
  19.   FMX.Controls,
  20.   FMX.Controls.Presentation,
  21.   FMX.Forms,
  22.   FMX.Graphics,
  23.   FMX.Layouts,
  24.   FMX.Objects,
  25.   FMX.StdCtrls,
  26.   FMX.Edit,
  27.   FMX.Dialogs,
  28.   FMX.Types,
  29.  
  30.   // 3) Проектные
  31.   ConfigData,
  32.   ISArcExM_Header;
  33.  
  34. type
  35.   TForm1 = class(TForm)
  36.     Background: TRectangle;
  37.     TitleBar: TLayout;
  38.     LabelTitle: TLabel;
  39.     ButtonClose: TButton;
  40.     StyleBook1: TStyleBook;
  41.     ImageContent: TImage;
  42.     LabelInfo: TLabel;
  43.     LabelDesc: TLabel;
  44.     ButtonExit: TButton;
  45.     ButtonInstall: TButton;
  46.     LabelDisk: TLabel;
  47.     LabelSize: TLabel;
  48.     DirEdit: TEdit;
  49.     ButtonBrowse: TButton;
  50.     LabelTasks: TLabel;
  51.     CheckBoxIcons: TCheckBox;
  52.     CheckBoxRedists: TCheckBox;
  53.     ProgressBarInstall: TProgressBar;
  54.     LabelStatus: TLabel;
  55.     LabelExtra: TLabel;
  56.     LabelPercent: TLabel;
  57.  
  58.     procedure TitleBarMouseDown(Sender: TObject; Button: TMouseButton;
  59.       Shift: TShiftState; X, Y: Single);
  60.     procedure ButtonCloseClick(Sender: TObject);
  61.     procedure FormCreate(Sender: TObject);
  62.     procedure ButtonBrowseClick(Sender: TObject);
  63.     procedure ButtonInstallClick(Sender: TObject);
  64.  
  65.   private
  66.     FTempDir: string;
  67.     procedure CleanTempFolder;
  68.     procedure StartUnpacking;
  69.  
  70.   public
  71.     procedure UpdateFreeLabel(const APath: string);
  72.   end;
  73.  
  74. var
  75.   Form1: TForm1;
  76.  
  77. implementation
  78.  
  79. {$R *.fmx}
  80.  
  81. uses
  82.   System.IOUtils,
  83.   FMX.Platform.Win;    // для WindowHandleToPlatform
  84.  
  85. resourcestring
  86.   SChooseFolder   = 'Выберите диск или папку установки';
  87.   SNotEnoughSpace = 'Недостаточно места на диске для установки.';
  88.   SSizeFormat     = '%.0f Гб. Нужно | %.0f Гб. Есть';
  89.   SWaitProc       = 'Ожидание...';
  90.   SUnpackProc     = 'Установка:';
  91.  
  92. //----------------------------------------------------------------------------//
  93. // Lowlevel расчёт свободного места (МБ)                                      //
  94. //----------------------------------------------------------------------------//
  95. function GetFreeSpaceMB(const APath: string): Double;
  96. var
  97.   freeBytes: UInt64;
  98.   driveRoot: string;
  99. begin
  100.   driveRoot := IncludeTrailingPathDelimiter(ExtractFileDrive(APath));
  101.   if not GetDiskFreeSpaceEx(PChar(driveRoot), @freeBytes, nil, nil) then
  102.     Exit(0);
  103.   Result := freeBytes / 1024 / 1024;
  104. end;
  105.  
  106. //----------------------------------------------------------------------------//
  107. // Проверка места на диске                                                    //
  108. //----------------------------------------------------------------------------//
  109. function CheckSpace(const APath: string; const RequiredMB: Double): Boolean;
  110. var
  111.   freeMB: Double;
  112. begin
  113.   freeMB := GetFreeSpaceMB(APath);
  114.   if freeMB < RequiredMB then
  115.   begin
  116.     ShowMessage(
  117.       Format(
  118.         SNotEnoughSpace + sLineBreak + SSizeFormat,
  119.         [RequiredMB / 1024, freeMB / 1024]
  120.       )
  121.     );
  122.     Exit(False);
  123.   end;
  124.   Result := True;
  125. end;
  126.  
  127. //----------------------------------------------------------------------------//
  128. // Создание уникальной временной папки                                        //
  129. //----------------------------------------------------------------------------//
  130. function CreateTempFolder: string;
  131. var
  132.   guid: TGUID;
  133. begin
  134.   CreateGUID(guid);
  135.   Result := TPath.Combine(TPath.GetTempPath, GUIDToString(guid));
  136.   ForceDirectories(Result);
  137. end;
  138.  
  139. //----------------------------------------------------------------------------//
  140. // Удаление уникальной временной папки                                        //
  141. //----------------------------------------------------------------------------//
  142. procedure TForm1.CleanTempFolder;
  143. begin
  144.   if TDirectory.Exists(FTempDir) then
  145.   begin
  146.     try
  147.       TDirectory.Delete(FTempDir, True);
  148.       FTempDir := '';
  149.     except
  150.       on E: Exception do
  151.         // здесь можно залогировать ошибку, но вы не мешаете закрытию
  152.     end;
  153.   end;
  154. end;
  155.  
  156. //----------------------------------------------------------------------------//
  157. // Извлечение только нужных RCDATA-ресурсов (имена в MyFiles[])               //
  158. //----------------------------------------------------------------------------//
  159. function EnumResNamesProc(hModule: HMODULE; lpszType, lpszName: PChar;
  160.   lParam: NativeInt): BOOL; stdcall;
  161. var
  162.   rs: TResourceStream;
  163.   outDir, resName, fileName: string;
  164.   i: Integer;
  165. begin
  166.   Result := True;
  167.   outDir  := PChar(lParam);
  168.   resName := string(lpszName);
  169.   for i := Low(MyFiles) to High(MyFiles) do
  170.     if SameText(MyFiles[i], resName) then
  171.     begin
  172.       try
  173.         rs := TResourceStream.Create(hModule, lpszName, RT_RCDATA);
  174.         try
  175.           fileName := TPath.Combine(outDir, resName);
  176.           rs.SaveToFile(fileName);
  177.         finally
  178.           rs.Free;
  179.         end;
  180.       except
  181.         // TODO: логировать ошибку
  182.       end;
  183.       Break;
  184.     end;
  185. end;
  186.  
  187. procedure ExtractAllRCDATA(const OutputDir: string);
  188. begin
  189.   if not DirectoryExists(OutputDir) then
  190.     ForceDirectories(OutputDir);
  191.   EnumResourceNames(
  192.     HInstance,
  193.     RT_RCDATA,
  194.     @EnumResNamesProc,
  195.     NativeInt(PChar(OutputDir))
  196.   );
  197. end;
  198.  
  199. //----------------------------------------------------------------------------//
  200. // Колбэк, вызываемый ISArcExM.dll при распаковке                             //
  201. //----------------------------------------------------------------------------//
  202. function ProgressStub(
  203.   OverallPct, CurrentPct, DiskTotalMB, DiskExtractedMB,
  204.   TotalFiles, CurFiles: Integer;
  205.   DiskName, CurrentFile, RemainsTime,
  206.   ElapsedTime, CurSpeed, AvgSpeed: WideString
  207. ): LongWord; stdcall;
  208. begin
  209.   // Обновляем UI
  210.   Form1.ProgressBarInstall.Value := OverallPct;
  211.   Form1.LabelPercent.Text := Format('%d%%', [OverallPct div 10]);
  212.   Form1.LabelExtra.Text := CurrentFile;
  213.  
  214.   // Возвращаем 0, чтобы процесс не прервался
  215.   Result := 0;
  216. end;
  217.  
  218. // ----------------------------------------------------------------------------
  219. // Реализация метода StartUnpacking
  220. // ----------------------------------------------------------------------------
  221.  
  222. procedure TForm1.StartUnpacking;
  223. var
  224.   ISArc        : TISArcExM;
  225.   exeFolder    : string;
  226.   arcPath      : string;
  227.   i, diskCount : Integer;
  228.   hWnd: Winapi.Windows.HWND;
  229. begin
  230.   hWnd := WindowHandleToPlatform(Self.Handle).Wnd;
  231.  
  232.   exeFolder := ExtractFilePath(ParamStr(0));
  233.   diskCount := 0;
  234.  
  235.   // 1. Инициализируем объект, указывая, где лежат DLL
  236.   ISArc := TISArcExM.Create(FTempDir);
  237.   try
  238.     // 2. Добавляем все диски (архивы) в список
  239.     for i := Low(Archives) to High(Archives) do
  240.     begin
  241.       arcPath := TPath.Combine(exeFolder, Archives[i]);
  242.       if not FileExists(arcPath) then
  243.         raise Exception.CreateFmt('Архив не найден: %s', [arcPath]);
  244.  
  245.       if not ISArc.AddDisksEx(arcPath, ArcPass, '', DirEdit.Text) then
  246.         raise Exception.CreateFmt('AddDisksEx failed for "%s"', [Archives[i]]);
  247.  
  248.       Inc(diskCount);
  249.     end;
  250.  
  251.     if diskCount = 0 then
  252.       raise Exception.Create('Нет ни одного диска для распаковки.');
  253.  
  254.     // 3. Инициализируем движок (InitEx)
  255.     //    1-й параметр — HWND приёма сообщений (0 = не нужен)
  256.     //    2-й параметр — номер сообщения (любое ваше, напр. WM_USER+1 или 2)
  257.     //    3-й параметр — функция обратного вызова
  258.     if not ISArc.InitEx(hWnd, 2, ProgressStub) then
  259.       raise Exception.Create('Не удалось инициализировать ISArcExM.dll');
  260.  
  261.     ISArc.ChangeLanguage('Russian');
  262.  
  263.     // 4. Цикл извлечения каждого диска
  264.     for i := 1 to diskCount do
  265.       if not ISArc.Extract(i, '', DirEdit.Text) then
  266.         raise Exception.CreateFmt('Ошибка Extract на диске %d', [i]);
  267.  
  268.     // 5. Завершаем работу
  269.     ISArc.Stop;      // либо ISArc.CleanUp;
  270.   finally
  271.     ISArc.Free;
  272.   end;
  273.  
  274.   ShowMessage('Установка успешно завершена!');
  275. end;
  276.  
  277. //----------------------------------------------------------------------------//
  278. // Обработчики формы                                                          //
  279. //----------------------------------------------------------------------------//
  280. procedure TForm1.FormCreate(Sender: TObject);
  281. var
  282.   defaultDir: string;
  283. begin
  284.   LabelTitle.Text  := AppName;
  285.   LabelInfo.Text   := AppInfo;
  286.   LabelDesc.Text   := AppDesc;
  287.   LabelStatus.Text := SWaitProc;
  288.  
  289.   CheckBoxIcons.Enabled   := Length(Shortcuts) > 0;
  290.   CheckBoxIcons.IsChecked := CheckBoxIcons.Enabled;
  291.   CheckBoxRedists.Enabled   := Length(Redists) > 0;
  292.   CheckBoxRedists.IsChecked := CheckBoxRedists.Enabled;
  293.  
  294.   defaultDir := TPath.Combine(
  295.     IncludeTrailingPathDelimiter(GetEnvironmentVariable('SystemDrive')),
  296.     AppPath
  297.   );
  298.   DirEdit.Text := defaultDir;
  299.   UpdateFreeLabel(defaultDir);
  300. end;
  301.  
  302. //----------------------------------------------------------------------------//
  303. // Объявление функции выбора папки/диска                                      //
  304. //----------------------------------------------------------------------------//
  305.  
  306. function BrowseForDrivesOnly(const OwnerWnd: HWND; const Title: string;
  307.   var Folder: string): Boolean;
  308. var
  309.   bi: TBrowseInfo;
  310.   pidlRoot, pidlSel: PItemIDList;
  311.   buf: array[0..MAX_PATH] of Char;
  312. begin
  313.   Result   := False;
  314.   pidlRoot := nil;
  315.   pidlSel  := nil;
  316.   ZeroMemory(@bi, SizeOf(bi));
  317.   bi.hwndOwner := OwnerWnd;
  318.   bi.lpszTitle := PChar(Title);
  319.   bi.ulFlags   := BIF_RETURNONLYFSDIRS or BIF_NEWDIALOGSTYLE;
  320.   if Succeeded(SHGetSpecialFolderLocation(OwnerWnd, CSIDL_DRIVES, pidlRoot)) then
  321.   try
  322.     bi.pidlRoot := pidlRoot;
  323.     pidlSel := SHBrowseForFolder(bi);
  324.     if (pidlSel <> nil) and SHGetPathFromIDList(pidlSel, buf) then
  325.     begin
  326.       Folder := buf;
  327.       Result := True;
  328.     end;
  329.   finally
  330.     if pidlSel  <> nil then CoTaskMemFree(pidlSel);
  331.     if pidlRoot <> nil then CoTaskMemFree(pidlRoot);
  332.   end;
  333. end;
  334.  
  335. procedure TForm1.ButtonBrowseClick(Sender: TObject);
  336. var
  337.   sel, fullPath: string;
  338. begin
  339.   sel := DirEdit.Text;
  340.   if BrowseForDrivesOnly(
  341.        WindowHandleToPlatform(Self.Handle).Wnd,
  342.        SChooseFolder,
  343.        sel
  344.      )
  345.   then
  346.   begin
  347.     fullPath := TPath.Combine(sel, AppPath);
  348.     DirEdit.Text := fullPath;
  349.     UpdateFreeLabel(fullPath);
  350.   end;
  351. end;
  352.  
  353. procedure TForm1.ButtonInstallClick(Sender: TObject);
  354. //var
  355. //  tempDir: string;
  356. begin
  357.   // Проверяем свободное место
  358.   if not CheckSpace(DirEdit.Text, AppSizeMB) then
  359.     Exit;
  360.  
  361.   // Распаковываем ресурсы в TEMP
  362.   FTempDir := CreateTempFolder;
  363.   ExtractAllRCDATA(FTempDir);
  364.  
  365.   // Обновляем статус и запускаем фактическую распаковку
  366.   LabelStatus.Text := SUnpackProc;
  367.   StartUnpacking;
  368. end;
  369.  
  370. procedure TForm1.ButtonCloseClick(Sender: TObject);
  371. begin
  372.   Close;
  373. end;
  374.  
  375. procedure TForm1.TitleBarMouseDown(Sender: TObject; Button: TMouseButton;
  376.   Shift: TShiftState; X, Y: Single);
  377. begin
  378.   if Button = TMouseButton.mbLeft then
  379.   begin
  380.     ReleaseCapture;
  381.     SendMessage(
  382.       FmxHandleToHWND(Self.Handle),
  383.       $0112, $F012, 0
  384.     );
  385.   end;
  386. end;
  387.  
  388. procedure TForm1.UpdateFreeLabel(const APath: string);
  389. var
  390.   needMB: Double;
  391. begin
  392.   needMB := AppSizeMB;
  393.   LabelSize.Text := Format(
  394.     SSizeFormat,
  395.     [needMB / 1024, GetFreeSpaceMB(APath) / 1024]
  396.   );
  397. end;
  398.  
  399. end.
Advertisement
Add Comment
Please, Sign In to add comment