Using constants and code in procedure calls (inno setup) - inno-setup

I tried calling a procedure from the AfterInstall method in Inno setup, but it gives me a syntax error. The section {code:GetShortName|{app}} is taken litterally. I tries using ExpandConstant, but I have the same error, the + operator does not seem to be liked there. I tried passing the whole thing in the ExpandConstant, but instead I get an error at runtime:
"Internal error: Expression error 'Script error: Could not call proc.'".
Source: "InputFiles\prog.exe";
DestDir: "{tmp}/" ;
AfterInstall: ExecuteCommand('{tmp}\prog.exe', '--distribute ' + ExpandConstant('{code:GetShortName|{app}}') + '/{#SandboxInternalFolder}')
How can I use the code and constant at this location?

The parser for parameters for AfterInstall values is fairly primitive -- it can only cope with single values or a single string value wrapped in an ExpandConstant call. You cannot use any expressions or other function calls.
There's still a way to write that particular construction though, due to the way that ExpandConstant works:
AfterInstall: ExecuteCommand(ExpandConstant('{tmp}\prog.exe'), ExpandConstant('--distribute {code:GetShortName|{app}}\{#SandboxInternalFolder}'))
Having said that, unless there's a really compelling reason to pass all that in as parameters, you should just specify a simple procedure name and do whatever you want in there directly. And that way you can avoid expanding the code constant -- you can just call GetShortName directly too.

Related

Why would we need to use the #expr directive in Inno Setup?

Here's the documentation for Inno Setup Preprocessor: #expr and this one is for Inno Setup Preprocessor: Preprocessor output.
Why would we want to use the #expr directive?
I believe the #expr documentation explicitly mentions, why would you use the directive:
This directive is intended to be used with functions that produce side effects and do not return any significant value.
The documentation also shows a practical example of that:
#expr SaveToFile(...)
It seems that you do not know, what a side effect it. Check the Wikipedia article on Side effect (computer science) (emphasis mine):
Function ... is said to have a side effect if it modifies some state variable value(s) outside its local environment, that is to say has an observable effect besides returning a value (the main effect) to the invoker of the operation.
In the case of the SaveToFile function, the side effect is the saving of the function parameter to a file. This particular function does not even return anything. So it would be safe to use it with the #emit directive:
#emit SaveToFile(...)
It would still save the file. And as the function "returns" void, it won't emit anything (though it actually emits an empty line, but that's in most cases ignored by Inno Setup). But it's confusing to use #emit, as a reader of the code might expect the function actually does return (and consequently emit) something. Use of the #expr explicitly shows that the expression (and whole directive) has no effect on the script contents.
And with expressions/functions that do return something, like with ForceDirectories or Exec, you have to use #expr. Of course, it makes sense only in case you do not bother checking the results.
#expr ForceDirectories(Path)
With #emit, the function would emit its result, what would result in an invalid script.
Another way to do, what #expr does, is using the #emit with a comma operator with void second parameter:
#emit ForceDirectories(Path), void
Though again, this emits an empty line. And it's less obvious, what you want to do, than with the #expr.

Passing argument to lua function after evaluating function in string

I have functions process and matrix. The following code works
process(matrix({{2,4,6},{8,10,12},{14,16,20}}))
However the following doesn't work.
n='matrix({{2,4,6},{8,10,12},{14,16,20}})'
process(n)
It throws some error. The reason is obvious that process takes n as string rather than the output of the function matrix. So the basic difficulty involved here is about evaluating string from variable n and then give it as argument to the function process. Here loadstring function is of no use as matrix is local function and can't be referred from loadstring.
Is there any work around for this? I hope that I have clearly stated the problem here. It is about evaluating (or unloading) string and then passing it as argument to another function. Any help will be appreciated. Thanks.
as matrix is local function
Lua takes local declarations seriously. If a variable is declared local, it can only be accessed by code which is statically within the local scope of that variable. Strings which you later turn into code are not statically in the local scope and therefore cannot access local variables.
Now, with Lua 5.2+, you can provide load with a second parameter, a table which represents the global environment against which that Lua chunk will be built. If that table contains a matrix value, then the loaded string can access it. For Lua 5.1, you'd have to use setfenv on the function returned to load to accomplish a similar effect. The Lua 5.2+ method would look like this:
local env = {matrix = matrix}
local func = load("return matrix({{2,4,6},{8,10,12},{14,16,20}})", nil, "t", env)
process(func())
Note the following:
You must create an explicit table which is the global environment. There's nothing you can pass that says "make my locals available"; you have to put every local you'd like to access there. Which is, generally speaking, why you pass these things as parameters or just make them globals.
You explicitly need the "return " there if you want to get the results of the call to matrix.
You have to call the function. Functions are values in Lua, so you can freely pass them around. But if you want to pass the results of a function to another function, you have to actually call it.

Why do I have to specify an ExtPgm parameter for the Main Procedure?

My program, PKGDAYMONR has the control option:
ctl-opt Main( CheckDailyPackages )
The CheckDailyPackages procedure has the following PI:
dcl-pi *n ExtPgm( 'PGMNAME' );
As you can see the ExtPgm parameter is not the name of the program. In fact, it’s what came over in the template source and I forgot to change it. Despite the wrong name in ExtPgm, the program runs without a problem.
If I remove that parameter and leave the keyword as just ExtPgm, I get the following message:
RNF3573: A parameter is required for the EXTPGM keyword when the
procedure name is longer than 10.
If I drop ExtPgm from the Procedure Interface altogether, it also complains:
RNF3834: EXTPGM must be specified on the prototype for the MAIN()
procedure.
So why is it that I have to specify a parameter if it doesn't matter what value I enter?
O/S level: IBM i 7.2
Probably worth pursuing as a defect with the service provider; presumably for most, that would be IBM rather than a third-party, as they would have to contact IBM anyhow, given the perceived issue is clearly with their compiler. Beyond that, as my "Answer", I offer some thoughts:
IMO, and in apparent agreement with the OP, naming the ExtPgm seems pointless in the given scenario. I think the compiler is confused while trying to enforce some requirements in validations of the implicitly generated Prototype for the linear-main for which only a Procedure Interface is supplied; i.e. enforcing requirements that are appropriate for an explicit Prototype, but requirements that could be overlooked [thus are no longer requirements] in the given scenario.? I am suggesting that while the RNF3573 would seem appropriate for diagnosing EXTPGM specifications of an explicit Prototype, IMO that same effect is inappropriate [i.e. the validation should not be performed] for an implicit prototype that was generated by the compiler.
FWiW: Was the fixed-format equivalent of that free-form code tested, to see if the same or a different error was the effect? The following source code currently includes the EXTPGM specification with 'PGMNAME' as the argument [i.e. supplying any bogus value of 10-byte naming to supplicate the compiler, just as is being done in the scenario of the OP, solely to effect a successful compile], but could be compiled with the other variations with changes to the source, mimicking what was done with free-form variations, to test if the same\consistent validations and errors are the effect:
- just EXTPGM keyword coded (w/out argument); is RNF3573 the effect?
- the EXTPGM keyword could be omitted; is RNF3834 the effect?
- the D-spec removed entirely (if there are no parameters defined); ¿that was not one of the variations noted in the OP as being tried, so... the effect?
H MAIN(CheckDailyPackages)
*--------------------------------------------------
* Program name: CheckDailyPackages (PGMNAME)
*--------------------------------------------------
P CheckDailyPackages...
P B
D PI EXTPGM('PGMNAME')
/free
// Work is done here
/end-free
P CheckDailyPackages...
P E
I got a response from IBM and essentially Biswa was on to something, it simply wasn't clear (in my opinion) about the answer.
Essentially the EXTPGM is required on long Main procedure names in order to support recursive program calls.
This is the response I received from IBM explaining the reason for the scenario:
The incorrect EXTPGM would only matter if there was a call to the main
procedure (the program) within the module.
When the compiler processes the procedure interface, it doesn't know
whether there might be a call that appears later in the module.
EXTPGM keyword is used to define the external name of the program which you want to prototype. If you mention the EXTPGM then the program will be called dynamically.
Let us take an example in order to explain your query.
PGMA
D cmdExc PR ExtPgm('QSYS/QCMDEXC')
D 200A const
D 15P05 const
c callp cmdExc('CLRPFM LIB1/PF1':200)
C Eval *INLR = *ON
In the above example CmdExc used for the dynamic call to QSYS/QCMDEXC.
When we use the same program name as the EXTPGM parameter it acts as an entry point to the program when called from other programs or procedure.
But in any case when we mention any name as the sample parameter the EXTPGM will not give any error in compilation, but it gives the error during run time as it tries to resolve the name during run time.

Added an extra variable to a VBA function - now has compile error stating Expected:=

I have a VBA function that worked fine, until I tried to pass an extra variable to it. Now the code won't run, and I get an error stating Expected:=, I've tried renaming the function, but no help.
Was - Function GetData(site_add)
Changed to Function GetData(site_add, temporary) and failed - despite changing the call to the function accordingly...!?!
Is it possible that the compiler is glitching and I should focus on that? I have other functions in the code that use 5 call 5 variables and don't even call/use them all...!? Help...
By adding the second parameter, you are effectively telling the compiler that every call to this method now requires two parameters instead of one. So you have to find everywhere you call the GetData() function and make sure it now passes two parameters instead of one, even if the second parameter is Nothing. Now, if you want it to default to nothing so you don't need to pass it you can rewrite it as
GetData(site_add, Optional temporary)
*my vb is rusty, so take my example with a grain of salt please.

Should I use String instead of TFilename?

When passing filename parameters to procedures/functions, should I use TFilename or String.
If there is a difference, what is it, and what are then potential ramifications if using a String?
e.g.
procedure TForm1.OpenFile(const AFilename : String);
begin
//Open the file if it exists
...
end;
I think TFilename should be used when developing components because that way IDE can show it's property editor (TOpenDialog will be shown when clicked on ellipsis in the property inspector).
Other than this there is basically no difference which one to use. Remember that if you use TFilename you must add SysUtils to your uses clause.
The only practical difference between string and TFileName types in a plain code is in passing an argument by reference; the following code
procedure GetFileName(var FileName: TFileName);
begin
FileName:= 'abcd.abc';
end;
procedure TForm1.Button2Click(Sender: TObject);
var
S: string;
begin
GetFileName(S);
end;
does not compile with error
[DCC Error] E2033 Types of actual and formal var parameters must be identical
Maybe this is a bit too obvious, but using the string type doesn't communicate anything about the intended usage of a variable. But when you encounter a variable declared as a TFileName, there's a lot more detail communicated right there.
The same principle applys to other basic types like Integer, Cardinal, Double etc. Instead you might want to consider using aliases for these like TCustomerID, THashValue, TInterestRate, etc. as these communicate much clearer what the intended usage of these variables is.
This improves readablility, and also allows for changing the base-type when needed, without having to touch any code using the type... just a recompile and you're done (but do be carefull with binary compatibility ofcourse).
Hmm, My strong preference is for const AFilename: String;
For the reason that especially for larger projects, if you ever need to add source code from another coder, if they have used lots of custom types like TCustomerID, THashValue, TInterestRate, instead of Integer, Cardinal, Double, then you have lots of the above mentioned E2033 to resolve.
Even lots of delphi built in source code doesn't use TFileName, like:
function MatchesMask(const Filename, Mask: string): Boolean;
Furthermore if I have a variable defined like AFileName: TFileName; then its obvious its a filename & the named type doesn't add any readability for me, if anything in some cases it makes code less readable, because you have to click through to check what actual variable its derived from.

Resources