NSIS: problem with $ 0- $ 9, $ R0- $ R9

Greetings to all who read :) The

target hub does not correspond to the content of the article, but what to do ... - there are no thematic ones for the purpose, but the most suitable one is “C”, because readers of it will most easily be able to perceive what is written.

Purpose of the article


An option to solve the problem of controlling global variables when writing the installer / uninstaller using NSIS .

Small introduction


When writing nsis code, the programmer has the opportunity to use global variables whose scope is the entire project. There are only 20 of them: $ 0- $ 9 and $ R0- $ R9.
It is very convenient to use them, because this eliminates the need to introduce additional variables formally intended for solving target problems. I say, formally, because any nsis variables have a global scope and if they differ in anything, then only with the qualifier const (speaking in C / C ++ terms):

!define variable1 ''value 1''

Features:

  • static variable of global scope;
  • Initialized upon declaration;
  • Contact Method: $ {variable1}

Var variable2

Features:

  • Dynamic variable of global scope;
  • It is declared anywhere by code, but above the first call;
  • It is initialized as many times as necessary, but only inside function blocks and macros;
  • Access Method: $ variable2

Thus, given the above features, it becomes clear that the targeted creation of any variables other than static is difficult to justify, because global variables already exist by default, there are enough of them to satisfy any needs (and if not, you need to ask the "appropriate" questions to the programmer) and any other ones will most likely only clog the code.

Description of the problem


But the trouble is, this code always generates errors:


Function F1
    StrCpy $0 "vladimir"
FunctionEnd

Function F2
    StrCpy $0 "alexei"
    Call F1
    ${If} $0 == "alexei" ; //  $0  "vladimir"
        ; // -  ...
    ${EndIf}
FunctionEnd

And you can come up with a lot of examples of this, and the nsis programming craftsmen solved this problem as best they could:

  • Someone just carefully watched all the variables (such people need to erect a monument));
  • Someone, mainly the creators of various plug-ins, each time wanting to use any of the variables presented, made it Push onto the stack, and after the end of use they returned the primary value by calling Pop;
  • And someone produced variables through Var, as described above.

In any case, they all faced the problem of the lack of a universal mechanism that solves the problem of monitoring the state of global variables.

Here I allow myself to offer her (problems) a universal solution.

Solution to the problem


To begin with, the link to NSISList is a plugin that introduces the ability to use a list-type data structure, which should be taken in a similar way to std :: list in C ++.

The second step is the source code of the problem-solving mechanism :


!verbose 3
!include NSISList.nsh	;      *

/*
  Dump, :

!define F1 "!insertmacro _F1"
!macro _F1 [paramIn1 paramIn2 ... paramInN] [paramOut1 paramOut1 ... paramOutM]
    ${Dump}
    [Push ${paramIn1}
	Push ${paramIn2}
	...
	Push ${paramInN}]
    !ifdef __UNINSTALL__
        Call un.F1
    !else
        Call F1
    !endif
    ${Undump}
    [Pop ${paramOut1}
	Pop ${paramOut2}
	...
	Pop ${paramOutM}]
!macroend

!macro Func_F1 un
    Function ${un}F1
        ;  ...
        ;         $0-$9, $R0-$R9.
        ;        .
    FunctionEnd
!macroend
!insertmacro Func_F1 ""
!insertmacro Func_F1 "un."

*/

/**   Dump */
!define InitDump "!insertmacro _InitDump"
!macro _InitDump
    !ifdef __UNINSTALL__
        Call un.InitDump
    !else
        Call InitDump
    !endif
!macroend

!macro Func_InitDump un
    Function ${un}InitDump
        #  $0-$9
        ${List.Create} d0
        ${List.Create} d1
        ${List.Create} d2
        ${List.Create} d3
        ${List.Create} d4
        ${List.Create} d5
        ${List.Create} d6
        ${List.Create} d7
        ${List.Create} d8
        ${List.Create} d9
        #  $R0-$R10
        ${List.Create} dR0
        ${List.Create} dR1
        ${List.Create} dR2
        ${List.Create} dR3
        ${List.Create} dR4
        ${List.Create} dR5
        ${List.Create} dR6
        ${List.Create} dR7
        ${List.Create} dR8
        ${List.Create} dR9
    FunctionEnd
!macroend
!insertmacro Func_InitDump ""
!insertmacro Func_InitDump "un."


/**     nsis-  Dump */
!define Dump "!insertmacro _Dump"
!macro _Dump
    !ifdef __UNINSTALL__
        Call un.Dump
    !else
        Call Dump
    !endif
!macroend

!macro Func_Dump un
    Function ${un}Dump
        # $0-$9
        ${List.Add} d0 $0
        ${List.Add} d1 $1
        ${List.Add} d2 $2
        ${List.Add} d3 $3
        ${List.Add} d4 $4
        ${List.Add} d5 $5
        ${List.Add} d6 $6
        ${List.Add} d7 $7
        ${List.Add} d8 $8
        ${List.Add} d9 $9
        # R0-R9
        ${List.Add} dR0 $R0
        ${List.Add} dR1 $R1
        ${List.Add} dR2 $R2
        ${List.Add} dR3 $R3
        ${List.Add} dR4 $R4
        ${List.Add} dR5 $R5
        ${List.Add} dR6 $R6
        ${List.Add} dR7 $R7
        ${List.Add} dR8 $R8
        ${List.Add} dR9 $R9
    FunctionEnd
!macroend
!insertmacro Func_Dump ""
!insertmacro Func_Dump "un."


/**     nsis-  Dump */
!define Undump "!insertmacro _Undump"
!macro _Undump
    !ifdef __UNINSTALL__
        Call un.Undump
    !else
        Call Undump
    !endif
!macroend

!macro Func_Undump un
    Function ${un}Undump
        # $0-$9
        ${List.Pop} $0 d0
        ${List.Pop} $1 d1
        ${List.Pop} $2 d2
        ${List.Pop} $3 d3
        ${List.Pop} $4 d4
        ${List.Pop} $5 d5
        ${List.Pop} $6 d6
        ${List.Pop} $7 d7
        ${List.Pop} $8 d8
        ${List.Pop} $9 d9
        # R0-R9
        ${List.Pop} $R0 dR0
        ${List.Pop} $R1 dR1
        ${List.Pop} $R2 dR2
        ${List.Pop} $R3 dR3
        ${List.Pop} $R4 dR4
        ${List.Pop} $R5 dR5
        ${List.Pop} $R6 dR6
        ${List.Pop} $R7 dR7
        ${List.Pop} $R8 dR8
        ${List.Pop} $R9 dR9
    FunctionEnd
!macroend
!insertmacro Func_Undump ""
!insertmacro Func_Undump "un."

And the third step is a small description of how it works:

Each function created and designed according to the rule described by the previous main code comment will be absolutely protected from the point of view of the danger of rewriting the global variables $ 0- $ 9, $ R0- $ R9 inside it, and therefore - from violation of the logic of the calling function.

It works like this:

  1. The F1 function itself is not called directly,
    Call F1
    and through the macro wrapper
    ${F1} [paramIn1 [paramIn2]...[paramInN]] [paramOut1 [paramOut2]...[paramOutM]]
  2. Even before the function is called directly, it will be called
    ${Dump}
    as a result, the current values ​​of the variables $ 0- $ 9, $ R0- $ R9 are saved in Dump. Under the hood he has a lot of lists, each tied to its target variable;
  3. Loading on the stack all the target variables for the function
    Push ${paramIn1} Push ${paramIn2} ... Push ${paramInN}
    (if it is needed);
  4. The function will be called;
  5. The function will execute its logic and exit;
  6. Call will happen
    ${Undump}
    as a result, the values ​​of global variables stored on the last call of $ {Dump} will be restored;
  7. Unloading the function results from the stack
    Pop ${paramOut1} Pop ${paramOut2} ... Pop ${paramOutM}
    (if it is needed);
  8. The macro wrapper on the F1 function will complete its work.

Conclusion


As a result, we got a universal design for safe writing of nsis code.

You can even say that it does not bring any minuses, except that the compiled code will work for about 30 ms. slower.

Hope this makes life easier for someone :)

Thank you!

Source: https://habr.com/ru/post/undefined/


All Articles