I'm working on making a launcher for a app called Skinner. But I keep getting this annoying message. My Code is:
Outfile ..\..\SkinnerPortable.exe
Name "Skinner Portable"
Icon "..\..\App\AppInfo\appicon2.ico"
SilentInstall Silent
AutoCloseWindow True
RequestExecutionLevel user
!include "FileFunc.nsh"
!include "ReplaceInFile.nsh"
!include "Registry.nsh"
!insertmacro GetRoot
Var APPDIR
Var SETDIR
Section Main
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "SkinnerPortable"2) i .r1 ?e'
Pop $0
StrCmp $0 0 DefineVariables TheEnd
DefineVariables:
StrCpy $APPDIR "$EXEDIR\App\Skinner"
StrCpy $SETDIR "$EXEDIR\Data\Settings"
CheckINI:
IfFileExists "$SETDIR\SkinnerPortable.ini" CheckReg
CopyFiles "$EXEDIR\App\DefaultData" "$SETDIR\SkinnerPortable.ini"
CheckReg:
IfFileExists "$SETDIR\Skinner.reg" "" Splash
${GetRoot} $EXEDIR $0
ReadINIStr $1 "$SETDIR\Skinner.ini" "Settings" "LastDriveLetter"
${ReplaceInFile} "$SETDIR\Skinner.reg" "$1\" "$0\"
${registry::RestoreKey} "$SETDIR\Skinner.reg" $R0
Splash:
ReadINIStr $0 "$SETDIR\SkinnerPortable.ini" "Settings" "DisplaySplash"
StrCpy $0 "false" LaunchApp
newadvsplash::show /NOUNLOAD 1200 0 0 -1 /L "$EXEDIR\Other\Source\skinner portable.jpg"
LaunchApp:
SetOutPath "$APPDIR"
ExecWait "$APPDIR\skinner.exe"
SaveRegKeys:
${registry::SaveKey} "HKEY_CURRENT_USER\Software\Zhorn" "$SETDIR\Skinner.reg" "" $R0
TheEnd:
newadvsplash::stop /WAIT
SectionEndAnd the error is:
Processed 1 file, writing output: Adding plug-ins initializing function... Done! Error: resolving install function "StrReplace" in function "RIF" Note: uninstall functions must begin with "un.", and install functions must not Error - aborting creation process
First you have an error here -
that should probably be -
instead.
Second, you need to include StrRep.nsh as well when using ReplaceInFile. Here it is in case you don't have it -
; StrReplace ; Replaces all ocurrences of a given needle within a haystack with another string ; Written by dandaman32 Var STR_REPLACE_VAR_0 Var STR_REPLACE_VAR_1 Var STR_REPLACE_VAR_2 Var STR_REPLACE_VAR_3 Var STR_REPLACE_VAR_4 Var STR_REPLACE_VAR_5 Var STR_REPLACE_VAR_6 Var STR_REPLACE_VAR_7 Var STR_REPLACE_VAR_8 Function StrReplace Exch $STR_REPLACE_VAR_2 Exch 1 Exch $STR_REPLACE_VAR_1 Exch 2 Exch $STR_REPLACE_VAR_0 StrCpy $STR_REPLACE_VAR_3 -1 StrLen $STR_REPLACE_VAR_4 $STR_REPLACE_VAR_1 StrLen $STR_REPLACE_VAR_6 $STR_REPLACE_VAR_0 loop: IntOp $STR_REPLACE_VAR_3 $STR_REPLACE_VAR_3 + 1 StrCpy $STR_REPLACE_VAR_5 $STR_REPLACE_VAR_0 $STR_REPLACE_VAR_4 $STR_REPLACE_VAR_3 StrCmp $STR_REPLACE_VAR_5 $STR_REPLACE_VAR_1 found StrCmp $STR_REPLACE_VAR_3 $STR_REPLACE_VAR_6 done Goto loop found: StrCpy $STR_REPLACE_VAR_5 $STR_REPLACE_VAR_0 $STR_REPLACE_VAR_3 IntOp $STR_REPLACE_VAR_8 $STR_REPLACE_VAR_3 + $STR_REPLACE_VAR_4 StrCpy $STR_REPLACE_VAR_7 $STR_REPLACE_VAR_0 "" $STR_REPLACE_VAR_8 StrCpy $STR_REPLACE_VAR_0 $STR_REPLACE_VAR_5$STR_REPLACE_VAR_2$STR_REPLACE_VAR_7 StrLen $STR_REPLACE_VAR_6 $STR_REPLACE_VAR_0 Goto loop done: Pop $STR_REPLACE_VAR_1 ; Prevent "invalid opcode" errors and keep the Pop $STR_REPLACE_VAR_1 ; stack as it was before the function was called Exch $STR_REPLACE_VAR_0 FunctionEnd !macro _strReplaceConstructor OUT NEEDLE NEEDLE2 HAYSTACK Push "${HAYSTACK}" Push "${NEEDLE}" Push "${NEEDLE2}" Call StrReplace Pop "${OUT}" !macroend !define StrReplace '!insertmacro "_strReplaceConstructor"'I don't get system::call .
Insert original signature here with Greasemonkey Script.
Allows you to check if you app is currently running. And if it is to then not start again.
Release Team Member
System::Call allows you to call functions in other DLLs, like Windows API functions. This particular function, CreateMutex, creates....a mutex. I don't fully understand the programming concept, but here's what I got -
In a multi-threaded process, it's bad to have more than one thread access the same piece of data at the same time. This can lead to corruption and inconsistent states. So a program can create a mutex (mutual exclusion). Only one thread can access a mutual exclusion at a time, therefore alleviating the problem.
As it relates to our purposes, the ERROR_ALREADY_EXISTS flag is returned by GetLastError() if a program tries to create a named mutex that already exists, ie the second time the launcher is run. The System::Call.... command creates this mutex, then calls GetLastError() and puts the value on the NSIS stack. We get this value with the "Pop $0" command. If this value is non-zero, then the mutex already exists and we know our launcher is being run a second time.
Why do it this way? Becuase if we just check for the launcher's process name, it will always return true, even the first time it's run.
Hope that makes a little sense.
Also the process name will change if someone renames the launcher.
I got it working perfectly now.
Release Team Member