I have a XML with some flags in Base64.
I want to decode them to show them on my installer's list box, is there any way to do it?
To convert a Base64 string to actual binary data, you can use the CryptStringToBinary Windows API function.
function CryptStringToBinary(
sz: string; cch: LongWord; flags: LongWord; binary: string; var size: LongWord;
skip: LongWord; flagsused: LongWord): Integer;
external 'CryptStringToBinaryW#crypt32.dll stdcall';
const
CRYPT_STRING_BASE64 = $01;
procedure LoadBitmapFromBase64(Bitmap: TBitmap; S: string);
var
Stream: TStream;
Buffer: string;
Size: LongWord;
Res: Integer;
begin
Stream := TStringStream.Create('');
try
Size := Length(S);
SetLength(Buffer, Size + 1);
Res := CryptStringToBinary(S, Size, CRYPT_STRING_BASE64, Buffer, Size, 0, 0);
if Res = 0 then
begin
RaiseException('Error decoding Base64 string');
end;
Stream.WriteBuffer(Buffer, Size);
Stream.Position := 0;
Bitmap.LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
The code requires the Unicode version of Inno Setup (the only version as of Inno Setup 6). You should not use the Ansi version anyway, in the 21st century. Though ironically, implementing this in the Ansi version would be way easier. See my answer to Writing binary file in Inno Setup for a use of the CryptStringToBinary that's compatible with both Ansi and Unicode version of Inno Setup.
Related
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.
I am using PrivilegesRequired=lowest in my Inno Setup script. If setup is running elevated, i.e. IsAdminLoggedOn or IsPowerUserLoggedOn reports TRUE, how can I determine if the elevated user account is the same account from which setup was launched?
My script can do different things accordingly.
You can use WTSQuerySessionInformation to retrieve an account username for the current Windows logon session.
function WTSQuerySessionInformation(
hServer: THandle; SessionId: Cardinal; WTSInfoClass: Integer;
var pBuffer: DWord; var BytesReturned: DWord): Boolean;
external 'WTSQuerySessionInformationW#wtsapi32.dll stdcall';
procedure WTSFreeMemory(pMemory: DWord);
external 'WTSFreeMemory#wtsapi32.dll stdcall';
procedure RtlMoveMemoryAsString(Dest: string; Source: DWord; Len: Integer);
external 'RtlMoveMemory#kernel32.dll stdcall';
const
WTS_CURRENT_SERVER_HANDLE = 0;
WTS_CURRENT_SESSION = -1;
WTSUserName = 5;
function GetCurrentSessionUserName: string;
var
Buffer: DWord;
BytesReturned: DWord;
QueryResult: Boolean;
begin
QueryResult :=
WTSQuerySessionInformation(
WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSUserName, Buffer,
BytesReturned);
if not QueryResult then
begin
Log('Failed to retrieve username');
Result := '';
end
else
begin
SetLength(Result, (BytesReturned div 2) - 1);
RtlMoveMemoryAsString(Result, Buffer, BytesReturned);
WTSFreeMemory(Buffer);
Log(Format('Retrieved username "%s"', [Result]));
end;
end;
(The code is for Unicode version of Inno Setup – The only version as of Inno Setup 6).
You can then compare the result against GetUserNameString.
You may need to add a domain name into the comparison.
If you need the full account name of the current user (e.g., authority\username format), you can use the GetUserNameExW Windows API function. The below snippet demonstrates how to call this function from Inno Setup:
const
ERROR_MORE_DATA = 234;
function GetUserNameExW(NameFormat: Integer; lpNameBuffer: string; var nSize: DWORD): Boolean;
external 'GetUserNameExW#secur32.dll stdcall';
function GetFullUserName(): string;
var
NumChars: DWORD;
OutStr: string;
begin
result := '';
NumChars := 0;
if (not GetUserNameExW(2, '', NumChars)) and (DLLGetLastError() = ERROR_MORE_DATA) then
begin
SetLength(OutStr, NumChars);
if GetUserNameExW(2, OutStr, NumChars) then
result := Copy(OutStr, 1, NumChars);
end;
end;
(The value 2 passed to the first parameter (NameFormat) in the GetUserNameExW function corresponds to NameSamCompatible in the EXTENDED_NAME_FORMAT enumeration.)
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.
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.
Am trying to find out how can I check, if proxy is being used in the computer via Inno Setup.
You can use the WinHttpGetDefaultProxyConfiguration function:
type
WINHTTP_PROXY_INFO = record
AccessType: Cardinal;
Proxy: Cardinal;
ProxyBypass: Cardinal;
end;
function WinHttpGetDefaultProxyConfiguration(var ProxyInfo: WINHTTP_PROXY_INFO): Boolean;
external 'WinHttpGetDefaultProxyConfiguration#winhttp.dll stdcall';
function StrCpyN(S1: string; S2: Cardinal; Max: Cardinal): Cardinal;
external 'StrCpyNW#shlwapi.dll stdcall';
function GetProxy: string;
var
ProxyInfo: WINHTTP_PROXY_INFO;
begin
if WinHttpGetDefaultProxyConfiguration(ProxyInfo) then
begin
SetLength(Result, 1024);
StrCpyN(Result, ProxyInfo.Proxy, Length(Result) - 1);
Result := Trim(Result);
Log('Retrieved proxy information: ' + Result);
end
else
begin
Log('Cannot retrieve proxy information');
end;
end;
Requires Unicode Inno Setup (the only version as of Inno Setup 6).
You can also use netsh winhttp show proxy command.