Archive: Calling Functions with arguments


Calling Functions with arguments
Hi

I need to know how to achieve the following:

Within a section I want to call a function and pass it a string that represents a file.

The Function will then determine if that file exists, if it does then the function simply returns and we do nothing, else we copy the file to the relevant directory.

Here is a first attempt:


!include "LogicLib.nsh"

OutFile "installer.exe"

Section
Call fileExists someFileA.txt
Call fileExists someFileB.txt
Call fileExists someFileC.txt
SectionEnd

Function fileExists file
${if} IfFileExists $SYSDIR\file
return
${else}
File /oname=$SYSDIR\file file
${EndIf
FunctionEnd


I realise this syntax in not correct but It's what I'm trying to achieve.

Could someone help me out please.

Thanks

Paul

You need to use the stack
Functions don't take argument in NSIS.

You would need to do something like this in your section:


Section
push someFileA.txt
Call myFileExists

push someFileB.txt
Call myFileExists

push someFileC.txt
Call myFileExists
SectionEnd

Function myFileExists
exch $R0
${if} ${FileExists} $SYSDIR\$R0
goto end
${else}
File /oname=$SYSDIR\$R0 $R0
${EndIf}
end:
pop $R0
FunctionEnd



If you want write code that looks like you are passing arguments, you can use a macro. You can wrap function calls in macros like this:


!macro myFileExists file
push ${file}
call myFileExists
!macroend

Section
!insertmacro myFileExists someFileA.txt
!insertmacro myFileExists someFileB.txt
!insertmacro myFileExists someFileC.txt
SectionEnd

Function myFileExists
exch $R0
${if} ${FileExists} $SYSDIR\$R0
goto end
${else}
File /oname=$SYSDIR\$R0 $R0
${EndIf}
end:
pop $R0
FunctionEnd



Have a read of http://nsis.sourceforge.net/Macro_vs_Function which is very useful for explaining all this.

Thank you very much.


I am having problems with the line


${if} ${FileExists} $SYSDIR\$R0


The error from the compiler is: Invalid command: ${if}

Any ideas?

Cheers

Paul

!include LogicLib.nsh

Stu


Oh man, school boy error.

Thanks,

Paul


Me again

Have a look at this demo program taken from a reply above.


!include LogicLib.nsh
OutFile "installer.exe"

InstallDir $DESKTOP\install

!macro myFileExists file
push ${file}
call myFileExists
!macroend

Section
SetOutPath $INSTDIR
File someFileA.txt
SectionEnd

Section
!insertmacro myFileExists "someFileA.txt"
SectionEnd

Function myFileExists
exch $R0
${if} ${FileExists} $SYSDIR\$R0
MessageBox MB_OK "$SYSDIR\$R0 exists"
goto end
${else}
MessageBox MB_OK "$SYSDIR\$R0 does not exists"
File /oname=$SYSDIR\$R0 $R0
${EndIf}
end:
pop $R0
FunctionEnd


Providing the file I am looking for is not in $SYSDIR then I want to copy a file over to that directory. However, the following line:

File /oname=$SYSDIR\$R0 $R0


Gives me an error saying:

File: "$R0" -> no files found.
Usage: File [/nonfatal] [/a] ([/r] [/x filespec [...]] filespec [...] |
/oname=outfile one_file_only)

Any ideas would be appreciated,

Cheers

Paul

File is compile time instruction, variables on the other hand are empty when compiling, they take action at run time.


OK, thanks for that. Do you have a solution? If not, could you point me in the right direction.

Thanks,

Paul


Yep, use a definition.


Use a definition? Great, what does that mean? Does it mean define something such as:

!define SOMETHING "something"


and then reference ${SOMETHING} later on in the script.

I'm very new to NSIS and some code would be helpful.

Thanks,

Paul

This is simplified example, note that the code you've posted above is kinda improper.
Also note that you're extracting the file on desktop\install and later you're searching for the file under $SYSDIR which means you'll never find it there.

!define MY_FILE "someFileA.txt"
!include LogicLib.nsh

OutFile "installer.exe"
InstallDir $DESKTOP\install

Section
SetOutPath $INSTDIR
File "${MY_FILE}"
call myFileExists
SectionEnd

Function myFileExists
${if} ${FileExists} "$SYSDIR\${MY_FILE}"
MessageBox MB_OK "$SYSDIR\${MY_FILE} exists"
${else}
MessageBox MB_OK "$SYSDIR\${MY_FILE} does not exists"
File /oname=$SYSDIR\${MY_FILE} ${MY_FILE}
${EndIf}
FunctionEnd

Thanks for that, I appreciate it a lot.

However, it's rather hard coded. I want to be able to call this method 3 times with a different file on each call.

Any ideas?

Thanks

Paul


As I said it's simplified example.
If you want to use it several times in your script you'd need to build a macro which calls the function as you did earlier.
Taking a look at wiki's code examples would give you many ideas on how to do that properly.


OK, thanks for your help.


You're welcome.

One more thing, for my opinion a good way to learn about macros in deep, is to examine the included headers e.g. filefunc.nsh.


I think you should be extracting your files to a temporary folder (I use $PLUGINSDIR\something) and then if the file does not exist in $SYSDIR you can use CopyFiles to copy it from the temporary folder to $SYSDIR.

Using $PLUGINSDIR is handy because the extra copies will be deleted when the installer quits.

Don


It seems to me wasting of time and unnecessary code because of unnecessary steps. 1st extract files in $PLUGINSDIR and 2nd use CopyFiles to put them in their correct location. I just can't catch the point.


Hi

I have written a macro that takes a "Path" and a "File". The macro determines if the file exists within the path, if it does we do nothing, otherwise we put it there.


!include LogicLib.nsh
OutFile "installer.exe"

!macro InstallFileTo Path File
${if} ${FileExists} ${Path}\${File}
; do nothing.
${else}
SetOutPath ${Path}
File ${File}
${endif}
!macroend

Section "Install files"
!insertmacro InstallFileTo "$SYSDIR" "FileA.txt"
!insertmacro InstallFileTo "$SYSDIR" "FileB.txt"
!insertmacro InstallFileTo "$SYSDIR" "FileC.txt"
SectionEnd


It works OK but I'm trying to make the macro smaller, I'm sure there is a better way.


Any ideas?

Cheers,

Paul

Use 'SetOverwrite off' and you won't have to check if the file exists.


!macro InstallFileTo Path File
SetOutPath ${Path}
File ${File}
!macroend

Section "Install files"
SetOverwrite off
!insertmacro InstallFileTo "$SYSDIR" "FileA.txt"
!insertmacro InstallFileTo "$SYSDIR" "FileB.txt"
!insertmacro InstallFileTo "$SYSDIR" "FileC.txt"
SectionEnd

Your example has the path all alike. In that case the macro doesn't save anything (and costs a little bit of space because it repeats the SetOutPath instruction each time).

Don

Thanks for the reply,

To avoid repeating SetOutPath would the code look like:


!include LogicLib.nsh
OutFile "installer.exe"

!define SYSTEM32 $SYSDIR

!macro InstallFileTo File
SetOutPath ${SYSTEM32}
File ${File}
!macroend

Section "Install files"
SetOverwrite off
!insertmacro InstallFileTo "FileA.txt"
!insertmacro InstallFileTo "FileB.txt"
!insertmacro InstallFileTo "FileC.txt"
SectionEnd


Also, I would like to keep a record of what files I have put in $SYSDIR, if any.

Would the code look something like:


!include LogicLib.nsh
OutFile "installer.exe"

!define SYSTEM32 $SYSDIR

!macro InstallFileTo File Key
SetOutPath ${SYSTEM32}
File ${File}
WriteRegStr HKLM "Software\CompanyName\CompanyApp\" ${Key} ${File}
!macroend

Section "Install files"
SetOverwrite off
!insertmacro InstallFileTo "FileA.txt" "FileInstalled_1"
!insertmacro InstallFileTo "FileB.txt" "FileInstalled_2"
!insertmacro InstallFileTo "FileC.txt" "FileInstalled_3"
SectionEnd

Section "Uninstall"

...

ReadRegStr $0 HKLM "Software\CompanyName\CompanyApp" "FileInstalled_1"
StrLen $1 $0
${if} $1 > '0'
Delete "${Path}\${File}"
${EndIf}

... FileInstalled_2
... FileInstalled_3

...

SectionEnd


Is this the best way to save something?

I realise there will be some duplicate code in the Uninstall section which is not
desirable. I suppose I would put that in a macro as well.

Is the best solution to put both install/uninstall in a function and then wrap that
with a macro. Therefore, we could call that on install and uninstall.

Cheers,

Paul

No need to have SetOutPath in the macro then if you are always using System32.

Stu


Thanks for the reply,

Initially I thought it would be nice to keep the macro generic so any part of my script can use it. Then I got advised that using SetOutPath in the way that I was was not the best option because "the macro doesn't save anything", fair enough.

So basically it's a trade off between how generic you want your code to be and efficiency.

Cheers,

Paul


I suppose you are saying it should be used like the following:


!include LogicLib.nsh
OutFile "installer.exe"

!macro InstallFileTo File
File ${File}
!macroend

Section "Install files"
SetOutPath $SYSDIR
SetOverwrite off
!insertmacro InstallFileTo "FileA.txt"
!insertmacro InstallFileTo "FileB.txt"
!insertmacro InstallFileTo "FileC.txt"
SectionEnd


Cheers,

Paul

Yep :)
But now you don't even need the macro because it only contains one instruction.

Stu


Ah man, I've got loads to learn. Thanks, my other earlier questions still stand though.

Cheers,

Paul


When you insert a macro, you are copying its contents and placing it there - same as !include if you like. Any instruction with a ! in front of it is a compile time instruction and it is non existent in the built installer. Macros are useful mainly for clearing up repeated code and helps when one needs to modify some code without having to make the changes more than once. Using a macro that contains 10 lines of code 100 times will create 1000 lines of code on compile, whereas 10 lines in a function called 100 times will still be 10 lines of code being executed 100 times. Of course, 1000 lines of code will create a bigger installer overhead.

Stu


Thanks,

Sorry I didn't make myself very clear. Just to reiterate I want to do the following:

(1) Add 3 files to $SYSDIR providing there not there (Which we have already discussed).

(2) If I write a file to $SYSDIR then I need to record that, so when I uninstall I can remove any files placed there. What is the best way to record stuff? To save, would it be:


WriteRegStr HKLM "Software\CompanyName\CompanyApp\" "some_key1" "FileA.txt"
WriteRegStr HKLM "Software\CompanyName\CompanyApp\" "some_key2" "FileB.txt"
WriteRegStr HKLM "Software\CompanyName\CompanyApp\" "some_key3" "FileC.txt"


To retrieve it on uninstall:


ReadRegStr $0 HKLM "Software\CompanyName\CompanyApp" "some_key1"
StrLen $1 $0
${if} $1 > '0'
Delete "${SYSDIR}\FileA.txt"
${EndIf}


Cheers,

Paul