How would I hide inno Ready MemoType? - inno-setup

How can I hide `ReadyMemoType, but not hide or disable the whole ready page? I can't find anything about it in the docs. On the ready to install page, the part that says Setup Type: Custom. That is the part I would like to hide. The text is in Default.isl as "ReadyMemoType", but I don't want to edit what it says. I just want to hide or disable it.
On the inno newsgroup, it was said:
"In [Code], the event function UpdateReadyMemo is called with the
individual text fragments. Simply append these together (with
appropriate newlines) in whatever order you wish, omitting whatever
you wish, and adding any extra information that you wish."
I have no idea what that means.

You can build the content of the ReadyMemo from the UpdateReadyMemo event method, into which are passed parameters with the information about the stuff to be installed. That includes also the setup type, which is passed as the MemoTypeInfo parameter, so to exclude this information from the memo you can concatenate a string without this parameter, for example:
[Code]
function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: string): string;
begin
// the MemoTypeInfo parameter value is not included in this string
Result :=
MemoUserInfoInfo + NewLine +
MemoDirInfo + NewLine +
MemoComponentsInfo + NewLine +
MemoGroupInfo + NewLine +
MemoTasksInfo;
end;

Related

Array variables and dynamic access in [Code] section

My installer has Components which come associated with downloadable files. These things are changing from build to build, so I'm using #insert to create the [Components] section as well as the appropriate entries in the [Files] section.
Some of these components rely on common downloadable files.
To now include the correct urls in the downloads page, I'm currently defining array variables that are named like the component and have as values the names of the required downloadable files, for example:
#dim myfeature[2] {"01aed27862e2087bd117e9b677a8685aebb0be09744723b4a948ba78d6011bac", "677756ac5969f814fd01ae677dbb832ab2e642513fea44ea0a529430d5ec1fdc"}
In the code for the download page I'm checking which components where selected via WizardSelectedComponents() and after converting the string to an array of strings, I'm trying to get to the previously defined variable and that is where I'm failing:
function GetDownloads(): Array of String;
var
Downloads: Array of String;
SelectedComponents: String;
SelectedArray: Array of String;
begin
SelectedComponents := WizardSelectedComponents(False);
// a custom procedure to parse the comma seperated string
SelectedArray := ParseArray(SelectedComponents, SelectedArray);
// trying to get to the constant array now this works:
MsgBox(ExpandConstant('{#myfeature[0]}'), mbInformation, MB_OK);
// same but trying to use the selected component value returns this as a literal
// '+SelectedArray[0]+' instead the expanded value
MsgBox(ExpandConstant('{#' + SelectedArray[0] + '[0]}'), mbInformation, MB_OK);
end;
So I understand something is up with the # mark but I could not find a way to solve this properly.
Thank you!
Markus
ExpandConstant expands Inno Setup "constants", not preprocessor values. See also Evaluate preprocessor macro on run time in Inno Setup Pascal Script.
You cannot access elements of a preprocessor compile-time array using run-time indexes.
If you know C/C++, it's like if you were trying to do:
#define VALUE1 123
#define VALUE2 456
int index = 1;
int value = VALUE ## index
I'm not really sure I completely understand what are you doing. But it seems that you need to create an array on compile time from various sources and use it on runtime.
There are several approaches that can be used for that. But you definitely need runtime array initialized on run time. But the code that initializes it can be generated on compile time.
An example of the approach follows (and some links to other approaches are at the end).
At the beginning of your script, define these support functions:
[Code]
var
FeatureDownloads: TStrings;
function AddFeature(
Feature: Integer; CommaSeparatedListOfDownloads: string): Boolean;
begin
if not Assigned(FeatureDownloads) then
begin
FeatureDownloads := TStringList.Create();
end;
while FeatureDownloads.Count <= Feature do
FeatureDownloads.Add('');
if FeatureDownloads[Feature] <> '' then
RaiseException('Downloads for feature already defined');
FeatureDownloads[Feature] := CommaSeparatedListOfDownloads;
Result := True;
end;
#define AddFeature(Feature, CommaSeparatedListOfDownloads) \
"<event('InitializeSetup')>" + NewLine + \
"function InitializeSetupFeature" + Str(Feature) + "(): Boolean;" + NewLine + \
"begin" + NewLine + \
" Result := AddFeature(" + Str(Feature) + ", '" + CommaSeparatedListOfDownloads + "');" + NewLine + \
"end;"
In your components include files, do:
#emit AddFeature(2, "01aed27862e2087bd117e9b677a8685aebb0be09744723b4a948ba78d6011bac,677756ac5969f814fd01ae677dbb832ab2e642513fea44ea0a529430d5ec1fdc")
If you add:
#expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")
to the end of your main script, you will see in the Preprocessed.iss generated by the preprocessor/compiler that the #emit directive expands to:
<event('InitializeSetup')>
function InitializeSetupFeature2(): Boolean;
begin
Result := AddFeature(2, '01aed27862e2087bd117e9b677a8685aebb0be09744723b4a948ba78d6011bac,677756ac5969f814fd01ae677dbb832ab2e642513fea44ea0a529430d5ec1fdc');
end;
Now you have FeatureDownloads Pascal Script runtime variable that you can access using FeatureDownloads[SelectedArray[0]] to get comma-separated string, which you can parse to the individual downloads.
This can be optimimized/improved a lot, but I do not know/understand the extent of your task. But I believe that once you grasp the concept (it might be difficult at the beginning), you will be able to do it yourself.
Another similar questions:
Evaluate a collection of data from preprocessor on run time in Inno Setup Pascal Script (simple example that be easier to grasp initially)
Scripting capabilities in the Registry section (slightly different approach from times event attributes were not available yet – and that's YOUR question)

Generating application arguments for Run section in scripted constant based on state of checkboxes and radio buttons on Finished page of Inno Setup

I want to modify the parameters of a FileName in the [Run] section according to the state of some radio and check buttons. In my code section I have added:
function GetParameterString:String;
var
I: Integer;
s1, s2: String;
begin
for I := 0 to WizardForm.RunList.Items.Count - 1 do
if (wizardform.runlist.items.checked[i] = true) then begin
if (wizardform.runlist.items.itemcaption[i] = 'View Whats''s New in ' + {#MyAppVersion}) then
s1 := GetShellFolderByCSIDL(CSIDL_APPDATA, True) + '\Positron Studio\What''s New.pdf';
if (wizardform.runlist.items.itemcaption[i] = 'Positron Studio Dark' then
s2 := '-d'
else if (wizardform.runlist.Items.itemcaption[i] = 'Positron Studio Light' then
s2 := '-l'
end;
end;
Result := s1 + s2
end;
and I am calling it from the [Run] section like this:
FileName:"{app}\{#MyAppExename}"; parameters: Code: GetParameterString; \
Flags: postinstall nowait
It fails on the wizardform.runlist.items.checked[i] = true with:
Unknown Identifier Checked
How do I get the Checked value of a checkbox or radiobutton?
Lookup Pascal Scripting: Scripted Constants where it states:
The Pascal script can contain several functions which are called when Setup wants to know the value of a scripted {code:...} constant. The called function must have 1 String parameter named Param, and must return a String or a Boolean value depending on where the constant is used.
So try:
FileName: "{app}\{#MyAppExename}"; Parameters: {code:GetParameterString}; Flags: postinstall nowait
The above has not been tested. It may not fix the underlaying issue with what you are actually doing in the GetParameterString method.
Your immediate problem is that there's indeed no wizardform.runlist.items.checked (nor wizardform.runlist.items.itemcaption).
You want WizardForm.RunList.Checked (and WizardForm.RunList.ItemCaption).
See the TNewCheckListBox documentation.
Your next problem will be the invalid syntax of the reference to your scripted constant in the Parameters parameter (as Andrew has already shown in his answer). It should be:
Parameters: {code:GetParameterString};
Your next problem will be "Invalid prototype":
"Identifier Expected" or "Invalid Prototype" when implementing a scripted constant in Inno Setup
It should be:
function GetParameterString(Param: string): string;
Depending on how you application handles the argument, you also might need to the path to the .pdf, as there are spaces in it.
It also bit strange, how you add the -d and -l to the path. Did you really intend to pass something like this to the application?
C:\Users\user\AppData\Roaming\Positron Studio\What's New.pdf-d
It also bit unclear to me, how are you combining multiple "run list" entries states into arguments of one particular entry. But I assume you know what you are doing.

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.

Lazarus: StringReplace ineffective when working with files (unicode issue)

I'm using Lazarus to build a simple app that builds Outlook signatures based on a template. The idea is to extract the template (a ZIP file), and replace variables within the files it contains.
For example, I may want to replace {fullname} with the name provided by the user.
I am currently using the implementation below, but it seems to be ineffective. The file is read and written to, but it appears the replacements are not being made. I have tested to see if my implementation of TFileStream is not correct, but using WriteAnsiString to append dummy text onto the end of the output file works.
Please would you kindly have a look at my code below and let me know what I may have done wrong, or if there are any better alternatives to StringReplace? I am aware that one can use TStringList - however, doing so breaks line endings. As memos and rich edits use TStringList, using those won't help either.
Update:
I have seen this, but using AnsiString makes no difference. If I'm not mistaken, FPC uses it by default anyway, instead of UnicodeString.
Update 2:
Indeed, AnsiString is the default. Using a unicode string (which makes the replacements work) adds ? to the beginning and end of the file. Why would it do that?
function multiStringReplace(const s: string; search, replace : array of string; flags : tReplaceFlags): string;
var c : cardinal;
begin
assert(length(search) = length(replace), 'Array lengths differ.');
result := s;
for c := low(search) to high(search) do
result := stringReplace(result, search[c], replace[c], flags);
end;
procedure fileReplaceString(const fileName: string; search, replace: array of string);
var
fs: tFileStream;
s: string;
begin
fs := tFileStream.create(fileName, fmOpenRead or fmShareDenyNone);
try
setLength(s, fs.size);
fs.readBuffer(s[1], fs.size);
finally
fs.free();
end;
s := multiStringReplace(s, search, replace, [rfReplaceAll, rfIgnoreCase]);
fs := tFileStream.create(fileName, fmOpenWrite);
try
fs.writeBuffer(s[1], length(s));
finally
fs.free();
end;
end;
Usage:
fileReplaceString(currentFile, ['{fullname}'], ['Full Name']);
Thanks to Abelisto's comment above, it appears the issue is due to the fact that Outlook saves the three files it creates with different encodings. To get around it, I simply used convertEncoding and guessEncoding from lconvencoding, as below:
uses
lconvencoding;
// Read string
s := convertEncoding(
multiStringReplace(s, search, replace, [rfReplaceAll, rfIgnoreCase]),
guessEncoding(s), encodingAnsi
);
// Write modified and converted string back to file
encodingAnsi appears to be the best conversion, at least in my case. Converting to UTF8 (with or without BOM) caused a bit of a headache with certain characters, specifically EmDash or EnDash.

Extra character added using {#emit SetupSetting("AppId")} in Inno Setup

I'm having problems using the following code, it's adding an extra "{".
For example:
[Setup]
AppID={{E643099E-1ECE-474F-B043-1E7A7CE405AA}
[Code]
const
INSTALL_KEY = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1';
Returns the following:
INSTALL_KEY = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{{E643099E-1ECE-474F-B043-1E7A7CE405AA}_is1';
Which obviously won't work detecting that key with RegKeyExists(HKLM, INSTALL_KEY) because of the extra "{" that isn't in the real path and the script won't compile if you remove the extra character in [Setup] because then it thinks it's a constant.
The issue looks to be resolved on later versions of Inno Setup (5.5.5). Following code works just fine:
[Setup]
AppId={{********-****-****-****-********}
...
[code]
sAppId := ExpandConstant('{#emit SetupSetting("AppId")}_is1');
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\') + sAppId;
I think that parser doesn't allow to use the custom constants enclosed by the {} characters, because they are used as introductors for the constants, so I think you will have to workaround it. Here is one of the options:
[Setup]
AppID=E643099E-1ECE-474F-B043-1E7A7CE405AA
[code]
const
INSTALL_KEY = '...\Uninstall\{{#emit SetupSetting("AppId")}}_is1';
My guess is that case you've described is just an unexpected bug, because the compiler prompts you to use double bracket in the beginning of your constant though but when you emit such constant you get it with the same double bracket back.
Use StringChange() to remove the extra {:
[Setup]
AppID={{E643099E-1ECE-474F-B043-1E7A7CE405AA}
[Code]
const INSTALL_KEY = '...\Uninstall\{#emit StringChange(SetupSetting("AppId"),"{{","{")}_is1';
The solution is to use ExpandConstant function as already mentioned by others.
[Setup]
AppId={{E643099E-1ECE-474F-B043-1E7A7CE405AA}
[Code]
var
INSTALL_KEY: String;
function InitializeSetup(): Boolean;
begin
INSTALL_KEY := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#SetupSetting("AppId")}_is1');
MsgBox(INSTALL_KEY, mbInformation, MB_OK);
end;
This is the expected behavior, not a bug. In [Code] section, constants and scripted constants are not expanded while emit preprocessor directives {#emit ...}/{#...} are replaced with their values by ISPP. So you need to use ExpandConstant function to convert {{ to {.
In this case, you can't define INSTALL_KEY as a Pascal constant as ExpandConstant function is used.
You could also define your application ID as a constant and use it like this:
#define MyAppID "{{E643099E-1ECE-474F-B043-1E7A7CE405AA}"
[Setup]
AppId={#MyAppID}
[Code]
const INSTALL_KEY = '...\Uninstall\' + ExpandConstant('{#MyAppID}') + '_is1';

Resources