FileForums

FileForums (https://fileforums.com/index.php)
-   Conversion Tutorials (https://fileforums.com/forumdisplay.php?f=55)
-   -   FMXInno: Hidden Features Revealed (https://fileforums.com/showthread.php?t=106355)

BLACKFIRE69 07-06-2024 01:08

FMXInno: Hidden Features Revealed
 
8 Attachment(s)
1) Today, I unlock the feature 'Runtime Scripts' for FMXInno for general use.


So, what are 'Runtime Scripts'? They are scripts that run at runtime without needing to be compiled at compile-time. This means that once you compile your Setup, you don't need to recompile it to apply changes made in 'Runtime Scripts'.

Enough explanations, let's dive into a simple example:

Here, I use the file extension '.fxs' for Runtime Scripts, but you can use whatever you want: '.pas', '.iss', '.txt', etc.


Code:

Test_1.fxs
-------------------------------------------
program Test1CreateRect; // This line is optional, so you can get rid of it.

var ARect: FRectangle;

procedure CleanUpRect;
begin
  ARect := nil;
end

begin
  ARect := InitRectangleHandle;
  ARect.FCreate(FMXFormHandle);
  ARect.SetBounds(200, 90, 250, 250);
  ARect.FillColor(ALRed);

  AExternalObj := ARect.Handle;
end.

Code:

Example.iss
-------------------------------------------
var
  ARuntimeScript: FRuntimeScript;
  FirstRun  : Boolean;
  AExternalObj: TFMXObject;
 
procedure ABtnOnClick(Sender: TObject);
begin
  if not FirstRun then
  begin
      // Get the handle of the external object that was created with the external script.
    AExternalObj := ARuntimeScript.GetRegisteredLongIntVar('AExternalObj');
      // Call for cleanup.
    ARuntimeScript.CallProcFromScriptNoVar('CleanUpRect');
      // Remove the external object from FMXForm as well.
    FMXForm.RemoveObject(AExternalObj);
  end;

  ARuntimeScript.CompileAndRun;
  FirstRun := False;
end;

Code:

procedure InitializeWizard();
begin
  { FMX Form }
  FMXForm.FCreateFluent(WizardForm.Handle, False, False, 0.96, 0);

  { ABtn }
  ABtn.FCreate(FMXForm.Handle);
  ABtn.Text('&Run');
  ABtn.OnClick(@ABtnOnClick);

  { ARuntimeScript }
  ARuntimeScript.FCreate(ExpandConstant('{src}\_Scripts\Test_1.fxs'));
  ARuntimeScript.RegisterLongIntConst('FMXFormHandle', FMXForm.Handle);
  ARuntimeScript.RegisterLongIntVar('AExternalObj', 0); 
end;

1. This example will create a Rectangle (ARect1: FRectangle) at runtime, while the Setup is running.
2. And make some changes to the Runtime Script and smash the 'Run' button to see the changes.

Code:

  // ARect.FillColor(ALRed);
  ARect.FillColor(ALGreen);

3. Try creating a similar object (ARect2) by following the 'ARect1' code. Once you save the file, click the 'Run' button.

Code:

Test_1.fxs
-------------------------------------------
var ARect, ARect2: FRectangle;
 
begin
  ARect := InitRectangleHandle;
  ARect.FCreate(FMXFormHandle);
  ARect.SetBounds(80, 90, 200, 200);
  ARect.FillColor(ALRed);
 
  ARect2 := InitRectangleHandle;
  ARect2.FCreate(FMXFormHandle);
  ARect2.SetBounds(360, 90, 200, 200);
  ARect2.FillColor(ALBlue);
  ...
end.


* FMXInno has supported runtime scripts for quite a while with 'Lua'. Some may not be familiar with 'Lua', so i didn't promote it. On the other hand, 'FRuntimeScript' is Pascal, so everyone who uses InnoSetup can use this without any doubt.


[*] Built-In Features / Functions / Constants / Variables:
  1. 'Runtime Scripts' has built-in support for FMXInno classes, so you can use them inside 'Runtime Scripts'.
    -- Currently, it only supports the 'FMX_Standard', 'FMX_Shapes', 'FMX_Additional', 'FMX_Layouts', and 'FMX_Effects' classes. because i'm too lazy to add all the classes at the moment.

  2. 'Runtime Scripts' are not literally InnoSetup, so functions like "ExpandConstant" and "ExtractTemporaryFile" used in InnoSetup can't be used in runtime scripts.
    -- However, I've added some extra useful functions by default from 'System.SysUtils', 'System.Math', 'Winapi.Windows', etc., and you'll find them in the 'BuiltIn Features.txt' file.

  3. Added default variable 'Application: TApplication', so you can now use 'Application.ProcessMessages;' inside the runtime scripts.

[*] Register Custom Types:
Code:

Example.iss
-------------------------------------------
  ARuntimeScript.RegisterType('TDummyRec', 'record' +#13#10+
                                            ' PosX: Integer;' +#13#10+
                                            ' PosY: Integer;' +#13#10+
                                            'end;');

  ARuntimeScript.RegisterType('TsNum', 'Single');
 

 Test_1.fxs
------------------------------------------- 
var
  ShippingFees: TsNum;
  ADummyRec: TDummyRec;


[*] Load External Libraries (DLLs):

You can call external DLLs inside runtime scripts in three different ways,

-- Supported flags: 'stdcall', 'cdecl', and 'delayload'.

1. System Libs:
Code:

Test_1.fxs
-------------------------------------------
function KillTimer(handle: HWND; TimerId: LongWord): boolean;
  external 'KillTimer@user32.dll stdcall';

2. Libs from custom path:
Code:

Test_1.fxs
-------------------------------------------
procedure MyDllFunc(hWnd: Integer);
  external 'MyDllFunc@"C:\Test\Libs\MyDll.dll" stdcall';

3. I've added three special constants to runtime scripts, but you need to define them manually first.
(a) 'Src':
Code:

Example.iss
-------------------------------------------
ARuntimeScript.RegisterSpecialSrcConst(ExpandConstant('{src}'));


 Test_1.fxs
-------------------------------------------
procedure MyDllFunc(hWnd: Integer);
  external 'MyDllFunc@"{src}\MyDll.dll" stdcall';

(b) 'App':
Code:

Example.iss
-------------------------------------------
ARuntimeScript.RegisterSpecialAppConst(ExpandConstant('{src}\_ExtrnlDll'));


 Test_1.fxs
-------------------------------------------
procedure MyDllFunc(hWnd: Integer);
  external 'MyDllFunc@"{app}\MyDll.dll" stdcall';

(c) 'Tmp':
Code:

Example.iss
-------------------------------------------
ARuntimeScript.RegisterSpecialTmpConst(ExpandConstant('{src}\_ExtrnlDll'));


 Test_1.fxs
-------------------------------------------
procedure MyDllFunc(hWnd: Integer);
  external 'MyDllFunc@"{tmp}\MyDll.dll" stdcall';

(d) You can also use 'files:' to define the path.
Code:

Test_1.fxs
-------------------------------------------
procedure MyDllFunc(hWnd: Integer);
  external 'MyDllFunc@"files:MyDll.dll" stdcall';

(i) In runtime scripts, 'files:' is equal to 'Tmp'.
(ii) It doesn't extract the Lib to the 'Tmp' directory as InnoSetup does.
(e) Added built-in support for 'CreateCallback' function.
Code:

Test_1.fxs
-------------------------------------------
  SetTimer(0, 0, 250, CreateCallback(@TimerProc1));


[*] Call a function/procedure declared in Runtime Scripts through InnoSetup:

There are two ways to call a function/procedure declared in Runtime Scripts via InnoSetup.
(1) Without Parameters:
Code:

Test_1.fxs
-------------------------------------------
procedure SayHi;
begin
  ShowMessage('Hi, I am from the Runtime Script!');
end

 Example.iss
-------------------------------------------
  ARuntimeScript.CallProcFromScriptNoVar('SayHi');

(2) With Parameters:
-- Here you have to use FNewArray for Parameters.
Code:

Test_1.fxs
-------------------------------------------
procedure CalAddition(const x, y: Integer);
begin
  MyMathResult := x + y; 
end;

 Example.iss
-------------------------------------------
var
  AParams  : FNewArray;
 
  { AParams }
  AParams.FCreate;
  AParams.AddInteger(32);
  AParams.AddInteger(37);
  ARuntimeScript.CallProcFromScript('CalAddition', AParams);


[*] Register InnoSetup's function/procedure and use them in Runtime Script:

You can register functions/procedures for Runtime Scripts that have been declared in the InnoSetup (.iss).
1. At the moment, it doesn't accept any parameters.
2. You have to use the Callback function.

Code:

Example.iss
-------------------------------------------
type
  TDummyFunc2 = function: Integer;

function XWrapNoVarFuncNew(Callback: TDummyFunc2): Longword;
  external 'XWrapNoVar@files:FMXInno.dll stdcall delayload';

function GetValueFromInnoSetup: Integer;
begin
  Result := 69;
end;

procedure InnoMsgProc;
var
  LNum: Longint;
begin
  MsgBox('Hi', mbInformation, MB_OK);
end;

procedure InitializeWizard();
begin
  ...
  ARuntimeScript.RegisterProcNoVar(XWrapNoVarProc(@InnoMsgProc), 'procedure InnoMsgProc;');
  ARuntimeScript.RegisterProcNoVar(XWrapNoVarFuncNew(@GetValueFromInnoSetup), 'function GetValueFromInnoSetup: Integer;');
  ...
end;

Code:

Test_1.fxs
-------------------------------------------
program Test7RegisterProc;

var LuckyNum: Integer;

procedure DoIt;
begin
  InnoMsgProc();
end

begin
  LuckyNum := GetValueFromInnoSetup();
  DoIt;
end.


[*] PreProcessor and Include Files:

Runtime scripts support preprocessors and include files.

Code:

Test_1.fxs
-------------------------------------------
program Test10aIncludeFiles; // This line is optional, so you can get rid of it.

{$DEFINE WHO_ARE_YOU}

{$I ".\_Scripts\Test_10b.fxs"}

var S: String;

begin
{$IFDEF WHO_ARE_YOU}
  S := 'Mr. BLACKFIRE';
{$ELSE}
  S := 'Mr/Ms/Mrs. Nobody';
{$ENDIF}

  SayHi(S);
end.


[*] Syntax Highlighting in the Text Editor:

Open the '.fxs' file in your favorite text editor or IDE and select the file syntax as 'Pascal'. Now you're good to go.




.


All times are GMT -7. The time now is 04:21.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2026, vBulletin Solutions Inc.
FileForums @ https://fileforums.com