Перейти из форума на сайт.

НовостиФайловые архивы
ПоискАктивные темыТоп лист
ПравилаКто в on-line?
Вход Забыли пароль? Первый раз на этом сайте? Регистрация
Компьютерный форум Ru.Board » Компьютеры » Программы » Inno Setup (создание инсталяционных пакетов)

Модерирует : gyra, Maz

Widok (10-08-2009 22:13): Лимит страниц. Продолжаем здесь.  Версия для печати • ПодписатьсяДобавить в закладки
На первую страницук этому сообщениюк последнему сообщению

   

STRATEG1992

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
[Code]
const
    Archives = '{src}\*.arc';    // укажите расположение архивов FreeArc; для внешних файлов строку в [Files] добавлять необязательно
 
    PM_REMOVE = 1;
    CP_ACP = 0; CP_UTF8 = 65001;
    oneMB=1024*1024;
    Period = 250; // частота обновления кнопки таскбара и строки статуса
    HC_ACTION = 0;
    VK_ESCAPE = 27;
    WM_PAINT = $F;
    WH_CALLWNDPROC = 4;
 
type
#ifdef UNICODE  ;// если у вас ошибка на этой строке, то установите препроцессор или исправьте скрипт для вашей версии Inno Setup
    #define A "W"
#else
    #define A "A"  ;// точка входа в SetWindowText, {#A} меняется на A или W в зависимости от версии
    PAnsiChar = PChar;  // Required for Inno Setup 5.3.0 and higher. (требуется для Inno Setup версии 5.3.0 и ниже)
#endif
#if Ver < 84018176
    AnsiString = String; // There is no need for this line in Inno Setup 5.2.4 and above (для Inno Setup версий 5.2.4 и выше эта строка не нужна)
#endif
 
    TMyMsg = record
        hwnd: HWND;
        message: UINT;
        wParam: Longint;
        lParam: Longint;
        time: DWORD;
        pt: TPoint;
    end;
 
    TFreeArcCallback = function (what: PAnsiChar; int1, int2: Integer; str: PAnsiChar): Integer;
    TArc = record Path: string; Size: Extended; end;
    TBarInfo = record stage, name: string; size: Extended; count, perc, pos, time: Integer; end;
    TCWPSTRUCT = record lParam: LongWord; wParam: Word; Msg: LongWord; hwnd: HWnd; end;
    TCWPSTRUCTProc = procedure(Code: Integer; wParam: Word; lParam: TCWPSTRUCT);
    TTimerProc = procedure(HandleW, Msg, idEvent, TimeSys: LongWord);
 
var
    ExtractFile, StatusInfo: TLabel;
    btnCancelUnpacking: TButton;
    ProgressBar: TNewProgressBar;
    CancelCode, n, ArcInd, UnPackError, StartInstall: Integer;
    Arcs: array of TArc;
    FSR: TFindRec;
    msgError: string;
    lastMb, baseMb: Integer;
    LastTimerEvent: DWORD;
    WndHookID, TimerID: LongWord;
    allSize: Extended;
    Status: TBarInfo;
    FreezeTimer: Boolean;
 
function WrapFreeArcCallback (callback: TFreeArcCallback; paramcount: integer):longword; external 'wrapcallback@files:innocallback.dll stdcall';
function FreeArcExtract (callback: longword; cmd1,cmd2,cmd3,cmd4,cmd5,cmd6,cmd7,cmd8,cmd9,cmd10: PAnsiChar): integer; external 'FreeArcExtract@files:unarc.dll cdecl';
 
Function OemToChar(lpszSrc, lpszDst: AnsiString): longint; external 'OemToCharA@user32.dll stdcall';
Function MultiByteToWideChar(CodePage: UINT; dwFlags: DWORD; lpMultiByteStr: PAnsiChar; cbMultiByte: integer; lpWideCharStr: PAnsiChar; cchWideChar: integer): longint; external 'MultiByteToWideChar@kernel32.dll stdcall';
Function WideCharToMultiByte(CodePage: UINT; dwFlags: DWORD; lpWideCharStr: PAnsiChar; cchWideChar: integer; lpMultiByteStr: PAnsiChar; cbMultiByte: integer; lpDefaultChar: integer; lpUsedDefaultChar: integer): longint; external 'WideCharToMultiByte@kernel32.dll stdcall';
 
function PeekMessage(var lpMsg: TMyMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL; external 'PeekMessageA@user32.dll stdcall';
function TranslateMessage(const lpMsg: TMyMsg): BOOL; external 'TranslateMessage@user32.dll stdcall';
function DispatchMessage(const lpMsg: TMyMsg): Longint; external 'DispatchMessageA@user32.dll stdcall';
 
function GetTickCount: DWord; external 'GetTickCount@kernel32';
function GetWindowLong(hWnd, nIndex: Integer): Longint; external 'GetWindowLongA@user32 stdcall delayload';
function SetWindowText(hWnd: Longint; lpString: String): Longint; external 'SetWindowText{#A}@user32 stdcall delayload';
function GetKeyState(nVirtKey: Integer): ShortInt; external 'GetKeyState@user32 stdcall delayload';
function GetCurrentThreadId: LongWord; external 'GetCurrentThreadId@kernel32 stdcall delayload';
 
function CallNextWNDPROC(idHook: LongWord; Code: Integer; wParam: Word; lParam: TCWPSTRUCT): LongWord; external 'CallNextHookEx@user32 stdcall delayload';
function SetWindowsHookEx(idHook: LongWord; callback: LongWord; hMod: LongWord; dwThreadID: HWND): LongWord; external 'SetWindowsHookExW@user32 stdcall delayload';
function UnhookWindowsHookEx(idHook: LongWord): LongWord; external 'UnhookWindowsHookEx@user32 stdcall delayload';
function WrapCWPSTRUCTProc(callback:TCWPSTRUCTProc; paramcount:integer): longword; external 'wrapcallback@files:innocallback.dll';
function WrapTimerProc(callback: TTimerProc; Paramcount: Integer): longword; external 'wrapcallback@files:innocallback.dll stdcall';
function SetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc: LongWord): longword; external 'SetTimer@user32';
function KillTimer(hWnd, nIDEvent: LongWord): LongWord; external 'KillTimer@user32 stdcall delayload';
 
procedure AppProcessMessage;
var
    Msg: TMyMsg;
begin
    while PeekMessage(Msg, WizardForm.Handle, 0, 0, PM_REMOVE) do begin
        TranslateMessage(Msg);
        DispatchMessage(Msg);
    end;
end;
 
// Sets the TaskBar title
Procedure SetTaskBarTitle(Title: String); var h: Integer;
Begin
    h:= GetWindowLong(MainForm.Handle, -8); if h <> 0 then SetWindowText(h, Title);
End;
 
// Перевод числа в строку с точностью 2 знака (%.2n) с округлением дробной части, если она есть
Function NumToStr(Float: Extended): String;
Begin
    Result:= Format('%.2n', [Float]); StringChange(Result, ',', '.');
    while ((Result[Length(Result)] = '0') or (Result[Length(Result)] = '.')) and (Pos('.', Result) > 0) do
        SetLength(Result, Length(Result)-1);
End;
 
Function ByteOrTB(Bytes: Extended; noMB: Boolean): String; {Перевод числа в значение бт/Кб/Мб/Гб/Тб (до 2х знаков после запятой)}
    Begin
        if not noMB then Result:= NumToStr(Int(Bytes)) +' Mb' else
            if Bytes < 1024 then if Bytes = 0 then Result:= '0' else Result:= NumToStr(Int(Bytes)) +' Bt' else
                if Bytes/1024 < 1024 then Result:= NumToStr(round((Bytes/1024)*10)/10) +' Kb' else
                    If Bytes/oneMB < 1024 then Result:= NumToStr(round(Bytes/oneMB*100)/100) +' Mb' else
                        If Bytes/oneMB/1000 < 1024 then Result:= NumToStr(round(Bytes/oneMB/1024*1000)/1000) +' Gb' else
                            Result:= NumToStr(round(Bytes/oneMB/oneMB*1000)/1000) +' Tb';
    End;
 
// Converts milliseconds to human-readable time
// Конвертирует милисекунды в человеко-читаемое изображение времени
Function TicksToTime(Ticks: DWord; h,m,s: String; detail: Boolean): String;
Begin
    if detail then            {hh:mm:ss format}
        Result:= PADZ(IntToStr(Ticks/3600000), 2) +':'+ PADZ(IntToStr((Ticks/1000 - Ticks/1000/3600*3600)/60), 2) +':'+ PADZ(IntToStr(Ticks/1000 - Ticks/1000/60*60), 2)
    else if Ticks/3600 >= 1000 then    {more than hour}
        Result:= IntToStr(Ticks/3600000) +h+' '+ PADZ(IntToStr((Ticks/1000 - Ticks/1000/3600*3600)/60), 2) +m
    else if Ticks/60 >= 1000 then    {1..60 minutes}
        Result:= IntToStr(Ticks/60000) +m+' '+ IntToStr(Ticks/1000 - Ticks/1000/60*60) +s
    else Result:= Format('%.1n', [Abs(Ticks/1000)]) +s    {less than one minute}
End;
 
function cm(Message: String): String; Begin Result:= ExpandConstant('{cm:'+ Message +'}') End;
 
Function LoWord(lw: LongWord): LongWord; Begin Result:= lw shr 16; End;
 
Function Size64(Hi, Lo: Integer): Extended;
Begin
    Result:= Lo;
    if Lo<0 then Result:= Result + $7FFFFFFF + $7FFFFFFF + 2;
    for Hi:= Hi-1 Downto 0 do
        Result:= Result + $7FFFFFFF + $7FFFFFFF + 2;
End;
 
// Converts OEM encoded string into ANSI
// Преобразует OEM строку в ANSI кодировку
function OemToAnsiStr(strSource: AnsiString): AnsiString;
var
    nRet : longint;
begin
    SetLength(Result, Length(strSource));
    nRet:= OemToChar(strSource, Result);
end;
 
// Converts ANSI encoded string into UTF-8
// Преобразует строку из ANSI в UTF-8 кодировку
function AnsiToUtf8(strSource: string): string;
var
    nRet, nRet2: integer; WideCharBuf, MultiByteBuf: AnsiString;
begin
    SetLength(WideCharBuf, Length(strSource) * 2);
    SetLength(MultiByteBuf, Length(strSource) * 2);
    nRet:= MultiByteToWideChar(CP_ACP, 0, strSource, -1, WideCharBuf, Length(WideCharBuf));
    nRet2:= WideCharToMultiByte(CP_UTF8, 0, WideCharBuf, -1, MultiByteBuf, Length(MultiByteBuf), 0, 0);
    if nRet * nRet2 = 0 then Result:= strSource else Result:= MultiByteBuf;
end;
 
// OnClick event function for btnCancel
procedure btnCancelUnpackingOnClick(Sender: TObject);
begin
    FreezeTimer:= true; // заблокировать таймер
    if MsgBox(SetupMessage(msgExitSetupMessage), mbInformation, MB_YESNO) = IDYES then
        CancelCode:= -127 else FreezeTimer:= false; // продолжить обновление информации
end;
 
// Scans the specified folders for archives and add them to list
function FindArcs(dir: string): Extended;
Begin
  Result:= 0;
    if FindFirst(ExpandConstant(dir), FSR) then
        try
            repeat
                // Skip everything but the folders
                if FSR.Attributes and FILE_ATTRIBUTE_DIRECTORY > 0 then CONTINUE;
                n:= GetArrayLength(Arcs);
                // Expand the folder list
                SetArrayLength(Arcs, n +1);
                Arcs[n].Path:= ExtractFilePath(ExpandConstant(dir)) + FSR.Name;
                Arcs[n].Size:= Size64(FSR.SizeHigh, FSR.SizeLow);
                Result:= Result + Arcs[n].Size;
            until not FindNext(FSR);
        finally
            FindClose(FSR);
        end;
End;
 
Procedure UpdateStatus();   // выполняется с переодичностью, заданной константой Period
var
    Remaining: Integer; i, t, s: string;
Begin
    if (GetTickCount - LastTimerEvent <= Period) or FreezeTimer then Exit else LastTimerEvent := GetTickCount;
  with WizardForm.ProgressGauge do begin
    if position > 0 then Remaining:= trunc((GetTickCount - StartInstall) * Abs((max - position)/position)) else Remaining:= 0;
        t:= cm('ending'); i:= t;
        if Remaining > 0 then begin
            t:= FmtMessage(cm('taskbar'), [IntToStr(Status.perc/10), TicksToTime(Remaining, 'h', 'm', 's', false)])
            i:= TicksToTime(Remaining, cm('hour'), cm('min'), cm('sec'), false)
        end;
  end;
    SetTaskBarTitle(t); // проценты и оставшееся время на кнопке инсталлятора
    if Status.size > 0 then
        s:= ' [' + ByteOrTB(Status.size*oneMB, true) + ']';   // можно сделать подсчёт размера папки {app} через CalcDirSize, но это может замедлить работу
    StatusInfo.Caption:= FmtMessage(cm('StatusInfo'), [IntToStr(Status.count), s, Format('%.1n', [Abs(Status.perc/10)]), i]);
    if GetArrayLength(Arcs) > 1 then begin   // показать прогрессбар и сведения при обработке нескольких архивов
        ExtractFile.Caption:= FmtMessage(cm('ArcInfo'), [IntToStr(ArcInd+1), IntToStr(GetArrayLength(Arcs)), ByteOrTB(Arcs[ArcInd].Size, true), Format('%.0n', [Status.pos/(Arcs[ArcInd].Size/oneMB)*100]), ByteOrTB(allSize, true)])
        ProgressBar.Position:= round(ProgressBar.Max * Status.pos/(Arcs[ArcInd].Size/oneMB))
        if not ProgressBar.Visible then ProgressBar.Show;
    end;
End;
 
Procedure MyTimerProc(h, msg, idevent, dwTime: Longword);
Begin
    UpdateStatus;
End;
 
Procedure OnWndHook(Code: Integer; wParam: Word; lParam: TCWPSTRUCT);
Begin
  if (Code = HC_ACTION) and (LoWord(lParam.msg) = WM_PAINT) then begin  //подготовка данных для последующего отображения по таймеру
      if Status.name <> WizardForm.FileNameLabel.Caption then // реагируем только на этапы извлечения файлов и распаковки архивов
        if (WizardForm.StatusLabel.Caption = SetupMessage(msgStatusExtractFiles)) or (WizardForm.StatusLabel.Caption = cm('ArcTitle')) then begin
            Status.name := WizardForm.FileNameLabel.Caption;
            Status.count:= Status.count + 1;    // кол-во файлов
        end;
        with WizardForm.ProgressGauge do
            Status.perc:= (Position-Min)/((Max - Min)/1000);   // 1000 процентов
    UpdateStatus();
  end;
    CallNextWNDPROC(WndHookID, Code, wParam, lParam)    {освобождение события}
End;
 
// The main callback function for unpacking FreeArc archives
function FreeArcCallback (what: PAnsiChar; Mb, sizeArc: Integer; str: PAnsiChar): Integer;
var
  Elapsed: Extended;
begin
//    if GetTickCount - LastTimerEvent > 1000 then begin
        // This code will be executed once each 1000 ms (этот код будет выполняться раз в 1000 миллисекунд)
        UpdateStatus();
        if GetKeyState(VK_ESCAPE) < 0 then btnCancelUnpacking.OnClick(btnCancelUnpacking);
        // End of code executed by timer
//        LastTimerEvent := LastTimerEvent+1000;
//    end;
  Case string(what) of
    'filename': begin   // Update FileName label
        WizardForm.FileNameLabel.Caption:= OemToAnsiStr(str); // извлекаемый файл
    end;
    'progress': if Mb >= 1 then with WizardForm.ProgressGauge do begin
        for n:= 0 to ArcInd-1 do Elapsed:= Elapsed + Arcs[n].Size; Elapsed:= Elapsed/oneMB + Mb; // обработано Mбайт (для небольшого архива точность страдает)
        Position:= round(Max * Elapsed/(allSize/oneMB))
        Status.pos := Mb;   // позиция в текущем архиве
    end;
    'written': begin // Assign to Mb *total* amount of data extracted to the moment from all archives
        lastMb := Mb;   // извлечено из текущего архива
        Status.size := baseMb+Mb; // запоминаем общий объём, чтобы снимать данные по таймеру
    end;
  End;
    AppProcessMessage;
    Result:= CancelCode;
end;
 
// Extracts all found archives
function UnPack(Archives: string): Integer;
var
    callback: longword;
    FreeMB, TotalMB: Cardinal;
begin
    // Show the 'Cancel unpacking' button and set it as default button
    btnCancelUnpacking.Show;
    WizardForm.ActiveControl:= btnCancelUnpacking;
    // Get the size of all archives
    allSize:= FindArcs(Archives);
    // Other initializations
    callback:= WrapFreeArcCallback(@FreeArcCallback,4);   //FreeArcCallback has 4 arguments
    WizardForm.StatusLabel.Caption:= cm('ArcTitle');    // начало этапa распаковки
    baseMb:= 0  // обнулить полученные мегабайты, если ранее вёлся подсчёт объёма файлов инсталлятора
    LastTimerEvent := 0;    // сброс таймера, чтобы игнорировать предыдущую задержку и немедленно обновить строку статуса
    Status.count:= 0;   // не учитывать файлы, извлечённые инсталлятором
  for ArcInd:= 0 to GetArrayLength(Arcs) -1 do begin
        CancelCode:= 0;
        AppProcessMessage;
        try
            // Pass the specified arguments to 'unarc.dll'
            Result:= FreeArcExtract (callback, 'x', '-o+', '-dp'+ AnsiToUtf8(ExpandConstant('{app}')), '--', AnsiToUtf8(Arcs[ArcInd].Path), '', '', '', '', '');
            if CancelCode < 0 then Result:= CancelCode;
        except
            Result:= -63;  //    ArcFail
        end;
        baseMb:= baseMb + lastMb    // общий объём распакованных файлов
 
    // Error occured
        if Result <> 0 then
        begin
            msgError:= FmtMessage(cm('ArcError'), [IntToStr(Result)]);
            WizardForm.StatusLabel.Caption:= msgError;
            WizardForm.FileNameLabel.Caption:= ExtractFileName(Arcs[ArcInd].Path);
            GetSpaceOnDisk(ExtractFileDrive(ExpandConstant('{app}')), True, FreeMB, TotalMB);
            case Result of
                -1: if FreeMB < 32 {Мб на диске} then msgError:= SetupMessage(msgDiskSpaceWarningTitle)
                    else msgError:= msgError + #13#10 + FmtMessage(cm('ArcBroken'), [ExtractFileName(Arcs[ArcInd].Path)]);
                -127:   msgError:= cm('ArcBreak');    //Cancel button
                -63:    msgError:= cm('ArcFail');
            end;
//          MsgBox(msgError, mbInformation, MB_OK);    // диалог не нужен, т.к. ошибка показывается на странице завершения
            Log(msgError);
            Break;    // прервать цикл распаковки
        end;
  end;
    // Hide labels and button
    btnCancelUnpacking.Hide;
    StatusInfo.Visible:= false;
    ExtractFile.Visible:= false;
    ProgressBar.Hide;
end;
 
procedure CurStepChanged(CurStep: TSetupStep);
begin
    if CurStep = ssInstall then begin
        StartInstall:= GetTickCount    {время начала извлечения файлов}
        WndHookID:= SetWindowsHookEx(WH_CALLWNDPROC, WrapCWPSTRUCTProc(@OnWndHook, 3), 0, GetCurrentThreadId);    {установка SendMessage хука}
        TimerID:= SetTimer(0, 0, 500 {полсекунды}, WrapTimerProc(@MyTimerProc, 4));    {установка таймера}
    end;
    if CurStep = ssPostInstall then
    begin
        StartInstall:= GetTickCount    {время начала распаковки}
        UnPackError:= UnPack(Archives)
        KillTimer(0, TimerID)        {удаление таймера}
        UnhookWindowsHookEx(WndHookID)    {удаление SendMessage хука}
        if UnPackError = 0 then
            SetTaskBarTitle(SetupMessage(msgSetupAppTitle))
        else
        begin
            // Error occured, uninstall it then
            if '{#SetupSetting("Uninstallable")}' <> 'false' then  // деинсталляция разрешёна
                Exec(ExpandConstant('{uninstallexe}'), '/SILENT','', sw_Hide, ewWaitUntilTerminated, n);    // откат установки из-за ошибки unarc.dll
            SetTaskBarTitle(SetupMessage(msgErrorTitle))
            WizardForm.Caption:= SetupMessage(msgErrorTitle) +' — '+ cm('ArcBreak')
        end;
    end;
end;
 
//    стандартный способ отката (не нужна CurPageChanged), но архивы распаковываются до извлечения файлов инсталлятора
//    if CurStep = ssInstall then
//      if UnPack(Archives) <> 0 then Abort;
 
Procedure CurPageChanged(CurPageID: Integer);
Begin
    if (CurPageID = wpFinished) and (UnPackError <> 0) then
    begin // Extraction was unsuccessful (распаковщик вернул ошибку)
        // Show error message
        WizardForm.FinishedLabel.Font.Color:= $0000C0;    // red (красный)
        WizardForm.FinishedLabel.Height:= WizardForm.FinishedLabel.Height * 2;
        WizardForm.FinishedLabel.Caption:= SetupMessage(msgSetupAborted) + #13#10#13#10 + msgError;
    end;
End;
 
procedure InitializeWizard();
begin
    with WizardForm.ProgressGauge do begin
// Create controls to show extended info
    StatusInfo:= TLabel.Create(WizardForm);
        StatusInfo.parent:=WizardForm.InstallingPage;
        StatusInfo.Autosize:= false;
        StatusInfo.Top:= Top + ScaleY(32);
        StatusInfo.Width:= Width;
    ProgressBar := TNewProgressBar.Create(WizardForm);
        ProgressBar.SetBounds(Left, StatusInfo.Top + StatusInfo.Height + ScaleY(16), Width, Height);
        ProgressBar.Parent := WizardForm.InstallingPage;
        ProgressBar.max := 65536;
        ProgressBar.Hide;   // будет показан при обработке нескольких архивов
    ExtractFile:= TLabel.Create(WizardForm);
        ExtractFile.parent:=WizardForm.InstallingPage;
        ExtractFile.Autosize:= false;
        ExtractFile.Top:= ProgressBar.Top + ScaleY(32);
        ExtractFile.Width:= Width;
    end;
// Create a 'Cancel unpacking' button and hide it for now.
    btnCancelUnpacking:=TButton.create(WizardForm);
    btnCancelUnpacking.Parent:= WizardForm;
    btnCancelUnpacking.SetBounds(WizardForm.cancelbutton.left, WizardForm.cancelbutton.top, WizardForm.cancelbutton.width, WizardForm.cancelbutton.height);
    btnCancelUnpacking.OnClick:= @btnCancelUnpackingOnClick;
    btnCancelUnpacking.Caption:= SetupMessage(msgButtonCancel);
    btnCancelUnpacking.Hide;
end;
 
Procedure DeInitializeSkin;
Begin
    UnhookWindowsHookEx(WndHookID)    {удаление SendMessage хука}
    KillTimer(0, TimerID)        {удаление таймера}
End;

Всего записей: 79 | Зарегистр. 29-04-2009 | Отправлено: 19:08 11-07-2009
   

На первую страницук этому сообщениюк последнему сообщению

Компьютерный форум Ru.Board » Компьютеры » Программы » Inno Setup (создание инсталяционных пакетов)
Widok (10-08-2009 22:13): Лимит страниц. Продолжаем здесь.


Реклама на форуме Ru.Board.

Powered by Ikonboard "v2.1.7b" © 2000 Ikonboard.com
Modified by Ru.B0ard
© Ru.B0ard 2000-2024

BitCoin: 1NGG1chHtUvrtEqjeerQCKDMUi6S6CG4iC

Рейтинг.ru