Can we make the DwinHs Inno Setup download plugin work faster? - inno-setup

DwinHs has a DwinsHs_Data_Buffer_Length macro. I have set it to a value of 8192 but the download speeds are still rather slow.
For example, I have a 200 MBit connection and the file is 25 MB. It takes two and a half minutes to download.
I have read through some of the answers here:
What is the best memory buffer size to allocate to download a file from Internet?
It suggests a 16K buffer instead. Either way, is there any way we can use a maximum buffer length with Pascal given the user system?
Browsers download faster so why can’t we here in Inno Setup?
Example
[ISPP]
#define HelpDocSetupURL "https://www.publictalksoftware.co.uk/downloads/PublicTalksHelpDocumentationSetup.exe"
[Setup]
AppID = TestID
AppName = Test App
OutputBaseFilename = My_Test_App_Setup
AppVersion = 1.0
DefaultDirName = {pf}\MyTestApp
DefaultGroupName = My Test App
[Tasks]
Name: "downloadhelp"; Description: "Task Desc"; GroupDescription: "Group Desc";
[Files]
Source: "{tmp}\HelpDocSetup.exe"; \
DestDir: "{app}"; \
Flags: external deleteafterinstall; \
Tasks: downloadhelp; \
Check: DwinsHs_Check( ExpandConstant('{tmp}\HelpDocSetup.exe'), '{#HelpDocSetupURL}', 'My_Setup', 'Get', 0, 0 )
[Code]
program Setup;
#define DwinsHs_Use_Predefined_Downloading_WizardPage
#define DwinsHs_Data_Buffer_Length 8192
#include "dwinshs.iss"
procedure InitializeWizard();
begin
DwinsHs_InitializeWizard(wpPreparing);
end;
procedure CurPageChanged(CurPageID: Integer);
begin
DwinsHs_CurPageChanged(CurPageID, nil, nil);
end;
function ShouldSkipPage(CurPageId: Integer): Boolean;
begin
Result := False;
DwinsHs_ShouldSkipPage(CurPageId, Result);
end;
function BackButtonClick(CurPageID: Integer): Boolean;
begin
Result := True;
DwinsHs_BackButtonClick(CurPageID);
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
Result := True;
DwinsHs_NextButtonClick(CurPageID, Result);
end;
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
DwinsHs_CancelButtonClick(CurPageID, Cancel, Confirm);
end;

The software author got back to me about this issue and proposed:
You can try to set the cache size to 16384, 32768 or 655536.
So I set it to:
#define DwinsHs_Data_Buffer_Length 655536
The results are much better:
As you can see, only 10 seconds.
I don't know what impact such a cache value would have for those who had a slower internet connection.

Related

Run post install file replace before creating service

I have configured the below script to ask the user for an IP address as part of the install wizard, this address gets written to a config file which the application will reference to know where to communicate with.
However currently, the service is created before the configuration file is updated (Run section happens before CurStep = ssPostInstall), so the default value has already been read from the file before it is updated.
This means that a further manual service restart is currently required to get the application to communicate.
I understand it is possible to have the Run section happen after the ssPostInstall.
I have read the article Inno Setup: How to run a code procedure in Run section or before Run section? which suggests I can use the BeforeInstall: parameter as part of the run command to perform the CurStepChanged procedure first. However, this leads to an error of
Required function or procedure 'CurStepChanged' found but not with a compatible prototype.
I also tried moving the run line and command above the CurStepChanged section (to match the article mentioned as closely as possible), but this still returned the same error.
Can anyone offer some guidance as to where I am going wrong with the configuration?
[Code]
var
PrimaryServerPage: TInputQueryWizardPage;
function FileReplaceString(ReplaceString: string):boolean;
var
MyFile : TStrings;
MyText : string;
begin
Log('Replacing in file');
MyFile := TStringList.Create;
try
Result := true;
try
MyFile.LoadFromFile(ExpandConstant('{app}' + '\providers\print\win\print-provider.conf'));
Log('File loaded');
MyText := MyFile.Text;
{ Only save if text has been changed. }
if StringChangeEx(MyText, 'REPLACE_WITH_CUSTOMER_IP', ReplaceString, True) > 0 then
begin;
Log('IP address inserted');
MyFile.Text := MyText;
MyFile.SaveToFile(ExpandConstant('{app}' + '\providers\print\win\print-provider.conf'));
Log('File saved');
end;
except
Result := false;
end;
finally
MyFile.Free;
end;
Result := True;
end;
procedure InitializeWizard;
begin
PrimaryServerPage :=
CreateInputQueryPage(
wpWelcome, 'Application Server Details', 'Where is installed?',
'Please specify the IP address or hostname of your ' +
'Primary Application Server, then click Next.');
PrimaryServerPage.Add('Primary Application Server IP/Hostname:', False);
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
Log('File installed, replacing IP address');
FileReplaceString(PrimaryServerPage.Values[0]);
end;
end;
[run]
Filename: {sys}\sc.exe; Parameters: "create PCPrintProvider start= auto binPath= ""{app}\providers\print\win\pc-print.exe PCPrintProvider"" depend= Spooler" ; Flags: runhidden ; BeforeInstall: CurStepChanged
Filename: {sys}\sc.exe; Parameters: "start PCPrintProvider" ; Flags: runhidden ; BeforeInstall: CurStepChanged
You have to define your own procedure for the BeforeInstall parameter:
[Run]
Filename: {sys}\sc.exe; \
Parameters: "create PCPrintProvider start= auto binPath= ""{app}\providers\print\win\pc-print.exe PCPrintProvider"" depend= Spooler"; \
Flags: runhidden; BeforeInstall: ReplaceIPAddress
[Code]
procedure ReplaceIPAddress;
begin
FileReplaceString(PrimaryServerPage.Values[0]);
end;
Try using Check param
[run]
Filename: {sys}\sc.exe; Parameters: "create PCPrintProvider start= auto binPath= ""{app}\providers\print\win\pc-print.exe PCPrintProvider"" depend= Spooler" ; Flags: runhidden ; Check: IsIpChanged;
[code]
function IsIpChanged: Boolean;
begin
FileReplaceString(PrimaryServerPage.Values[0]);
Result := True;
end;

Inno Setup - How to force programs started from Inno Setup to open on a certain place on the screen?

With this code: Install DirectX & VCRedist in freearc default script when progress bar is full & paused after main file extraction I can install DirectX and VCRedist with Inno Setup. But, is it possible to force the installation window of these programs to a certain place on the screen? For example:
It's hardly possible to make an application to start at desired position, unless the application explicitly supports it.
So in general, what you can do is to watch for a certain window to appear and move it afterwards. You can identify the window by its caption (FindWindowByWindowName) or class (FindWindowByClassName). Drawback is that the window will briefly appear on its default position.
[Files]
Source: "DXWebSetup.exe"; DestDir: "{tmp}"; Flags: deleteafterinstall
[Run]
Filename: "{tmp}\DXWebSetup.exe"; StatusMsg: "Installing DirectX..."; \
BeforeInstall: StartWaitingForDirectXWindow; \
AfterInstall: StopWaitingForDirectXWindow
[Code]
function SetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc: LongWord): LongWord;
external 'SetTimer#User32.dll stdcall';
function KillTimer(hWnd, nIDEvent: LongWord): LongWord;
external 'KillTimer#User32.dll stdcall';
function GetTickCount: DWord; external 'GetTickCount#kernel32 stdcall';
function SetWindowPos(hWnd: HWND; hWndInsertAfter: HWND; X: Integer; Y: Integer;
cx: Integer; cy: Integer; uFlags: UINT): BOOL;
external 'SetWindowPos#user32.dll stdcall';
const
SWP_NOSIZE = $01;
SWP_NOZORDER = $04;
var
WindowWaitTimer: LongWord;
WindowWaitStarted: DWord;
MoveWindowRunning: Boolean;
procedure MoveDirectXWindowProc(
H: LongWord; Msg: LongWord; IdEvent: LongWord; Time: LongWord);
var
Retry: Boolean;
Handle: HWND;
begin
Handle := FindWindowByWindowName('Installing Microsoft(R) DirectX(R)');
if Handle = 0 then
begin
if DWord(GetTickCount - WindowWaitStarted) < 5000 then
begin
Log('DirectX window not found, will try again shortly');
Retry := True;
end
else
begin
Log('Giving up waiting for DirectX window');
Retry := False;
end
end
else
begin
Log('DirectX window found');
SetWindowPos(
Handle, 0, WizardForm.Left + ScaleX(150), WizardForm.Top + ScaleX(30),
0, 0, SWP_NOSIZE or SWP_NOZORDER);
Retry := False;
end;
if not Retry then
begin
Log('Stopping timer');
KillTimer(0, WindowWaitTimer);
WindowWaitTimer := 0;
end;
end;
procedure StartWaitingForDirectXWindow;
begin
Log('Starting waiting for DirectX window');
WindowWaitTimer := SetTimer(0, 0, 100, CreateCallback(#MoveDirectXWindowProc));
WindowWaitStarted := GetTickCount;
end;
procedure StopWaitingForDirectXWindow;
begin
if WindowWaitTimer <> 0 then
begin
Log('DirectX installer finished, and we are still waiting for its window, stopping');
KillTimer(0, WindowWaitTimer);
WindowWaitTimer := 0;
end
else
begin
Log('DirectX installer finished, and we are no longer waiting for its window');
end;
end;
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library.

Get Image File Information using MediaInfo library in Inno Setup Pascal Script

I have been trying for more than two days to get JPEG Image and MP4 Video File Information using MediaInfo.DLL in my Pascal Script.
But I keep getting error
Runtime Error (at 6:366) - Access Violation at address 0042FD23. Read of address 8065241E.'
The error mostly points to (at 6:366).
I can't think what problem is causing this exception when trying to get Media Information using MediaInfo.DLL.
The code I added to my Script:
[Files]
Source: Lamborghini_Aventador.jpg; DestDir: {tmp}; Flags: dontcopy
Source: MediaInfo.dll; DestDir: {tmp}; Flags: dontcopy
[Code]
#ifdef UNICODE
type
PWideChar = WideString;
#endif
const
StreamKind_Image = 5;
InfoKind_Text = 1;
function MediaInfo_New: Cardinal;
external 'MediaInfo_New#{tmp}\MediaInfo.dll stdcall delayload';
function MediaInfo_Open(Handle: Cardinal; File__: PWideChar): Boolean;
external 'MediaInfo_Open#{tmp}\MediaInfo.dll stdcall delayload';
function MediaInfo_Get(Handle: Cardinal; StreamKind: Integer; StreamNumber: Integer; Parameter: PWideChar; KindOfInfo: Integer; KindOfSearch: Integer): PWideChar;
external 'MediaInfo_Get#{tmp}\MediaInfo.dll stdcall delayload';
procedure RetrieveImageInformation;
var
IHandle: Cardinal;
Width: PWideChar;
begin
ExtractTemporaryFile('Lamborghini_Aventador.jpg');
ExtractTemporaryFile('MediaInfo.dll');
IHandle := MediaInfo_New();
MediaInfo_Open(IHandle, PWideChar(ExpandConstant('{tmp}\Lamborghini_Aventador.jpg')));
Width := MediaInfo_Get(IHandle, StreamKind_Image, 0, 'Width', InfoKind_Text, 0);
Log('Width of the JPEG Image: ' + PWideChar(Width) + '.');
end;
The line which the exception is generating is:
Width := MediaInfo_Get(IHandle, StreamKind_Image, 0, 'Width', InfoKind_Text, 0);
I expected that the compiler output will be Width of the JPEG Image: 1920.
I use latest version of Unicode Inno Setup Compiler (5.5.9 - U)
Thanks in advance for your important help.
I do not think you can call a function that returns a pointer to a string (character buffer) from Inno Setup Pascal Script.
But you can hack it like this:
Declare the function as if it returns Cardinal;
Use some available function that takes a pointer and copies it to another pointer. Declare the source pointer as Cardinal and the target pointer as string. One such function is the StrCpyN.
function MediaInfo_Get(
Handle: Cardinal; StreamKind: Integer; StreamNumber: Integer;
Parameter: string; KindOfInfo: Integer; KindOfSearch: Integer): Cardinal;
external 'MediaInfo_Get#{tmp}\MediaInfo.dll stdcall delayload';
function StrCpyN(S1: string; S2: Cardinal; Max: Cardinal): Cardinal;
external 'StrCpyNW#shlwapi.dll stdcall';
var
P: Cardinal;
S: string;
begin
P := MediaInfo_Get(IHandle, StreamKind_Image, 0, 'Width', InfoKind_Text, InfoKind_Name);
S := StringOfChar(' ', 1024);
StrCpyN(S, P, Length(S) - 1);
S := Trim(S);
...
end;
The code requires Unicode Inno Setup (the only version as of Inno Setpu 6).
You can use the MediaInfo Command Line Interface with Inno Setup Ansi or Unicode Version.
Usage Example:
[Files]
Source: MediaInfo.exe; DestDir: {tmp}; Flags: Dontcopy
[code]
function InitializeSetup(): Boolean;
var
ErrorCode: Integer;
begin
ExtractTemporaryFile('MediaInfo.exe');
ShellExec('Open', 'MediaInfo.exe', ExpandConstant('"YourFileName.mp4" --LogFile="YourFileName Prperties.log"'), ExpandConstant('{tmp}'), SW_HIDE, ewWaitUntilTerminated, ErrorCode);
if SysErrorMessage(DLLGetLastError) = SysErrorMessage(0) then
Result := True;
end;
Now, navigate to the Inno Setup Temporary Directory as Administrator using Run (Windows Key + R) Command and see your Media Information Log File exists there which contains the Information about the File you given in the Command.

Conditional reboot in innosetup installer [duplicate]

My Inno Setup script is used to install a driver. It runs my InstallDriver.exe after this file was copied during step ssInstall.
I need to ask the user to restart in some cases according to the value returned by InstallDriver.exe.
This means that I cannot put InstallDriver.exe in section [Run] because there's no way to monitor it's return value.
So I put it in function CurStepChanged() as follows:
procedure CurStepChanged(CurStep: TSetupStep);
var
TmpFileName, ExecStdout, msg: string;
ResultCode: Integer;
begin
if (CurStep=ssPostInstall) then
begin
Log('CurStepChanged(ssPostInstall)');
TmpFileName := ExpandConstant('{app}') + '\InstallDriver.exe';
if Exec(TmpFileName, 'I', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then .......
However, I can't find a way to make my script restart at this stage.
I thought of using function NeedRestart() to monitor the output of the driver installer, but it is called earlier in the process.
Does it make sense to call the driver installer from within NeedRestart()?
NeedRestart does not look like the right place to install anything. But it would work, as it's fortunately called only once. You will probably want to present a progress somehow though, as the wizard form is almost empty during a call to NeedRestart.
An alternative is to use AfterInstall parameter of the InstallDriver.exe or the driver binary itself (whichever is installed later).
#define InstallDriverName "InstallDriver.exe"
[Files]
Source: "driver.sys"; DestDir: ".."
Source: "{#InstallDriverName}"; DestDir: "{app}"; AfterInstall: InstallDriver
[Code]
var
NeedRestartFlag: Boolean;
const
NeedRestartResultCode = 1;
procedure InstallDriver();
var
InstallDriverPath: string;
ResultCode: Integer;
begin
Log('Installing driver');
InstallDriverPath := ExpandConstant('{app}') + '\{#InstallDriverName}';
if not Exec(InstallDriverPath, 'I', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
Log('Failed to execute driver installation');
end
else
begin
Log(Format('Driver installation finished with code %d', [ResultCode]))
if ResultCode = NeedRestartResultCode then
begin
Log('Need to restart to finish driver installation');
NeedRestartFlag := True;
end;
end;
end;
function NeedRestart(): Boolean;
begin
if NeedRestartFlag then
begin
Log('Need restart');
Result := True;
end
else
begin
Log('Do not need restart');
Result := False;
end;
end;

How to get the full computer name in Inno Setup

I would like to know how to get the Full computer name in Inno Setup, for example Win8-CL01.cpx.local in the following image.
I already know how to get the computer name with GetComputerNameString but I also would like to have the Domain name of the computer. How can I get this full computer name or this domain name ?
There's no built-in function for this in Inno Setup. You can use the GetComputerNameEx Windows API function:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Code]
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
const
ERROR_MORE_DATA = 234;
type
TComputerNameFormat = (
ComputerNameNetBIOS,
ComputerNameDnsHostname,
ComputerNameDnsDomain,
ComputerNameDnsFullyQualified,
ComputerNamePhysicalNetBIOS,
ComputerNamePhysicalDnsHostname,
ComputerNamePhysicalDnsDomain,
ComputerNamePhysicalDnsFullyQualified,
ComputerNameMax
);
function GetComputerNameEx(NameType: TComputerNameFormat; lpBuffer: string; var nSize: DWORD): BOOL;
external 'GetComputerNameEx{#AW}#kernel32.dll stdcall';
function TryGetComputerName(Format: TComputerNameFormat; out Output: string): Boolean;
var
BufLen: DWORD;
begin
Result := False;
BufLen := 0;
if not Boolean(GetComputerNameEx(Format, '', BufLen)) and (DLLGetLastError = ERROR_MORE_DATA) then
begin
SetLength(Output, BufLen);
Result := GetComputerNameEx(Format, Output, BufLen);
end;
end;
procedure InitializeWizard;
var
Name: string;
begin
if TryGetComputerName(ComputerNameDnsFullyQualified, Name) then
MsgBox(Name, mbInformation, MB_OK);
end;
With built-in functions you can get the full name this way:
[Code]
procedure InitializeWizard;
begin
MsgBox(GetComputerNameString + '.' + GetEnv('UserDnsDomain'), mbInformation, MB_OK);
end;
Although TLama's solution gives you wider possibilities of further development.

Resources