Can you tell me how can I do that? Is there any Freepascal unit that can do this for me? I need that so my program can store binary data in it's XML-based fileformat.
Use the base64 unit and its two classes, TBase64EncodingStream and TBase64DecodingStream.
Here is a simple example:
program demo;
uses Classes, base64;
var
DecodedStream: TStringStream;
EncodedStream: TStringStream;
Encoder: TBase64EncodingStream;
Output: string;
begin
DecodedStream := TStringStream.Create('Hello World!');
EncodedStream := TStringStream.Create('');
Encoder := TBase64EncodingStream.Create(EncodedStream);
Encoder.CopyFrom(DecodedStream, DecodedStream.Size);
Output := EncodedStream.DataString;
{ Outputs 'SGVsbG8gV29ybGQh' }
WriteLn(Output);
DecodedStream.Free;
EncodedStream.Free;
Encoder.Free;
end.
And, in the opposite direction:
program demo;
uses Classes, base64;
var
DecodedStream: TStringStream;
EncodedStream: TStringStream;
Decoder: TBase64DecodingStream;
Output: string;
begin
EncodedStream := TStringStream.Create('SGVsbG8gV29ybGQh');
DecodedStream := TStringStream.Create('');
Decoder := TBase64DecodingStream.Create(EncodedStream);
DecodedStream.CopyFrom(Decoder, Decoder.Size);
Output := DecodedStream.DataString;
{ Outputs 'Hello World!' }
WriteLn(Output);
DecodedStream.Free;
EncodedStream.Free;
Decoder.Free;
end.
or the shorthands encodestringbase64 and decodestringbase64 (2.4.4+) for non stream based usage:
Uses Base64;
var
s : AnsiString;
Begin
s:=EncodeStringBase64('Hello world!');
Writeln('Encoded : ',s);
s:=DecodeStringBase64(s);
Writeln('Decoded again : ',s);
end.
Related
Sorry, Forgive my poor english !!!
I found a piece of code in the method CreateFormFromStrings in the System.Net.HttpClient unit 1720 line .
procedure THTTPClient.CreateFormFromStrings(const ASource: TStrings; const AEncoding: TEncoding;
const AHeaders: TNetHeaders; var ASourceStream: TStream; var ASourceHeaders: TNetHeaders);
var
LParams: string;
LEncoding: TEncoding;
I: Integer;
Pos: Integer;
begin
// ******** Ignor some code
ASourceStream := TStringStream.Create(LParams, TEncoding.ASCII, False);
try
ASourceHeaders := [TNetHeader.Create(sContentType,
'application/x-www-form-urlencoded; charset=' + LEncoding.MIMEName)] + AHeaders; // do not translate
except
FreeAndNil(ASourceStream);
raise;
end;
end;
So I took it out and tested it separately. I don't know if the memory leaked. Use valgrind tools to test,you will find:
I want to create a script for Inno Setup where the install path would be taken from a file in defined directory - no registry. I suppose it would require writing specific code for it, where would be defined some variable which will contain the value after reading the file. The path and name of the file is always the same for any user so the only value that changes is the install path.
Complete structure, where InstallLocation is the variable:
{
"FormatVersion": 0,
"bIsIncompleteInstall": false,
"AppVersionString": "1.0.1",
...
"InstallLocation": "h:\\Program Files\\Epic Games\\Limbo",
...
}
Any ideas for ideal code that would do this?
Thank you
Implement a scripted constant to provide the value to DefaultDirName directive.
You can use JsonParser library to parse the JSON config file.
[Setup]
DefaultDirName={code:GetInstallLocation}
[Code]
#include "JsonParser.pas"
// Here go the other functions the below code needs.
// See the comments at the end of the post.
const
CP_UTF8 = 65001;
var
InstallLocation: string;
<event('InitializeSetup')>
function InitializeSetupParseConfig(): Boolean;
var
Json: string;
ConfigPath: string;
JsonParser: TJsonParser;
JsonRoot: TJsonObject;
S: TJsonString;
begin
Result := True;
ConfigPath := 'C:\path\to\config.json';
Log(Format('Reading "%s"', [ConfigPath]));
if not LoadStringFromFileInCP(ConfigPath, Json, CP_UTF8) then
begin
MsgBox(Format('Error reading "%s"', [ConfigPath]), mbError, MB_OK);
Result := False;
end
else
if not ParseJsonAndLogErrors(JsonParser, Json) then
begin
MsgBox(Format('Error parsing "%s"', [ConfigPath]), mbError, MB_OK);
Result := False;
end
else
begin
JsonRoot := GetJsonRoot(JsonParser.Output);
if not FindJsonString(JsonParser.Output, JsonRoot, 'InstallLocation', S) then
begin
MsgBox(Format('Cannot find InstallLocation in "%s"', [ConfigPath]),
mbError, MB_OK);
Result := False;
end
else
begin
InstallLocation := S;
Log(Format('Found InstallLocation = "%s"', [InstallLocation]));
end;
ClearJsonParser(JsonParser);
end;
end;
function GetInstallLocation(Param: string): string;
begin
Result := InstallLocation;
end;
The code uses functions from:
How to parse a JSON string in Inno Setup? (ParseJsonAndLogErrors, ClearJsonParser, GetJsonRoot, FindJsonValue and FindJsonString);
Inno Setup - Convert array of string to Unicode and back to ANSI (MultiByteToWideChar and LoadStringFromFileInCP).
I have a software which requires the default browser installed on user computer.
Is there a way that I can get it?
Thanks
An solution that correctly works on modern versions of Windows cannot be based on association with http protocol, as that's no longer reliable. It should rather be based on a solution like the answer by #GregT to How to determine the Windows default browser (at the top of the start menu).
So something like:
function GetBrowserCommand: string;
var
UserChoiceKey: string;
HtmlProgId: string;
begin
UserChoiceKey :=
'Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.html\UserChoice';
if RegQueryStringValue(HKCU, UserChoiceKey, 'ProgId', HtmlProgId) then
begin
Log(Format('ProgID to registered for .html is [%s].', [HtmlProgId]));
if RegQueryStringValue(HKCR, HtmlProgId + '\shell\open\command', '', Result) then
begin
Log(Format('Command for ProgID [%s] is [%s].', [HtmlProgId, Result]));
end;
end;
{ Fallback for old version of Windows }
if Result = '' then
begin
if RegQueryStringValue(HKCR, 'http\shell\open\command', '', Result) then
begin
Log(Format('Command registered for http: [%s].', [Result]));
end;
end;
end;
If you want to extract browser path from the command, use a code like:
function ExtractProgramPath(Command: string): string;
var
P: Integer;
begin
if Copy(Command, 1, 1) = '"' then
begin
Delete(Command, 1, 1);
P := Pos('"', Command);
end
else P := 0;
if P = 0 then
begin
P := Pos(' ', Command);
end;
Result := Copy(Command, 1, P - 1);
end;
(based on Executing UninstallString in Inno Setup)
Take this:
function GetBrowser() : String;
var
RegistryEntry: String;
Browser: String;
Limit: Integer ;
begin
if RegQueryStringValue(HKEY_CLASSES_ROOT, 'http\shell\open\command', '', RegistryEntry) then
begin
Limit := Pos('.exe' ,RegistryEntry)+ Length('.exe');
Browser := Copy(RegistryEntry, 1, Limit );
MsgBox('Your browser: ' + Browser , mbInformation, MB_OK);
end;
end;
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.
Is it possible to get an output of an Exec'ed executable?
I want to show the user an info query page, but show the default value of MAC address in the input box. Is there any other way to achieve this?
Yes, use redirection of the standard output to a file:
[Code]
function NextButtonClick(CurPage: Integer): Boolean;
var
TmpFileName, ExecStdout: string;
ResultCode: integer;
begin
if CurPage = wpWelcome then begin
TmpFileName := ExpandConstant('{tmp}') + '\ipconfig_results.txt';
Exec('cmd.exe', '/C ipconfig /ALL > "' + TmpFileName + '"', '', SW_HIDE,
ewWaitUntilTerminated, ResultCode);
if LoadStringFromFile(TmpFileName, ExecStdout) then begin
MsgBox(ExecStdout, mbInformation, MB_OK);
{ do something with contents of file... }
end;
DeleteFile(TmpFileName);
end;
Result := True;
end;
Note that there may be more than one network adapter, and consequently several MAC addresses to choose from.
I had to do the same (execute command line calls and get the result) and came up with a more general solution.
It also fixes strange bugs if quoted paths are used in the actual calls by using the /S flag for cmd.exe.
// Exec with output stored in result.
// ResultString will only be altered if True is returned.
function ExecWithResult(
Filename, Params, WorkingDir: String; ShowCmd: Integer;
Wait: TExecWait; var ResultCode: Integer; var ResultString: String): Boolean;
var
TempFilename: String;
Command: String;
ResultStringAnsi: AnsiString;
begin
TempFilename := ExpandConstant('{tmp}\~execwithresult.txt');
// Exec via cmd and redirect output to file.
// Must use special string-behavior to work.
Command :=
Format('"%s" /S /C ""%s" %s > "%s""', [
ExpandConstant('{cmd}'), Filename, Params, TempFilename]);
Result :=
Exec(ExpandConstant('{cmd}'), Command, WorkingDir, ShowCmd, Wait, ResultCode);
if not Result then
Exit;
LoadStringFromFile(TempFilename, ResultStringAnsi); // Cannot fail
// See https://stackoverflow.com/q/20912510/850848
ResultString := ResultStringAnsi;
DeleteFile(TempFilename);
// Remove new-line at the end
if (Length(ResultString) >= 2) and
(ResultString[Length(ResultString) - 1] = #13) and
(ResultString[Length(ResultString)] = #10) then
Delete(ResultString, Length(ResultString) - 1, 2);
end;
Usage:
Success :=
ExecWithResult('ipconfig', '/all', '', SW_HIDE, ewWaitUntilTerminated,
ResultCode, ExecStdout) and
(ResultCode = 0);
The result can also be loaded into a TStringList object to get all lines:
Lines := TStringList.Create;
Lines.Text := ExecStdout;
// ... some code ...
Lines.Free;