Is it possible to test Pascal Script functions without compiling the installer? - inno-setup

I am wondering if it is somehow possible to test the functions in my [Code] section without compiling the whole installer each time and run it. This would make the development and testing of the functions much easier.
Many thanks!
Sören

The direct answer to the question "Is it possible to test Pascal Script functions without compiling the installer?" is "no." Compilation is required to run the code. The real issue, I think, is not that compilation is required but rather that large projects can be time-consuming to recompile when you are performing code testing.
One workaround for time-consuming recompiles is to create a "stub" setup for code testing. Simple example:
[Setup]
AppName=TestCode
ArchitecturesInstallIn64BitMode=x64
AppVerName=TestCode
UsePreviousAppDir=false
DefaultDirName={commonpf}\TestCode
Uninstallable=false
OutputDir=.
OutputBaseFilename=TestCode
PrivilegesRequired=none
[Messages]
SetupAppTitle=TestCode
[Code]
function BoolToStr(const B: Boolean): string;
begin
if B then
result := 'true'
else
result := 'false';
end;
procedure Test();
begin
MsgBox(BoolToStr(true), mbInformation, MB_OK);
end;
function InitializeSetup(): Boolean;
begin
result := false;
Test();
end;
In the InitializeSetup event function we set result to false so nothing actually gets "installed"; the whole purpose is simply to run the Test procedure and then terminate. The Test procedure tests the sample BoolToStr function to make sure it works as expected.
When you are certain your code works as expected, you can copy it to your main project. (Don't forget to copy all dependencies such as global variables, DLL import statements, etc.)

I think yes
For compiling:
Use 'Ctrl+F9'
For testing
use only 'F9' (_without '' in inno setup).
Thanks.
EDIT:
Sorry You cant use this Processes.Look into Tlama comment.

Related

Inno Setup ParseVersion is not callable from [Code]

The macros ParseVersion and RemoveBackslash, for example, are both declared in ISPPBuiltins.iss. If I attempt to call both from within [Code]:
function InitializeSetup: Boolean;
var
Major, Minor, Rev, Build: Integer;
begin
RemoveBackslash('123\');
ParseVersion('12345', Major, Minor, Rev, Build);
end;
RemoveBackslash compiles fine, but adding ParseVersion causes a compiler error:
Unknown identifier 'ParseVersion'"
When part of another macro declaration, ParseVersion seems to compile fine, just not from [Code]. Should I be able call it like that?
As #Andrew wrote already, the ParseVersion (or actually since Inno Setup 6.1, the GetVersionComponents) is a preprocessor function. So it has to be called using preprocessor directives and its results stored into preprocessor variables.
#define Major
#define Minor
#define Rev
#define Build
#expr GetVersionComponents("C:\path\MyProg.exe", Major, Minor, Rev, Build)
If you need to use the variables in the Pascal Script Code, you again need to use preprocessor syntax. For example:
[Code]
function InitializeSetup: Boolean;
begin
MsgBox('Version is: {#Major}.{#Minor}.{#Rev}.{#Build}.', mbInformation, MB_OK);
Result := True;
end;
The above is true, if you really want to extract the version numbers on the compile-time. If you actually want to do it in the Code section, i.e. on the install-time, you have to use Pascal Script support function GetVersionComponents (yes, the same name, but a different language):
[Code]
function InitializeSetup: Boolean;
var
Major, Minor, Rev, Build: Word;
Msg: string;
begin
GetVersionComponents('C:\path\MyProg.exe', Major, Minor, Rev, Build);
Msg := Format('Version is: %d.%d.%d.%d', [Major, Minor, Rev, Build]);
MsgBox(Msg, mbInformation, MB_OK);
Result := True;
end;
The Pascal Script function GetVersionComponents is available since Inno Setup 6.1 only.
The RemoveBackslash works in both contexts, as there's both Pascal Script RemoveBackslash and Preprocessor RemoveBackslash.
In the Change Log (for 6.1.x) it mentioned:
Support function GetFileVersion and ParseVersion have been renamed to GetVersionNumbersString and GetVersionComponents respectively. The old names are still supported, but it is recommended to update your scripts to the new names and the compiler will issue a warning if you don't.
So be wary of that when you upgrade. But as you rightly say, these are Inno Setup Preprocessor (ISPP) functions. With respects to the Pascal Script section there is nothing listed in the Support Function Reference.
Someone else might be able to shed more insight about this, or offer a workaround, but you might have to request the feature in the Info Setup forum.

Why do I need Sharemem in my Delphi dll which only exposes a function with WideString parameters?

I have a dll and a test application written in Delphi. The test application uses multiple threads to call the function exported by the dll. The exported function has a trivial thread safe implementation. When running the test application various errors (access violation, invalid pointer operation, stack overflow etc) happens or the application freezes. In some cases the application finishes without errors.
Note that these errors only happen (surface) when using multiple threads. When calling the function from the main thread only then everything works fine.
I have found that adding ShareMem to both the dll and the application stops all these kind of errors. But I don't understand why. To my knowledge ShareMem is only needed when passing long strings between the dll and the application. As far as I know WideString is not a long string.
Also according to this post ShareMem should not be required:
Why can Delphi DLLs use WideString without using ShareMem?
Here is the source of the dll:
library External;
uses
Winapi.Windows;
type
TMyType = class
private
FText: string;
end;
function DoSomething(input: WideString; out output: WideString): Bool; stdcall;
var
x: TObject;
begin
x := TMyType.Create;
try
output := x.ClassName;
finally
x.Free;
end;
Result := True;
end;
exports
DoSomething;
begin
end.
Here is the test application:
program ConsoleTest;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Winapi.Windows,
OtlParallel;
function DoSomething(input: WideString; out output: WideString): Bool; stdcall; external 'External.dll' name 'DoSomething';
var
sResult: WideString;
begin
try
Parallel.&For(0, 500).Execute(procedure(value: Integer)
var
sResult: WideString;
begin
DoSomething('hhh', sResult);
end);
WriteLn('Done');
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Why ShareMem makes the bugs go away and is there another way to fix these bugs?
I am using Delphi XE2 and OmniThread 3.07.5.
Update
- Same issue when run from a VCL application's button's on click event handler
- If DoSomething uses a critical section inside then runs fine
- If FText field is removed from TMyClass then no errors are reported but the application randomly freezes
For the standard memory manager (FastMM) to support multi threading, you need to set the IsMultiThread flag.
When you use RTL for threading, this flag is automatically set. As revealed in the comments to the question, OTL also use RTL to start its threads. So the memory manager in your executable is aware of threading, but the distinct memory manager in the dll causes errors. When you use "sharemem", there is only one memory manager which is aware of threading because of OTL, so you encounter no errors.
An alternative solution, apart from using a shared memory manager, would be to set the flag also for the memory manager in the dll.

Inno Setup: how to call custom functions from the InstallDelete section

I would need Inno Setup generated installer to delete certain files prior installation if the software is already installed with an older version.
I tried to do this by comparing version numbers (custom function below) but when compiling, Inno Setup generates an error:
[ISPP] Undeclared identifier: "GetInstalledVersion".
The Inno Setup script relevant extract is:
(...)
[Code]
function GetInstalledVersion(MandatoryButNotUsedParam: String): String;
var Version: String;
begin
if RegValueExists(HKEY_LOCAL_MACHINE,'Software\Microsoft\Windows\CurrentVersion\Uninstall\'+ExpandConstant('AppId')+'_is1', 'DisplayVersion') then
begin
RegQueryStringValue(HKEY_LOCAL_MACHINE,'Software\Microsoft\Windows\CurrentVersion\Uninstall\'+ExpandConstant('AppId')+'_is1', 'DisplayVersion', Version);
MsgBox(ExpandConstant('Existing version:'+Version+' New version:'+ExpandConstant('AppVersion')), mbInformation, MB_OK);
Result := Version;
end
else
begin
Result := '';
end
end;
(...)
[InstallDelete]
#define InstalledAppVersion GetInstalledVersion('')
#if "1.013" > InstalledAppVersion
Type: files; Name: {userappdata}\xxx\*.hhd
#endif
Being new to Inno Setup, this is certainly a trivial question but no answer found on forums. The question is thus: how can I properly call the function GetInstalledVersion from the [InstallDelete] section?
Is there an issue because [InstallDelete] section might be called before [code] section is read?
Many thanks for any help / hint!
Do you want to check for currently installed version and if it's below 1.013,
then remove user files from {userappdata}\xxx\*.hhd ?
then what you need is parameter Check http://www.jrsoftware.org/ishelp/index.php?topic=scriptcheck
[Code]
function isOldVersionInstalled: Boolean;
begin
// Result := <True|False>;
end;
[InstallDelete]
Type: files; Name: {userappdata}\xxx\*.hhd; Check:isOldVersionInstalled;
What is wrong with your example:
You are calling a Pascal function from the pre-processor.
Those are two different things.
You can define a macro in pre-processor - that's kind of like a function,
but that's not what you want because Pre-processor only runs on compile time and so it can't be used to check on state of User's files/environment.

Can you define a function prototype in Inno Setup

I would like to be able to structure my code for my Inno Setup project but I am forced to move code around because you can't call a function unless it is defined first.
Is there a way to declare a prototype at the top so that I don't get the "Unknown identifier" error and so that I can structure my code in logical blocks.
In Pascal (including a Pascal Script used in Inno Setup), you can define a function prototype (aka forward declaration) using a forward keyword:
procedure ProcA(ParamA: Integer); forward;
procedure ProcB;
begin
ProcA(1);
end;
procedure ProcA(ParamA: Integer);
begin
{ some code }
end;
See Forward declared functions.

Extending event functions via #Include directive

I have a base inno-setup script that I use as a template for several installers. As part of the base script I have a call to the event function, NextButtonClick.
I would now like to add some additional code to the NextButtonClick event that will only be executed by one of my installers. Is there some way to "extend" the NextButtonClick event? I'm thinking of something along the lines of Python's super() function.
Inno-setup uses Pascal as a scripting language, so perhaps a Pascal expert can offer some insight.
Not directly
Remember the #include directive is just a pre-compiler directive which makes the included file to appear in the place the directive is to the inno setup script compiler.
but
To avoid including individual installer code on the template script, you can create a convention to call a procedure in the template.
The only rule you have to follow is that every installer must declare the procedure, even blank. That way, you can customize as per-installer basis while maintaining a neutral template.
Your template may be something like:
function NextButtonClick(CurPageID: Integer): Boolean;
begin
Result := BeforeNextButtonClick(CurPageID);
//if the per-installer code decides not to allow the change,
//this code prevents further execution, but you may want it to run anyway.
if not Result then
Exit;
//your template logic here
Result := Anything and More or Other;
//same here!
if not Result then
Exit;
//calling the per-installer code
Result := AfternextButtonClck(CurPageID);
end;
Then individual installers may look like this:
function BeforeNextButtonClick(CurPageID: Integer): Boolean;
begin
//specific logic here
Result := OtherThing;
end
function AfterNextButtonClick(CurPageID: Integer): Boolean;
begin
//and here, a blank implementation
Result := True;
end;
#include MyCodeTemplate.iss
Maybe it is possible to implement a complex approach, I just can't remember if PascalScript supports procedural types and no time to check with inno.
disclaimer all code written directly here to show you the idea, it may not compile.
I'm using the following workaround, which may make things hard to manage eventually, but using version control I'm able to keep a handle on it for now:
In my individual installers, I have a series of #define directives. For example:
#define IncludeSomeFeature
#define IncludeSomeOtherOption
Then in my base template, I use the #ifdef directives to optionally include different pieces of code within the Pascal scripting events:
function NextButtonClick(CurPageID: Integer): Boolean;
var
ResultCode: Integer;
begin
Result := True;
if CurPageID = wpReady then begin
#ifdef IncludeSomeFeature
... some code ...
#endif
#ifdef IncludeSomeOtherOption
... some more code ...
#endif
... code to run for every installer ...
end;
end;
The one downside to this approach is that code that the base template will slowly fill up with code that really belongs with the individual installer. However, since these are compile time directives, the resulting setup executables should not get bloated.
Really, though, my biggest problem with this approach is that it just doesn't feel like The Right Way™.

Resources