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).
Related
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.
So given this function, I get the error "Identifier Expected" on the GetRoot := ROOTPage.Values[0]; line. I expect it is telling me that the ROOTPage is not defined?
const
DefaultRoot = 'C:\IAmGRoot';
Var
ROOTPage : TInputQueryWizardPage;
procedure SetupRoot;
begin
ROOTPage := CreateInputQueryPage(wpUserInfo,
ExpandConstant('{cm:RootTitle}'),
ExpandConstant('{cm:RootInstructions}'),
ExpandConstant('{cm:RootDescription}') + ' "' + DefaultRoot + '"'
);
ROOTPage.Add(ExpandConstant('{cm:SSRoot}') + ':', False);
ROOTPage.Values[0] := ExpandConstant('{DefaultRoot}');
// add SSROOT to path
end;
function GetRoot : string;
begin
GetRoot := ROOTPage.Values[0];
end;
How should I interpret this error. What is an identifier in Pascal?
This page tells me that identifiers are variable names. Perhaps I need to expand the ROOTPage.Values[0] in some way since I am referencing an array from an Inno Setup object?
Or maybe I need to return the value differently. I saw one page on Pascal that said that you needed to avoid assigning to the function value on parameter less functions to avoid a recursive loop. Does this mean I should pass in a dummy value? or is there a different syntax? That page did not explain.
I secretly think my real issue is that I am not defining my function correctly... but well. That much compiles at least. This question could become: How do you handle a parameter-less function in Pascal?
I do not think Inno Setup is part of the issue, but I am working with Inno Setup in case that is important.
Update:
It doesn't seem to be the array because this gets the same error:
const
DefaultRoot = 'C:\IAmGRoot';
function GetRoot : string;
begin
GetRoot := DefaultRoot;
end;
Update:
This link has said that the function name can be replaced/ should be replaced with the key word Result such as the following code. I actually knew this, but the Inno Setup compiler does not recognize this as valid syntax. It then tells me my function is an invalid prototype.
function GetRoot : string;
begin
Result := DefaultRoot;
end;
Update:
If I do this I get "Invalid Prototype for GetRoot"
function GetRoot : boolean;
begin
Result := False;
end;
Update for #Martin Prikryl:
Well I use it a few places but the typical use would be like this:
[Files]
Source: "C:\ValidPath\Release\*"; DestDir: "{app}\bin"; Components: DefinedComponent
Source: "C:\ValidPath\Deployment\*"; DestDir: "{code:GetRoot}\"; Flags: ignoreversion recursesubdirs; Components: DefinedComponent
Identifier expected
Your code would be correct in a Pascal, but it does not compile in Pascal Script.
In Pascal, when you want to assign a return value of a function, you either assign the value to a "variable" with a name of the function or to Result variable.
So this is correct:
function GetRoot: string;
begin
GetRoot := ROOTPage.Values[0];
end;
And this too (both are equivalent):
function GetRoot: string;
begin
Result := ROOTPage.Values[0];
end;
In the Pascal Script, only the Result works. When you use the name of the function, you get the "Identifier expected."
Invalid prototype
You get this when the function is called from outside of the Code section and a specific parameter list/return value is required. But you didn't tell us, what you use the GetRoot function for.
There are two places, where you can use a custom function in Inno Setup:
Check parameter: For this the function must return a Boolean and take either no parameter or one parameter (the parameter type is determined by a value you provide in the Check parameter).
function MyProgCheck(): Boolean;
function MyDirCheck(DirName: String): Boolean;
Scripted Constants: The function must return a string and take one string parameter, even if no parameter is provided in the scripted constant. I assume this is your use case. If you do not need any parameter, just declare it, but do not use it:
function GetRoot(Param: String): string;
begin
Result := ROOTPage.Values[0];
end;
The title doesn't quite capture the essence of the issue.
I have a UDF function that returns a PChar.
function AccountDescription(sAccountId: PChar) : PChar; stdcall;
This was working fine but I realized I wanted to return #N/A if the accountId was not found.
I discovered CVErr(xlErrNA) and changed the Signature to return OleVariant.
But now I am receiving [Error] Incompatible types: 'OleVariant' and 'PAnsiChar'.
I could not find any information on how to resolve this so I figure my understanding of the problem must not be correct.
I tried just passing a string which compiled but produced a runtime error of "Invalid variant type".
The full code is:
function AccountDescription(sAccountId: PChar): OleVariant; stdcall;
var
strResult: string;
strPChar : PChar;
begin
try
strResult:= repo.GetAccount(sAccountId).Description;
strPChar := strAlloc(length(strResult)+1) ;
StrPCopy(strPChar, strResult) ;
Result := strPChar;
except
Result := CVErr(xlErrNA);
end;
end;
Note: Is excel responsible for destroying the string or is that my cleanup? Should I be creating a copy or should I just be returning a pointer to an existing string. After typing it I feel like I should be returning a pointer.
Update:
Removed some irrelevant code in the example.
Now using:
function AccountDescription(sAccountId: PChar): OleVariant; stdcall;
var
strResult: string;
begin
try
Result := PChar(repo.GetAccount(sAccountId).Description);
except
Result := CVErr(xlErrNA);
end;
end;
You do not need the PChar cast, you can assign a String directly to an OleVariant (it will be converted by the RTL into a BSTR that the receiver will then free when done using it):
Result := repo.GetAccount(sAccountId).Description;
As for reporting an error, do you have a viable CVErr() function in your Delphi code? In VB, CVErr() returns a Variant of type Error (varError in Delphi) containing an error code (xlErrNA is 2042). Delphi has a VarAsError() function for that same purpose:
Result := VarAsError(2042);
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;
When I compile the code below, it completes without errors, but when I attempt to run the setup file I get a type mismatch error. Can anyone tell me what might be causing it? (exact error message is "Runtime Error (at 1:66): Type Mismatch.")
[Setup]
DefaultDirName={code:AppDir}\MyApp
[Code]
function AppDir(Param: String): String;
var
Check: Integer;
begin
Check := GetWindowsVersion();
if Check = 6.0 then
Result := ExpandConstant('{userdocs}')
else
Result := ExpandConstant('{pf}');
end;
Quoting from the Inno Setup documentation for GetWindowsVersion():
Returns the version number of Windows packed into a single integer. The upper 8 bits specify the major version; the following 8 bits specify the minor version; the lower 16 bits specify the build number. For example, this function will return $05000893 on Windows 2000, which is version 5.0.2195.
You can't compare with a floating point value, you need to extract the parts of the version number, like so:
function AppDir(Param: String): String;
var
Ver: Cardinal;
VerMajor, VerMinor, BuildNum: Cardinal;
begin
Ver := GetWindowsVersion();
VerMajor := Ver shr 24;
VerMinor := (Ver shr 16) and $FF;
BuildNum := Ver and $FFFF;
if VerMajor >= 6 then
Result := ExpandConstant('{userdocs}')
else
Result := ExpandConstant('{pf}');
end;
Note that you should never check VerMajor for equality, as this would fail for either lower or higher Windows versions. Always use <= or >= instead.