Skip to content
⌘ NSIS Forum Archive

Calling Functions with arguments

28 posts

pgg1#

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
DrDan#
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.
pgg1#
I am having problems with the line


${if} ${FileExists} $SYSDIR\$R0
The error from the compiler is: Invalid command: ${if}

Any ideas?

Cheers

Paul
pgg1#
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
Red Wine#
File is compile time instruction, variables on the other hand are empty when compiling, they take action at run time.
pgg1#
OK, thanks for that. Do you have a solution? If not, could you point me in the right direction.

Thanks,

Paul
pgg1#
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
Red Wine#
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
pgg1#
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
Red Wine#
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.
Red Wine#
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.
demiller9#
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
Red Wine#
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.
pgg1#
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
demiller9#
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
pgg1#
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
pgg1#
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
pgg1#
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
Afrow UK#
Yep 🙂
But now you don't even need the macro because it only contains one instruction.

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

Cheers,

Paul
Afrow UK#
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
pgg1#
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