Inno Setup从.NET Framework 4.5(或更高版本)安装程序获取进度以更新进度栏位置

罗伯特·威格利

我目前正在安装.NET Framework 4.6.2作为PrepareToInstall事件功能的先决条件,以便我可以获取退出代码,设置NeedsReboot状态或在安装失败时中止操作。我的代码在下面,并且一切正常。

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安装程序的显示方式来设置安装类型为无提示或无人值守,/q/passive控制.NET Framework安装程序显示的可见GUI的数量,具体取决于Inno Setup的运行方式以及使用Marquee样式进度条指示是否存在某些问题。发生。但是,从此处的Microsoft文档看来,可以使用.NET Framework安装程序来报告安装进度,以报告其安装进度。/pipe开关,这可能允许它以交互方式在实际进度上更新普通样式进度条。这意味着.NET Framework安装程序可能会被完全隐藏,并且Inno Setup用来指示相对进度,这是一个更为整洁的解决方案。不幸的是,我不懂C ++,而且只是一个新手程序员。因此,任何人都可以确认是否可以使用Inno Setup进行此操作,如果可以,如何进行尝试?

马丁·普里克里(Martin Prikryl)

下面显示了如何操作中的代码的Pascal脚本实现
:从.NET Framework 4.5安装程序获取进度

[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;

以下屏幕截图显示了Inno Setup中的“进度页”如何链接到.NET Framework安装程序(当然,.NET Framework安装程序被/q开关隐藏了,只是为了获取屏幕快照而临时显示)。

在此处输入图片说明


我已经在上成功测试了代码

请注意,该代码InstallSoFar考虑了代码,因为上述两个安装程序都是脱机的。对于在线安装程序,DownloadSoFar也应考虑在内。实际上,甚至离线安装程序有时也会下载一些内容。


ShellExecuteExInno Setup Exec()函数获取代码等待有限的时间

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Inno Setup .net Framework自动安装程序问题

来自分类Dev

如何使用Inno Setup作为必备组件安装.NET Framework?

来自分类Dev

从Dephi DLL更新Inno Setup进度栏

来自分类Dev

Inno Setup-从.Net 4.5位置运行InstallUtil

来自分类Dev

Inno Setup,检测Java版本

来自分类Dev

如何从Inno Setup安装JRE?

来自分类Dev

在Inno Setup中取消安装

来自分类Dev

使用Inno Setup启动.NET Core安装程序失败,结果代码为2

来自分类Dev

.Net Framework 4安装失败

来自分类Dev

使Inno Setup Installer向主安装程序报告其安装进度状态

来自分类Dev

使用Inno Setup创建透明的安装程序?

来自分类Dev

自动更新Inno Setup程序

来自分类Dev

根据在线文件设置Inno Setup安装程序版本

来自分类Dev

Inno Setup:仅在非常安静时安装

来自分类Dev

Inno Setup禁用安装向导页面

来自分类Dev

Inno Setup“安装完成”向导页面

来自分类Dev

Inno Setup:仅在不静音时安装

来自分类Dev

Inno Setup“安装完成”向导页面

来自分类Dev

Inno Setup禁用安装向导页面

来自分类Dev

Inno Setup –更新之前压缩本地文件

来自分类Dev

卸载程序中的Inno Setup脚本常量

来自分类Dev

获取单选按钮值[INNO SETUP]

来自分类Dev

Inno Setup获取默认浏览器

来自分类Dev

Inno Setup:如何在“运行”部分操作进度栏?

来自分类Dev

Inno Setup:如何在“注册”部分操作进度栏?

来自分类Dev

Inno Setup通过文件循环并注册每个.NET dll

来自分类Dev

在Inno Setup中安装结束时,以管理员权限运行进程(net.exe或sc.exe)

来自分类Dev

Inno Setup-用于多个安装程序的安装程序

来自分类Dev

Inno Setup-用于多个安装程序的安装程序

Related 相关文章

热门标签

归档