Inno Setup loop from A to Z - inno-setup

I am trying to write a function that will check for the existence of a particular directory on each drive in turn, store the path where it is found as a variable and return true if it exists and false otherwise:
function IsDirPresent(): Boolean;
var
chrDriveLetter: Char;
strMyDir: String;
begin
for chrDriveLetter := 'A' to 'Z' do
begin
if DirExists(chrDriveLetter + ':\MyDir') then
strMyDirPath := chrDriveLetter + ':\MyDir'
end;
if strMyDirPath <> '' then
Result := True;
end;
I get a Type mismatch error on my loop line when compiling this. I am struggling to work out what is wrong with the loop and haven't been able to find any information on how to correctly create a loop from A to Z in Inno Setup.

Your loop is valid for Pascal language because the Char type is ordinal and its range constants are of the same type, so I guess this is a specific limitation of the Pascal Script language notation. I don't know if there's some syntax workaround nor if the Char type is even allowed to be used for the loop control variable.
I can only provide you a workaround working with integer type loop control variable. It uses the Ord and Chr functions. The first returns the ordinal value of a char, the latter ASCII char from the ordinal value:
var
C: Char;
I: Integer;
begin
for I := Ord('A') to Ord('Z') do
begin
C := Chr(I);
...
end;
end;

Related

Pascal Script fails to retrieve wide string from a custom DLL

I need Inno Setup to load a wide string from a function in a custom DLL. I have no problem loading Ansi strings – this, for example, works fine:
int __stdcall GetStringA(char *lpText)
{
StringCchCopyA(lpText, 30, "New Ansi value");
return strlen(lpText);
}
Pascal Script
function GetStringA(lpText: AnsiString): Integer;
external 'GetStringA#files:MyDll.dll';
function LoadStringA(): AnsiString;
var
Str: AnsiString;
Length: Integer;
begin
Str := 'Initial Ansi value';
SetLength(Str, 30);
Length := GetStringA(Str);
SetLength(Str, Length);
Result := Str; // Str == 'New Ansi value'
end;
Doing the same thing with wide strings fails, however:
int __stdcall GetStringW(wchar_t *lpText)
{
StringCchCopyW(lpText, 30, L"New Wide value");
return wcslen(lpText);
}
Pascal Script
function GetStringW(lpText: WideString): Integer;
external 'GetStringW#files:MyDll.dll';
function LoadStringW(): WideString;
var
Str: WideString;
Length: Integer;
begin
Str := 'Initial Wide value';
SetLength(Str, 30);
Length := GetStringW(Str); // ** DOES NOT ALTER 'Str' **
SetLength(Str, Length);
Result := Str; // Str == 'Initial Wide v' (old string, new length)
end;
Sending a WideString to the DLL works fine. Changing the buffer contents in the DLL also works fine…but somehow that does not percolate back to my Pascal Script variable.
I suspect that some sort of marshaling goes on under the hood, so that the DLL actually manipulates a copy of my variable. Any insights or workarounds appreciated!
I'm using Inno Setup 6.2.0.
I made it work by replacing WideString with String:
function GetStringW(lpText: String): Integer;
external 'GetStringW#files:MyDll.dll';
function LoadStringW(): WideString;
var
Str: String;
Length: Integer;
begin
Str := 'Initial Wide value';
SetLength(Str, 30);
Length := GetStringW(Str); // ** ALTERS 'Str' **
SetLength(Str, Length);
Result := Str; // Str == 'New Wide value'
end;
I'd have thought WideString and String were equivalent, but apparently not quite. It is necessary to change both the function prototype and the Str declaration. It is not necessary to change the LoadStringW return type, though you may – and the returned value can in either case be assigned to a variable of type WideString or String without problem.
So: Problem solved. I'm still curious about the underlying behavior, though.

'' Is Not a Valid Identifier (Delphi XE5)

I'm new to Delphi XE5 (part of Embarcadero's RAD Studio), and using the StrToFloat function in a VCL Forms project designed to build a simple calculator.
The code to display the text to the user works well, but I'm having trouble extracting it in a way that will allow the calculator to actually perform calculations. The means I've settled on is displaying the code to the user in a Tedit, and storing the code to be executed in a Tmemo. Pressing the buttons on the calculator stores the numbers to both, and pressing an operator (the symbols for add, subtract, exponent, multiply, and divide) starts a new line in the memo. This way, each number is on its own line, and each operator is on its own line.
I then want to extract the numbers with respect to the operators, by iterating through the lines of the memo. For 3 ^ 4, you would see...
3
^
4
...in the TMemo.
I want to find the ^ sign at line index 1 (lines start at index 0, right?), and store 3 in a base variable by the logic that, if ^ is lines[1], then 3 is lines[1-1], or lines[0], and store 4 in an exponent variable. Both variables would be of type extended. This is the code I've created to do this...
procedure TForm1.btnEqualsClick(Sender: TObject);
var
i: Integer;
base: extended;
exponent: extended;
result: extended;
begin
{This For loop looks for the exponentiation operator (^) in memCurrentEntry. If
it's found, it then solves the equation.}
for i := Low(memCurrentEntry.Lines.Text.Length) to High(memCurrentEntry.Lines.Text.Length) do
if AnsiContainsStr(memCurrentEntry.Lines.Text, '^') then
begin
base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);
exponent := StrToFloat(memCurrentEntry.Lines[ansiPos('^', memCurrentEntry.Lines.Text)+2]);
result := power(base, exponent);
end;
When I run the calculator, the numbers appear correctly, but when I hit the equals button to run the code above, I get the message Project Calculator_u.exe raised exception class EConvertError with Message ''' is not a valid floating point value'.
I know the StrToFloat function is trying to convert a null character or something (How do I identify what code is generating " '' is not a valid floating point value" error dialogue box), but what should I do to correct this?
Below is the code for each button -- the digit (0, 1, 2, 3, etc.) changes, but the rest is the same for each one...
procedure TForm1.btn0Click(Sender: TObject);
begin
//Adds the digit 0 to the end of the current number in memCurrentEntry
memCurrentEntry.SelStart := Length(memCurrentEntry.Text);
memCurrentEntry.Lines.Text := Copy(memCurrentEntry.Lines.Text,1,
memCurrentEntry.SelStart)+'0'+ Copy(memCurrentEntry.Lines.Text,
memCurrentEntry.SelStart+1,Length(memCurrentEntry.Lines.Text)
- memCurrentEntry.SelStart);
//Adds the digit 0 to the end of the current number in edtCurrentEntry
edtcurrententry.Text := edtcurrententry.Text + '0';
end;
I can post the full unit file upon request.
Use TryStrToFloat. This returns a boolean indicating whether or not the conversion succeeded.
var
str: string;
val: Double;
....
if TryStrToFloat(str, val) then
// val contains the floating point value represented by str
else
// str does not contain a floating point value, do something else with it
Of course, you could use StrToFloat, and catch the exception. But since you expect to encounter the exception, it will result in much cleaner code if you use TryStrToFloat.
the usage of lines on the memo is wrong:
base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);
the lines.text returns the ENTIRE text, which is not what you want. Instead use some LOCAL variables to hold your lookup values...that way you can set a breakpoint and see whats wrong and use the following method to locate the "^"
result := 0;
carrotLine := memCurrentEntry.Lines.IndexOf('^');
if carrotline > 0 then // must be 1 or greater, so a number can be at the 0 position
begin
base := TryStrToFloat(MemCurrentEntry.Lines[carrotLine-1],0);
exponent := 0;
// only try to read the next value if there is one.
if memCurrentEntry.Lines.Count >= carrotLine then
exponent := TryStrToFloat(MemCurrentEntry.Lines[carrotline+1],0);
result := power(base,exponent);
end;
Note that this code ONLY handles a single ^ in the memo (your code had the same issue), to go beyond this is to use a loop and possibly a state engine or some other parsing logic to handle computations.
Use StrToFloat is not wrong. If the conversion is failed, user should see an error message. If you want to suppress the error quietly, then you should add a try/except to protect your code.
try
Value := StrToFloat(A_String);
except
on EConvertError do
Value := 0; // Fallback to a default value or execute other code logic
end;
According to your given code example, if the base and the exponent are entered in your given format (first line is the base, second line is a "^" and the third line is the exponent). I'd suggest you write your code as follows:
var
Numbers: TArray<string>;
// Other variables go here
begin
// ...
Numbers := memCurrentEntry.Lines.Text.Split('^');
if Length(Numbers) = 2 then
try
Base := StrToFloat(Numbers[0]);
Exponent := StrToFloat(Numbers[1]);
Result := Power(Base, Exponent);
except
on EConvertError do
begin
ShowMessage(Format('Unable to calculate the formula %s', [memCurrentEntry.Lines.Text]));
Result := 0; // Fallback to a default value, such as 0
end;
end;
// ...
end;

Is it possible to build a Inno Setup script line from variables

Is it possible to build a script line using a variable and some conditions (have executable that requires different switches depending on what is checked on a custom page. Possibly something like this:
var
linecommand : string;
function FileParams();
begin
if check1 then linecommand := linecommand+' conditionone' end;
if check2 then linecommand := linecommand+' conditiontwo' end;
if check3 then linecommand := linecommand+' conditiontwo' end;
end;
function check123()
begin
if check1 or check2 or check3 then
begin
linecommand='Executable file name.exe '+linecommand;
Shellexec(linecommand);
end;
end;
You can use a {code:...} constant to insert a value calculated in a [Code] function into a specific value within a standard entry that accepts both strings and constants (such as the Parameters value of a [Run] entry).
Or you can write multiple entries and use a Check function to determine which (if any) of them actually get executed.

How do I create a random string in Free Pascal?

I want to write a function GenString that returns a random string of length [0 .. 100] based on a parameterized function GenChar that users could customize.
That is, if one user wants to generate a string of all as, he could do that by passing in a function that always returns a, and another user could generate random alphabetic Hebrew words by passing in a custom GenChar function that returns random Hebrew letters.
Lemma 1: I want to write a function GenArray that generates an array of whatever type a passed-in function GenThing : sometype returns. Could this be done in Free Pascal.
Lemma 2: What I'm trying to do is port QuickCheck to Free Pascal.
Here's an example. Unicode string is used since international characters is mentioned.
//The functions
type
TGenChar = function: widechar;
function GenString(MaxLength: integer; GenChar: TGenChar): widestring;
var
i: integer;
begin
if MaxLength < 1 then
MaxLength:= 1; //minimum length is 1
setlength(result, random(MaxLength+1));
for i:= 1 to length(result) do
result[i]:= GenChar;
end;
//The usage example (remember to call randomize somewhere in your program)
var
SampleCharSet: widestring= 'ABCxyz';
function SampleGenChar: widechar;
begin
result:= SampleCharSet[random(length(SampleCharSet))+1];
end;
function GetRandomStringSample: widestring;
begin
result:= GenString(100, SampleGenChar);
end;

Inno Setup comma error

I have GetVersion function in [Code] that returns a string like this "1004", "1003", etc.
I created this function to check the registry value for lowest version numbers and uninstall them.
Here is a snippet of the code it is giving error point to StrtoInt conversion line stating
Comma (,) expected
Here is the snippet:
function DoesOldVersionsExist(): Boolean;
var
AppVersion: integer;
mstr: string;
VersionInstalled: cardinal;
begin
AppVersion := StrToInt(GetVersion({#MyAppVersion}), 0);
...
after that line I'm simple comparing the values and return true or false. Much Appreciated.
This is what error message says:
Line 55
Column 40.
Comma (,) expected
Thanks Deanna but unfortunately that is the error message pointing to this :
AppVersion := StrToInt(GetVersion({#MyAppVersion}), 0);
^
Here is the GetVersion function:
function GetVersion(AppVersion: String): String;
var
Version: String;
CharIndex: integer;
c: char;
begin
for CharIndex := 1 to Length(AppVersion) do begin
c := AppVersion[CharIndex];
if (c <> '.') then
Version := Version + c;
end;
Result := Version;
end;
I think that you can't just have Inno Setup constants in code like this, you have to use ExpandConstant():
AppVersion := StrToInt(GetVersion(ExpandConstant('{#MyAppVersion}')), 0);
You have not given us enough information to give a definite answer, but I think that the situation is as follows.
You have defined some constant called MyAppVersion which you let the ISPP (the Inno Setup pre-processor) substitute. Now, you have not told us what type this variable is, and you have not told us what the signature of GetVersion is (in particular, what type of argument does it expect?). However, if these types are strings, you need to write
StrToInt(GetVersion('{#MyAppVersion}'), 0);
in order to obtain, say,
StrToInt(GetVersion('Some string, this is!'), 0);
instead of
StrToInt(GetVersion(Some string, this is!), 0);
which is malformed (indeed, to such an extent that it hurts my eyes to look at it).

Resources