Skip files during installation depending on text file command line argument - inno-setup

My scenario is currently that I'd like the possibility for users to skip installation of certain files that a user might have specified via a command line argument at install time.
The idea would be for the user to specify a text file and ideally during installation, the installer would check if the file currently being copied is listed in the supplied text file and decide depending on that.
My [Files] section atm is referencing full directories: would the best approach be to list all files individually and make use of the Check parameter or is there a different approach available? There seems to be no event function that would lend itself for this task.
Thanks
Markus

No need to list all files individually. The Check function is called for each file individually, even when the Source is a wildcard.
Use the CurrentFilename function to tell what file is being processed at the moment.
Note that the Check function is called multiple times for each file.
[Files]
Source: "C:\path\*.*"; DestDir: "{app}"; Check: AllowFile
[Code]
var
Whitelist: TStringList;
function AllowFile: boolean;
var
FileName: string;
begin
FileName := ExtractFileName(ExpandConstant(CurrentFileName));
Result := (Whitelist = nil) or (Whitelist.IndexOf(Uppercase(FileName)) >= 0);
if Result then
Log(Format('Allowing "%s"', [FileName]))
else
Log(Format('Skipping "%s"', [FileName]));
end;
function InitializeSetup(): Boolean;
var
WhitelistFile: string;
Lines: TArrayOfString;
I: Integer;
begin
WhitelistFile := ExpandConstant('{param:Whitelist}');
if WhitelistFile = '' then
begin
Log('No whitelist specified, allowing all files');
end
else
begin
Whitelist := TStringList.Create;
Whitelist.Sorted := True;
LoadStringsFromFile(WhitelistFile, Lines);
for I := 0 to GetArrayLength(Lines) - 1 do
Whitelist.Add(Uppercase(Lines[I]));
Log(Format('Loaded %d entries to whitelist from "%s"', [
Whitelist.Count, WhitelistFile]));
end;
Result := True;
end;

Related

Disallow empty and spaces-only input on Inno Setup custom page

Is there a way to disallow user input if it contains spaces only?
I already tried this solution:
Inno Setup - Create User Input Query Page with input length and format limit and use the input
But, I don't want that solution because it disable -space- completely.
E.g. if input in text field is "my name" it will return error because -space- is not allowed.
Use the same code as in:
Inno Setup - Create User Input Query Page with input length and format limit and use the input
Just use this implementation of ValidateInput:
function ValidateInput(Sender: TWizardPage): Boolean;
begin
Result := True;
if Trim(Page.Values[0]) = '' then
begin
MsgBox('Input cannot be empty.', mbError, MB_OK);
Result := False;
end;
end;
The Trim function is the key.

DBMS_XSLPROCESSOR.CLOB2FILE to subdirectory

I have a pretty simple piece of code, that just writes object source to the server.
DECLARE
data CLOB;
objType varchar2(30) := 'PACKAGE_SPEC';
objSchema varchar2(30) := 'TEST';
objName varchar2(30) := 'TEST';
fname varchar2(256) := objName || '.sql';
BEGIN
SELECT dbms_metadata.get_ddl(objType,objName,objSchema) into data from dual;
dbms_output.put_line(fname);
DBMS_XSLPROCESSOR.CLOB2FILE(data,'SVN',fname);
END;
/
SVN is a directory
create or replace directory svn as '/tmp/svn'
I was wondering if I could write into subdirectory of svn, but that doesn't seem to work
e.g.
DECLARE
data CLOB;
objType varchar2(30) := 'PACKAGE_SPEC';
objSchema varchar2(30) := 'TEST';
objName varchar2(30) := 'TEST';
fname varchar2(256) := objType || '/'|| objName || '.sql';
BEGIN
SELECT dbms_metadata.get_ddl(objType,objName,objSchema) into data from dual;
dbms_output.put_line(fname);
DBMS_XSLPROCESSOR.CLOB2FILE(data,'SVN',fname);
END;
/
just ignores the part before "/".
Is there a way to write into a subdirectory or do I have to create a (oracle) directory for every path?
In lieu of any definitive information, it seems reasonable that dbms_xlsprocessor either uses utl_file or the same underlying mechanisms. The documentation for utl_file has an example that says "subdirectories are not accessible", but more usefully fopen() states:
If a directory path is given as a part of the filename, it is ignored by FOPEN.
Which matches the behaviour you're seeing; you don't get an error, the file is just created in the directory pointed to by the directory object, and the subdirectory part you supplied is being ignored.
So yes, you will need to create an Oracle directory object for every path you want to write to, and provide that directory object's name to clob2file.

Inno Setup Inputbox for serialcode with output into the registry

I used the code from this Answer from the Member TLama. I think it's exactly what i need, but I have two problems with it:
I need the Serial from the edit boxes in the registry. This is what i tried:
Root: "HKCU"; Subkey: "Software\myProg"; ValueType: string; ValueName: "Serial"; ValueData: "{code:GetSerialNumber}"; Flags: deletevalue uninsdeletevalue
but Inno gives me an error. TLama wrote in his answer (from the Link above), it's enought to call the GetSerialNumber part, but I do sth. wrong...
The other question: Is it possible to prefill the serialbox with an example code? E.g. 12345 or abcde? I'm using only one input box with 10 chars...
Hope someone can help, and sorry for my bad english ;)
You can use UserInfoPage and then {userinfoserial} but if you want to use TLama's solution then you should slightly change NextButtonClick function:
function NextButtonClick(CurPageID: Integer): Boolean;
var
S: string;
I: Integer;
begin
Result := True;
if CurPageID = SerialPage.ID then
begin
S := '';
for I := 0 to High(SerialEdits) do
S := S + SerialEdits[I].Text + '-';
SetLength(S, Length(S)-1);
RegWriteStringValue(HKEY_CURRENT_USER, 'Software\myProg',
'Serial', S);
end;
end;
Var SerialEdits: array of TEdit; has to be set as Global for the script. You also may want to add key to registry later (e.g. with CurStepChanged when ssDone or something) or write your own function that will pass Serial to Result as String and then call it in Registry Section.

How to save a string to a .txt file in Delphi?

I need to make a program that generates a password that is saved in a text file format in a specific destination I set and the user needs to open the .txt to get the password to 'unlock' another program.
I have already got the code to generate the password in the string sPass and now I need to use the SaveToFile function to save it into the text file I created called Password.txt but I cannot find the general form to use the SaveTo File Function in Delphi and I do not know where to put the sPass and Password.txt in the function.
It should be something like : SaveToFile(...) but I do not know how to save sPass in Password.txt
Edit :
Just one more question, how do you delete what is previously stored in Password.txt before you add the string to it so that Password.txt is blank before the string is added ? Thanks
The Modern Modern way is to use TFile.WriteAllText in IOUtils (Delphi 2010 and up)
procedure WriteAllText(const Path: string; const Contents: string);
overload; static;
Creates a new file, writes the specified string to the file, and then
closes the file. If the target file already exists, it is overwritten.
The modern way is to create a stringlist and save that to file.
procedure MakeAStringlistAndSaveThat;
var
MyText: TStringlist;
begin
MyText:= TStringlist.create;
try
MyText.Add('line 1');
MyText.Add('line 2');
MyText.SaveToFile('c:\folder\filename.txt');
finally
MyText.Free
end; {try}
end;
Note that Delphi already has a related class that does everything you want: TInifile.
It stores values and keys in a key = 'value' format.
passwordlist:= TInifile.Create;
try
passwordlist.LoadFromFile('c:\folder\passwords.txt');
//Add or replace a password for `user1`
passwordlist.WriteString('sectionname','user1','topsecretpassword');
passwordlist.SaveToFile('c:\folder\passwords.txt');
finally
passwordlist.Free;
end; {try}
Warning
Note that saving unecrypted passwords in a textfile is a security-leak. It's better to hash your passwords using a hashfunction, see: Password encryption in Delphi
For tips on how to save passwords in a secure way.
You can use the TFileStream class to save a string to a file:
uses
Classes;
procedure StrToFile(const FileName, SourceString : string);
var
Stream : TFileStream;
begin
Stream:= TFileStream.Create(FileName, fmCreate);
try
Stream.WriteBuffer(Pointer(SourceString)^, Length(SourceString));
finally
Stream.Free;
end;
end;
and to read
function FileToStr(const FileName : string):string;
var
Stream : TFileStream;
begin
Stream:= TFileStream.Create(FileName, fmOpenRead);
try
SetLength(Result, Stream.Size);
Stream.Position:=0;
Stream.ReadBuffer(Pointer(Result)^, Stream.Size);
finally
Stream.Free;
end;
end;
Fastest and simplest way, no need to declare any variables:
with TStringList.Create do
try
Add(SomeString);
SaveToFile('c:\1.txt');
finally
Free;
end;

How to change defaultdirname parameter just before Install in Inno Setup?

I want to change defaultdirname parameter in ssInstall part. How can I do that? Is there a function for setting [Setup] parameters.
The following global objects are available :
MainForm of type TMainForm, WizardForm of type TWizardForm and UninstallProgressForm of type TUninstallProgressForm and one special constant: crHand of type TControl.Cursor.
If you want to set the default directory in the wizard, just access to it's componants like you would in normal delphi code.
For example, set the directory to a custom value:
WizardForm.DirEdit.Text := 'c:\test';
to read that value you can use the WizardDirValue function.
I say 'just access'... but it took me an hour to figure out ;)
There seems to be no way to change a script constant via scripting.
I think your best bet is to modify the target directory for each entry in the [Files] section, e.g.
[Files]
Source: "MYPROG.EXE"; DestDir: "{code:NewTargetDir}"
and derive your new installation directory like this:
[Code]
function NewTargetDir(Param: String): String;
begin
Result := ExpandConstant('{app}') + '\MySubDir';
end;
Since the NewTargetDir function will be called just before the file is actually copied, this should work.
However, I think you should reconsider your approach. First asking the user to specify a directory to installinto, and then actually installing into a different directory, which seems to be your intent, is the wrong way, IMO. Do you really have a compelling reason to install into another directory than the one specified by the user? Besides, the result of my example code could just as well be achieved by specifying
[Files]
Source: "MYPROG.EXE"; DestDir: "{app}\MySubDir"
without any scripting needed. When in doubt, go for the simpler solution.
I have a similar situation, where the setup app is receiving the install path from the command line.
I'm using the solution proposed by Jonx:
WizardForm.DirEdit.Text := 'c:\test';
Example:
function CompareParameter(param, expected: String): Boolean;
begin
Result := False;
if Length(param) >= Length(expected) then
begin
if CompareText(Copy(param, 1, Length(expected)), expected) = 0 then
begin
Result := True;
end;
end;
end;
function GetParameter(expectedParam: String): String;
var
i : LongInt;
begin
Result := '';
for i := 0 to ParamCount() do
begin
if CompareParameter(ParamStr(i), '/' + expectedParam + '=') then
begin
Result := Copy(ParamStr(i), Length(expectedParam) + 3, Length(ParamStr(i)));
break;
end;
end;
end;
procedure InitializeWizard();
var
newInstallFolder: String;
begin
newInstallFolder := GetParameter('INSTALL_FOLDER');
if Length(newInstallFolder) > 2 then
begin
if Copy(newInstallFolder, 1, 1) = '"' then
newInstallFolder := Copy(newInstallFolder, 2, Length(newInstallFolder) - 2)
if Length(newInstallFolder) > 1 then
WizardForm.DirEdit.Text := newInstallFolder;
end;
end;
The setup app is being started from another setup, in silent mode. It worked OK for me.

Resources