2016-08-31 2 views
2

현재 종료 코드를 얻을 수 있도록 PrepareToInstall 이벤트 기능에 .NET Framework 4.6.2를 필수 구성 요소로 설치 중입니다. NeedsReboot 상태이거나 설치가 실패하면 중단됩니다. 내 코드가 아래에 있으며이 모든 작동합니다. 순간 Inno 설치 진행률 표시 줄 위치를 업데이트하기 위해 .NET Framework 4.5 이상에서 설치 진행

var 
    PrepareToInstallLabel: TNewStaticText; 
    PrepareToInstallProgressBar: TNewProgressBar; 
    intDotNetResultCode: Integer; 
    CancelWithoutPrompt, AbortInstall: Boolean; 

function InitializeSetup(): Boolean; 
begin 
    Result := True; 
    OverwriteDB := False; 
    CancelWithoutPrompt := False; 
    AbortInstall := False; 
end; 

function PrepareToInstall(var NeedsRestart: Boolean): String; 
var 
    intResultCode: Integer; 
    strInstallType: String; 
begin 
    if not IsDotNet45Installed and IsWindows7Sp1OrAbove then 
    begin 
     HidePrepareToInstallGuiControls; 
     PrepareToInstallLabel.Caption := 'Installing Microsoft .NET Framework 4.6.2...'; 
     ShowPrepareToInstallGuiControls; 
     ExtractTemporaryFile('NDP462-KB3151800-x86-x64-AllOS-ENU.exe'); 
     if WizardSilent = True then 
     begin 
      strInstallType := '/q'; 
     end 
     else 
     begin 
      strInstallType := '/passive'; 
     end; 
     Exec(ExpandConstant('{tmp}\NDP462-KB3151800-x86-x64-AllOS-ENU.exe'), strInstallType + ' /norestart', '', SW_SHOW, 
     ewWaitUntilTerminated, intDotNetResultCode); 
     if (intDotNetResultCode = 0) or (intDotNetResultCode = 1641) or (intDotNetResultCode = 3010) then 
     begin 
      Log('Microsoft .NET Framework 4.6.2 installed successfully.' + #13#10 + 'Exit Code: ' + IntToStr(intDotNetResultCode)); 
      CancelWithoutPrompt := False; 
      AbortInstall := False; 
     end 
     else 
     begin 
      if WizardSilent = True then 
      begin 
       Log('Microsoft .NET Framework 4.6.2 failed to install.' + #13#10 + 'Exit Code: ' + IntToStr(intDotNetResultCode) + #13#10 + 'Setup aborted.'); 
      end 
      else 
      begin 
       MsgBox('Microsoft .NET Framework 4.6.2 failed to install.' + #13#10 + #13#10 + 
       'Exit Code: ' + IntToStr(intDotNetResultCode) + #13#10 + #13#10 + 
       'Setup aborted. Click Next or Cancel to exit, or Back to try again.', 
       mbCriticalError, MB_OK); 
      end; 
      PrepareToInstallProgressBar.Visible := False; 
      PrepareToInstallLabel.Caption := 'Microsoft .NET Framework 4.6.2 failed to install.' + #13#10 + #13#10 + 'Exit Code: ' + IntToStr(intDotNetResultCode) + #13#10 + #13#10 + 'Setup aborted. Click Next or Cancel to exit, or Back to try again.'; 
      CancelWithoutPrompt := True; 
      AbortInstall := True; 
      Abort; 
     end; 
    end; 
end; 

procedure InitializeWizard(); 
begin 
//Define the label for the Preparing to Install page 
    PrepareToInstallLabel := TNewStaticText.Create(WizardForm); 
    with PrepareToInstallLabel do 
    begin 
     Visible := False; 
     Parent := WizardForm.PreparingPage; 
     Left := WizardForm.StatusLabel.Left; 
     Top := WizardForm.StatusLabel.Top; 
    end; 
//Define Progress Bar for the Preparing to Install Page 
    PrepareToInstallProgressBar := TNewProgressBar.Create(WizardForm); 
    with PrepareToInstallProgressBar do 
    begin 
     Visible := False; 
     Parent := WizardForm.PreparingPage; 
     Left := WizardForm.ProgressGauge.Left; 
     Top := WizardForm.ProgressGauge.Top; 
     Width := WizardForm.ProgressGauge.Width; 
     Height := WizardForm.ProgressGauge.Height; 
     PrepareToInstallProgressBar.Style := npbstMarquee; 
    end; 
end; 

procedure CurStepChanged(CurStep: TSetupStep); 
begin 
    if CurStep = ssInstall then 
    begin 
     if AbortInstall = True then 
     begin 
      Abort; 
     end; 
    end; 
end; 

, 나는 어떻게 이노 설치 프로그램이 실행하고를 사용하는에 따라 .NET Framework를 설치 표시를 볼 GUI의 양을 제어 할 수 /q 또는 /passive를 사용하여 자동 또는 무인 하나에 설치 유형을 설정하고 Marquee 스타일 진행 표시 줄이 나타나고 있음을 나타냅니다. 그러나 Microsoft 설명서 here에서 .NET Framework 설치 관리자가 /pipe 스위치를 사용하여 설치 진행 상황을보고하도록 할 수 있으므로 실제 진행 상황에서 일반 스타일 진행률 표시 줄을 대화식으로 업데이트 할 수 있습니다. 이는 .NET Framework 설치 프로그램이 완전히 숨겨져 Inno Setup이 상대 진행 상황을 나타내는 데 사용된다는 것을 의미합니다. 이는 훨씬 더 세련된 솔루션입니다. 불행히도, 나는 C++을 모르며 초보 프로그래머 일뿐입니다. 그러므로 Inno Setup으로 가능하면 누구나 확인할 수 있습니까? 그렇다면 어떻게 시도 할 수 있습니까?

+0

@KenWhite 나에게 맞는 것 같습니다. 'CreateFileMapping'과'MapViewOfFile' 같은 함수를 가져와야합니다. 그리고 메모리 매핑 파일을'string'으로 매핑하십시오. 아니면 특정 문제가 있습니까? –

+0

@ Martin : 전혀 문제가 보이지 않습니다. 나는 MMIO를위한 "기본적으로 제공되는"고유 한 지원이 없다는 것을 언급하고있었습니다. –

+0

@Ken Inno Setup 소스 코드를 수정하는 것이 좋습니다. 필자는 Pascal Script에서 이것을 구현하는 것이 타당하다고 믿습니다. –

답변

6

다음은 아래처럼 사용하거나 설치 과정의 다른 장소에 수
How to: Get Progress from the .NET Framework 4.5 Installer

[Files] 
Source: "NDP462-KB3151800-x86-x64-AllOS-ENU.exe"; Flags: dontcopy 

[Code] 

{ Change to unique names }  
const 
    SectionName = 'MyProgSetup'; 
    EventName = 'MyProgSetupEvent'; 

const 
    INFINITE = 65535; 
    WAIT_OBJECT_0 = 0; 
    WAIT_TIMEOUT = $00000102; 
    FILE_MAP_WRITE = $0002; 
    E_PENDING = $8000000A; 
    S_OK = 0; 
    MMIO_V45 = 1; 
    MAX_PATH = 260; 
    SEE_MASK_NOCLOSEPROCESS = $00000040; 
    INVALID_HANDLE_VALUE = -1; 
    PAGE_READWRITE = 4; 
    MMIO_SIZE = 65536; 

type 
    TMmioDataStructure = record 
    DownloadFinished: Boolean; { download done yet? } 
    InstallFinished: Boolean; { install done yet? } 
    DownloadAbort: Boolean; { set downloader to abort } 
    InstallAbort: Boolean; { set installer to abort } 
    DownloadFinishedResult: Cardinal; { resultant HRESULT for download } 
    InstallFinishedResult: Cardinal; { resultant HRESULT for install } 
    InternalError: Cardinal; 
    CurrentItemStep: array[0..MAX_PATH-1] of WideChar; 
    DownloadSoFar: Byte; { download progress 0 - 255 (0 to 100% done) } 
    InstallSoFar: Byte; { install progress 0 - 255 (0 to 100% done) } 
    { event that chainer 'creates' and chainee 'opens'to sync communications } 
    EventName: array[0..MAX_PATH-1] of WideChar; 

    Version: Byte; { version of the data structure, set by chainer. } 
        { 0x0 : .Net 4.0 } 
        { 0x1 : .Net 4.5 } 

    { current message being sent by the chainee, 0 if no message is active } 
    MessageCode: Cardinal; 
    { chainer's response to current message, 0 if not yet handled } 
    MessageResponse: Cardinal; 
    { length of the m_messageData field in bytes } 
    MessageDataLength: Cardinal; 
    { variable length buffer, content depends on m_messageCode } 
    MessageData: array[0..MMIO_SIZE] of Byte; 
    end; 

function CreateFileMapping(
    File: THandle; Attributes: Cardinal; Protect: Cardinal; 
    MaximumSizeHigh: Cardinal; MaximumSizeLow: Cardinal; Name: string): THandle; 
    external '[email protected] stdcall'; 

function CreateEvent(
    EventAttributes: Cardinal; ManualReset: Boolean; InitialState: Boolean; 
    Name: string): THandle; 
    external '[email protected] stdcall'; 

function CreateMutex(
    MutexAttributes: Cardinal; InitialOwner: Boolean; Name: string): THandle; 
    external '[email protected] stdcall'; 

function WaitForSingleObject(
    Handle: THandle; Milliseconds: Cardinal): Cardinal; 
    external '[email protected] stdcall'; 

function MapViewOfFile(
    FileMappingObject: THandle; DesiredAccess: Cardinal; FileOffsetHigh: Cardinal; 
    FileOffsetLow: Cardinal; NumberOfBytesToMap: Cardinal): Cardinal; 
    external '[email protected] stdcall'; 

function ReleaseMutex(Mutex: THandle): Boolean; 
    external '[email protected] stdcall'; 

type 
    TShellExecuteInfo = record 
    cbSize: DWORD; 
    fMask: Cardinal; 
    Wnd: HWND; 
    lpVerb: string; 
    lpFile: string; 
    lpParameters: string; 
    lpDirectory: string; 
    nShow: Integer; 
    hInstApp: THandle;  
    lpIDList: DWORD; 
    lpClass: string; 
    hkeyClass: THandle; 
    dwHotKey: DWORD; 
    hMonitor: THandle; 
    hProcess: THandle; 
    end; 

function ShellExecuteEx(var lpExecInfo: TShellExecuteInfo): BOOL; 
    external '[email protected] stdcall'; 

function GetExitCodeProcess(Process: THandle; var ExitCode: Cardinal): Boolean; 
    external '[email protected] stdcall'; 

procedure CopyPointerToData(
    var Destination: TMmioDataStructure; Source: Cardinal; Length: Cardinal); 
    external '[email protected] stdcall'; 

procedure CopyDataToPointer(
    Destination: Cardinal; var Source: TMmioDataStructure; Length: Cardinal); 
    external '[email protected] stdcall'; 

var 
    FileMapping: THandle; 
    EventChaineeSend: THandle; 
    EventChainerSend: THandle; 
    Mutex: THandle; 
    Data: TMmioDataStructure; 
    View: Cardinal; 

procedure LockDataMutex; 
var 
    R: Cardinal; 
begin 
    R := WaitForSingleObject(Mutex, INFINITE); 
    Log(Format('WaitForSingleObject = %d', [Integer(R)])); 
    if R <> WAIT_OBJECT_0 then 
    RaiseException('Error waiting for mutex'); 
end; 

procedure UnlockDataMutex; 
var 
    R: Boolean; 
begin 
    R := ReleaseMutex(Mutex); 
    Log(Format('ReleaseMutex = %d', [Integer(R)])); 
    if not R then 
    RaiseException('Error releasing waiting for mutex'); 
end; 

procedure ReadData; 
begin 
    CopyPointerToData(Data, View, MMIO_SIZE); 
end; 

procedure WriteData; 
begin 
    CopyDataToPointer(View, Data, MMIO_SIZE); 
end; 

procedure InitializeChainer; 
var 
    I: Integer; 
begin 
    Log('Initializing chainer'); 

    FileMapping := 
    CreateFileMapping(
     INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, MMIO_SIZE, SectionName); 
    Log(Format('FileMapping = %d', [Integer(FileMapping)])); 
    if FileMapping = 0 then 
    RaiseException('Error creating file mapping'); 

    EventChaineeSend := CreateEvent(0, False, False, EventName); 
    Log(Format('EventChaineeSend = %d', [Integer(EventChaineeSend)])); 
    if EventChaineeSend = 0 then 
    RaiseException('Error creating chainee event'); 

    EventChainerSend := CreateEvent(0, False, False, EventName + '_send'); 
    Log(Format('EventChainerSend = %d', [Integer(EventChainerSend)])); 
    if EventChainerSend = 0 then 
    RaiseException('Error creating chainer event'); 

    Mutex := CreateMutex(0, False, EventName + '_mutex'); 
    Log(Format('Mutex = %d', [Integer(Mutex)])); 
    if Mutex = 0 then 
    RaiseException('Error creating mutex'); 

    View := 
    MapViewOfFile(FileMapping, FILE_MAP_WRITE, 0, 0, 0); 
    if View = 0 then 
    RaiseException('Cannot map data view'); 
    Log('Mapped data view'); 

    LockDataMutex; 

    ReadData; 

    Log('Initializing data'); 
    for I := 1 to Length(EventName) do 
    Data.EventName[I - 1] := EventName[I]; 
    Data.EventName[Length(EventName)] := #$00; 

    { Download specific data } 
    Data.DownloadFinished := False; 
    Data.DownloadSoFar := 0; 
    Data.DownloadFinishedResult := E_PENDING; 
    Data.DownloadAbort := False; 

    { Install specific data } 
    Data.InstallFinished := False; 
    Data.InstallSoFar := 0; 
    Data.InstallFinishedResult := E_PENDING; 
    Data.InstallAbort := False; 

    Data.InternalError := S_OK; 

    Data.Version := MMIO_V45; 
    Data.MessageCode := 0; 
    Data.MessageResponse := 0; 
    Data.MessageDataLength := 0; 

    Log('Initialized data'); 

    WriteData; 

    UnlockDataMutex; 

    Log('Initialized chainer'); 
end; 

var 
    ProgressPage: TOutputProgressWizardPage; 

procedure InstallNetFramework; 
var 
    R: Cardinal; 
    ExecInfo: TShellExecuteInfo; 
    ExitCode: Cardinal; 
    InstallError: string; 
    Completed: Boolean; 
    Progress: Integer; 
begin 
    ExtractTemporaryFile('NDP462-KB3151800-x86-x64-AllOS-ENU.exe'); 

    { Start the installer using ShellExecuteEx to get process ID } 
    ExecInfo.cbSize := SizeOf(ExecInfo); 
    ExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS; 
    ExecInfo.Wnd := 0; 
    ExecInfo.lpFile := ExpandConstant('{tmp}\NDP462-KB3151800-x86-x64-AllOS-ENU.exe'); 
    ExecInfo.lpParameters := '/pipe ' + SectionName + ' /chainingpackage mysetup /q'; 
    ExecInfo.nShow := SW_HIDE; 

    if not ShellExecuteEx(ExecInfo) then 
    RaiseException('Cannot start .NET framework installer'); 

    Log(Format('.NET framework installer started as process %x', [ExecInfo.hProcess])); 

    Progress := 0; 
    { Displaying indefinite progress while the framework installer is initializing } 
    ProgressPage.ProgressBar.Style := npbstMarquee; 
    ProgressPage.SetProgress(Progress, 100); 
    ProgressPage.Show; 
    try 
    Completed := False; 

    while not Completed do 
    begin 
     { Check if the installer process has finished already } 
     R := WaitForSingleObject(ExecInfo.hProcess, 0); 
     if R = WAIT_OBJECT_0 then 
     begin 
     Log('.NET framework installer completed'); 
     Completed := True; 
     if not GetExitCodeProcess(ExecInfo.hProcess, ExitCode) then 
     begin 
      InstallError := 'Cannot get .NET framework installer exit code'; 
     end 
      else 
     begin 
      Log(Format('Exit code: %d', [Integer(ExitCode)])); 
      if ExitCode <> 0 then 
      begin 
      InstallError := 
       Format('.NET framework installer failed with exit code %d', [ExitCode]); 
      end; 
     end; 
     end 
     else 
     if R <> WAIT_TIMEOUT then 
     begin 
     InstallError := 'Error waiting for .NET framework installer to complete'; 
     Completed := True; 
     end 
     else 
     begin 
     { Check if the installer process has signaled progress event } 
     R := WaitForSingleObject(EventChaineeSend, 0); 
     if R = WAIT_OBJECT_0 then 
     begin 
      Log('Got event from the installer'); 
      { Read progress data } 
      LockDataMutex; 
      ReadData; 
      Log(Format(
      'DownloadSoFar = %d, InstallSoFar = %d', [ 
       Data.DownloadSoFar, Data.InstallSoFar])); 
      Progress := Integer(Data.InstallSoFar) * 100 div 255; 
      Log(Format('Progress = %d', [Progress])); 
      UnlockDataMutex; 
      { Once we get any progress data, switch to definite progress display } 
      ProgressPage.ProgressBar.Style := npbstNormal; 
      ProgressPage.SetProgress(Progress, 100); 
     end 
      else 
     if R <> WAIT_TIMEOUT then 
     begin 
      InstallError := 'Error waiting for .NET framework installer event'; 
      Completed := True; 
     end 
      else 
     begin 
      { Seemingly pointless as progress did not change, } 
      { but it pumps a message queue as a side effect } 
      ProgressPage.SetProgress(Progress, 100); 
      Sleep(100); 
     end; 
     end; 
    end; 
    finally 
    ProgressPage.Hide; 
    end; 

    if InstallError <> '' then 
    begin 
    { RaiseException does not work properly while TOutputProgressWizardPage is shown } 
    RaiseException(InstallError); 
    end; 
end; 

function InitializeSetup(): Boolean; 
begin 
    InitializeChainer; 

    Result := True; 
end; 

procedure InitializeWizard(); 
begin 
    ProgressPage := CreateOutputProgressPage('Installing .NET framework', ''); 
end; 

의 코드의 파스칼 스크립트 구현을 보여줍니다.

function NextButtonClick(CurPageID: Integer): Boolean; 
begin 
    Result := True; 

    if CurPageID = wpReady then 
    begin 
    try 
     InstallNetFramework; 
    except 
     MsgBox(GetExceptionMessage, mbError, MB_OK); 
     Result := False; 
    end; 
    end; 
end; 

은 다음 스크린 샷 이노 설정에서 "진행 페이지가"물론 .NET 프레임 워크 설치가 /q 스위치에 의해 숨겨진 .NET 프레임 워크 설치 (에 연결하는 방법을 보여줍니다, 일시적 불과했다 스크린 샷을 얻기 위해 표시됨).

  • NDP462-KB3151800-x86-x64-AllOS-ENU.exe (.NET 프레임 워크 4.6 -

    enter image description here


    나는 성공적으로

    • dotnetfx45_full_x86_x64.exe (오프라인 설치 .NET 프레임 워크 4.5)에 코드를 테스트했습니다. 2 - 오프라인 설치자)

    참고 th 위의 두 설치 관리자가 오프라인이므로 코드의 InstallSoFar 만 고려합니다. 온라인 설치 프로그램의 경우 DownloadSoFar도 고려해야합니다. 실제로 오프라인 설치 프로그램도 언젠가 다운로드해야합니다.


    Inno Setup Exec() function Wait for a limited time에서 촬영 한 ShellExecuteEx 코드입니다.

  • +0

    매우 훌륭하게 완료되었습니다. –

    +0

    좋은 대답, 좋은 작품. –

    +0

    아주 좋은 대답입니다. – GTAVLover