- NSIS Discussion
- Calling Functions with arguments
Archive: Calling Functions with arguments
pgg1
9th August 2007 11:01 UTC
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
9th August 2007 11:45 UTC
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
9th August 2007 11:49 UTC
Thank you very much.
pgg1
9th August 2007 12:01 UTC
I am having problems with the line
${if} ${FileExists} $SYSDIR\$R0
The error from the compiler is: Invalid command: ${if}
Any ideas?
Cheers
Paul
Afrow UK
9th August 2007 12:12 UTC
!include LogicLib.nsh
Stu
pgg1
9th August 2007 12:15 UTC
Oh man, school boy error.
Thanks,
Paul
pgg1
9th August 2007 13:50 UTC
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
9th August 2007 13:57 UTC
File is compile time instruction, variables on the other hand are empty when compiling, they take action at run time.
pgg1
9th August 2007 14:05 UTC
OK, thanks for that. Do you have a solution? If not, could you point me in the right direction.
Thanks,
Paul
Red Wine
9th August 2007 14:10 UTC
Yep, use a definition.
pgg1
9th August 2007 14:21 UTC
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
9th August 2007 14:28 UTC
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
9th August 2007 14:37 UTC
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
9th August 2007 14:50 UTC
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.
pgg1
9th August 2007 15:09 UTC
OK, thanks for your help.
Red Wine
9th August 2007 15:18 UTC
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
9th August 2007 19:20 UTC
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
9th August 2007 19:46 UTC
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
18th August 2007 22:24 UTC
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
18th August 2007 22:47 UTC
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
19th August 2007 17:58 UTC
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
Afrow UK
19th August 2007 18:05 UTC
No need to have SetOutPath in the macro then if you are always using System32.
Stu
pgg1
19th August 2007 18:24 UTC
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
19th August 2007 18:37 UTC
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
19th August 2007 18:45 UTC
Yep :)
But now you don't even need the macro because it only contains one instruction.
Stu
pgg1
19th August 2007 19:01 UTC
Ah man, I've got loads to learn. Thanks, my other earlier questions still stand though.
Cheers,
Paul
Afrow UK
19th August 2007 19:54 UTC
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
19th August 2007 20:47 UTC
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