다음은 아래처럼 사용하거나 설치 과정의 다른 장소에 수
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](https://i.stack.imgur.com/HI8yJ.png)
나는 성공적으로
- dotnetfx45_full_x86_x64.exe (오프라인 설치 .NET 프레임 워크 4.5)에 코드를 테스트했습니다. 2 - 오프라인 설치자)
참고 th 위의 두 설치 관리자가 오프라인이므로 코드의 InstallSoFar
만 고려합니다. 온라인 설치 프로그램의 경우 DownloadSoFar
도 고려해야합니다. 실제로 오프라인 설치 프로그램도 언젠가 다운로드해야합니다.
Inno Setup Exec() function Wait for a limited time에서 촬영 한 ShellExecuteEx
코드입니다.
@KenWhite 나에게 맞는 것 같습니다. 'CreateFileMapping'과'MapViewOfFile' 같은 함수를 가져와야합니다. 그리고 메모리 매핑 파일을'string'으로 매핑하십시오. 아니면 특정 문제가 있습니까? –
@ Martin : 전혀 문제가 보이지 않습니다. 나는 MMIO를위한 "기본적으로 제공되는"고유 한 지원이 없다는 것을 언급하고있었습니다. –
@Ken Inno Setup 소스 코드를 수정하는 것이 좋습니다. 필자는 Pascal Script에서 이것을 구현하는 것이 타당하다고 믿습니다. –