/************************************************************************** Name: ...Portable Version: Website: Written for: NSIS 2.17 or higher (last tested with 2.26) Required plugins: NewAdvSplash, Registry, FindProcDLL License: Copyright? 2007 by More informations about installation, directory structure etc. could be found at the very end of this file or in the accompanying readme.txt Portable application template created 2007 by Karl Loncarek, version 1.9.1 - 2007/05/05 Modified by Steve Babineau 2007/05/06 babineau@gmail.com http://thebabineaus.com/software/PAT/PAT_sab.nsi The license of the template is a two clause BSD-style license. It could be found a the end of this file or in the accompanying readme.txt. **************************************************************************/ ; ########################################################################## ; # Change the following constants depending on the application you want to make portable ; ########################################################################## ; ---- Basic info !define AUTHOR "Steve Babineau" ; your name !define APP "" ; insert application name, e.g. "TestApp" !define VER "1.0.0.0" ; insert version of launcher !define EXE "" ; insert application exe name to run, e.g. "testapp.exe" ; ---- Application-specific configuration !define ENVUSERVARS "" ; Add environment variables seperated by "||" in this format: ; "VARIABLE1=value||VARIABLE2=value||VARIABLE3+=value" ; = means to set the variable to the value ; += means to prefix the variable with the value (in the case of PATH, for example) !define REGKEYS "" ; insert regkeys to use separated by "||", comment out, ; when not used, e.g. HKCU\Software, maximum of 1024 characters !define SETTINGSFILES "" ; insert settings files to use separated by "||" as stored on the host computer, ; e.g. "$WINDIR\TEST.INI", comment out, when not used !define SETTINGSDIRS "" ; insert settings directories to use separated by "||" as stored on the host ; computer, e.g. "$PROFILE\TEST", comment out, when not used !define ADMINREQUIRED "FALSE" ; Require local administrator rights to run the launcher, necessary when writing ; to e.g. HKLM registry key. If not required comment out !define REDIRECTUSERPROFILE "FALSE" ; Redirect Userprofile folder, comment out when your application calls other ; programs, i.e. to disable automatic redirection. Default value "TRUE". !define INSTALLSOURCES "TRUE" ; When "TRUE" a launcher is created that contains the sources and copies them ; into the appropriate folder if they do not exist yet. !define REGFILE "" ; insert reg4 filename to replace vars with. Will output to RegKey# when done. ; Comment out when not used. !define REGISTERDLLS "" ; DLLs to register before loading application then unload when done, separated by "||". ; If you don't need it, comment it out. NOTE: All DLL paths are relative to ; "$EXEDIR\App\${APP}" ; ---- Normally you do not have to change these !define PNAME "${APP}Portable" ; format of portable name (dirs and filenames) !define ICON "${PNAME}.ico" ; comment this line out when default icon should be used !define SPLASHIMAGE "${PNAME}.jpg" ; comment this line out when no splashscreen image should be used !define INI "${PNAME}.ini" ; could be changed when settings for multiple applications should be stored in one INI file ; ########################################################################## ; # Normally no need to change anything after this point (only for intermediate/advanced users!) ; ########################################################################## ; ************************************************************************** ; * Compiler Flags (to reduce executable size, saves some bytes) ; ************************************************************************** SetDatablockOptimize on SetCompress force SetCompressor /SOLID /FINAL lzma ; ************************************************************************** ; * Includes ; ************************************************************************** !ifdef REGKEYS !if ! "${REGKEYS}" == "" !include "Registry.nsh" ; add registry manipulation macros, not included to NSIS by default !define REGINCLUDE "" !endif !endif !ifdef REGFILE !if ! "${REGFILE}" == "" !ifndef REGINCLUDE !include "Registry.nsh" !endif !include "TextReplace.nsh" ; Add text file manipulation macros !include "TextFunc.nsh" !insertmacro LineFind !insertmacro TrimNewLines !endif !endif !include "WordFunc.nsh" ; add header for word manipulation !insertmacro "WordFind" ; add function for splitting strings !include "FileFunc.nsh" ; add header for file manipulation !insertmacro "GetParameters" ; add function for retrieving command line parameters !define VAR_R0 10 ;$R0 - needed for dialogs ; ************************************************************************** ; * Runtime Switches ; ************************************************************************** CRCCheck On ; do CRC check on launcher before start ("Off" for later EXE compression) WindowIcon Off ; show no icon of the launcher SilentInstall Silent ; start as launcher, not as installer AutoCloseWindow True ; automatically close when finished ; ************************************************************************** ; * Define working variables ; ************************************************************************** Var SPLASHSCREEN ; holds the information whether the splash screen should be shown, default "enabled" Var PROGRAMEXE ; holds the name of the EXE file to launch Var PROGRAMDIR ; holds the path to the above EXE file Var PROGRAMPARMS ; holds some additional parameters when launching the EXE Var DATADIR ; holds the path to the location where all the settings should be saved Var REGLICENSEKEY ; Holds a registration license key, if there is one. Var REGLICENSECOMPANY ; Holds a registration company name, if there is one Var REGLICENSENAME ; Holds a registration name, if there is one. Var INIFILE ; holds the complete path to the found INI file Var SECONDLAUNCH ; holds whether the EXE may be called a second time !if "${INSTALLSOURCES}" = "TRUE" Var SOURCEDIR ; holds the path to the location where the launcher source is stored Var EXTRACTSOURCES ; holds whether the sources should be extracted eevry time !endif !ifdef REGFILE !if ! "${REGFILE}" == "" Var REGOUT ; holds Registry Search/Replace Count (or error value) Var MODIFIED_STR ; Holds return val from StrRep function Var REGDESTPATH ; Text replacement variables Var REGDATAPATH ; Var REGUSERDATAPATH ; Var REGEXETPATH ; Var REGCOMPUTERNAME ; Var REGCOMPUTERNAMEFULL ; Var REGUSERSID ; SID of the currently logged in user Var CURREGFILE ; Holds the current Registry file that is being worked on Var CURBACKUPREGFILE ; Holds the current backup Registry file Var CURCONDENSEDREGFILE ; Holds the current condensed registry file !endif !endif ; ************************************************************************** ; * Set basic information ; ************************************************************************** Name "${APP} Portable" !ifdef ICON Icon "${ICON}" !endif Caption "${APP} Portable - ${VER}" OutFile "${PNAME}.exe" ; ************************************************************************** ; * Set version information ; ************************************************************************** LoadLanguageFile "${NSISDIR}\Contrib\Language files\English.nlf" VIProductVersion "${Ver}" VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "${APP} Portable" VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "Allow ${APP} to be run from a removeable drive. This launcher is based on the Portable Application Template created by Klonk (Karl Loncarek)." VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Launcher created by ${AUTHOR}" VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "by ${AUTHOR}" VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "${APP} Portable" VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${VER}" VIAddVersionKey /LANG=${LANG_ENGLISH} "OriginalFilename" "${PNAME}.exe" ; ************************************************************************** ; * Main section ; ************************************************************************** Section "Main" Call InitINI ; apply INI settings Call InitVars ; set default variable values when no INI is used Call InitInstall ; installs additional files, e.g. sources or INI-files Call Init ; other initalizations before any registry, folder, or fileoperations are done Call InitEnv ; Initialize the environment, setting any ENV-VARS Call InitReg ; backup current reg, apply portable reg Call InitRegFiles ; backup current reg, apply portable reg Call InitFiles ; rename current files, apply portable files Call InitFolders ; rename current folders, apply portable folders Call InitDlls ; Register DLLs Call RunApp ; --> run app <-- Call CleanDlls ; Unregister DLLs Call CleanFolders ; copy portable folders, delete portable folders, restore original folders Call CleanFiles ; copy portable files, delete portable files, restore original files Call CleanRegFiles ; copy reg, restore original reg Call CleanReg ; copy reg, restore original reg Call Clean ; Absolute last things to do SectionEnd ; ************************************************************************** ; * Macro: Helps calling the StrRep function ; ************************************************************************** !macro ReplaceSubStr OLD_STR SUB_STR REPLACEMENT_STR Push "${OLD_STR}" ;String to do replacement in (haystack) Push "${SUB_STR}" ;String to replace (needle) Push "${REPLACEMENT_STR}" ; Replacement Call StrRep Pop $R0 ;result StrCpy $MODIFIED_STR $R0 !macroend ; ************************************************************************** ; * Function: Search for INI file, read it, and set variables when necessary ; ************************************************************************** Function InitINI ; -------------------------------------------------------------------------- ; Empty variables ; -------------------------------------------------------------------------- StrCpy "$PROGRAMDIR" "" StrCpy "$DATADIR" "" StrCpy "$PROGRAMEXE" "" StrCpy "$SPLASHSCREEN" "" StrCpy "$PROGRAMPARMS" "" !if "${INSTALLSOURCES}" = "TRUE" StrCpy "$EXTRACTSOURCES" "TRUE" !endif ; -------------------------------------------------------------------------- ; Check whether an INI file exists, set variable pointing to it ; -------------------------------------------------------------------------- IfFileExists "$EXEDIR\${INI}" "" CheckPortableINI StrCpy "$INIFILE" "$EXEDIR\${INI}" Goto ReadINIFile CheckPortableINI: IfFileExists "$EXEDIR\${PNAME}\${INI}" "" CheckPortableAppsINI StrCpy "$INIFILE" "$EXEDIR\${PNAME}\${INI}" Goto ReadINIFile CheckPortableAppsINI: IfFileExists "$EXEDIR\PortableApps\${PNAME}\${INI}" "" CheckAppsINI StrCpy "$INIFILE" "$EXEDIR\PortableApps\${PNAME}\${INI}" Goto ReadINIFile CheckAppsINI: IfFileExists "$EXEDIR\Apps\${PNAME}\${INI}" "" CheckDataINI StrCpy "$INIFILE" "$EXEDIR\Apps\${PNAME}\${INI}" Goto ReadINIFile CheckDataINI: IfFileExists "$EXEDIR\Data\${PNAME}\${INI}" "" InitINIEnd StrCpy "$INIFILE" "$EXEDIR\Data\${PNAME}\${INI}" Goto ReadINIFile Goto InitINIEnd ; -------------------------------------------------------------------------- ; Read content of the INI file, save only used ; -------------------------------------------------------------------------- ReadINIFile: ReadINIStr $0 "$INIFILE" "${PNAME}" "ProgramDirectory" StrCmp $0 "" INIDataDirectory ; if emtpy check next setting StrCpy "$PROGRAMDIR" "$EXEDIR\$0" ; save program directory INIDataDirectory: ReadINIStr $0 "$INIFILE" "${PNAME}" "DataDirectory" StrCmp $0 "" INIProgramExecutable ; if empty retrieve correct setting StrCpy "$DATADIR" "$EXEDIR\$0" ; save data directory INIProgramExecutable: ReadINIStr $0 "$INIFILE" "${PNAME}" "ProgramExecutable" StrCmp $0 "" INISplashScreen ; if emtpy use default StrCpy "$PROGRAMEXE" "$0" ; save .exe name INISplashScreen: ReadINIStr $0 "$INIFILE" "${PNAME}" "SplashScreen" StrCmp $0 "" INIProgramParameters ; check whether variable splashscreen was empty StrCpy "$SPLASHSCREEN" "$0" ; save state of splashscreen display INIProgramParameters: ReadINIStr $0 "$INIFILE" "${PNAME}" "ProgramParameters" StrCmp $0 "" +2 ; if emtpy use default StrCpy "$PROGRAMPARMS" "$0" ; save additional program parameters !if "${INSTALLSOURCES}" = "TRUE" ReadINIStr $0 "$INIFILE" "${PNAME}" "ExtractSources" StrCmp $0 "" INIRegLicenseCompany ; check whether variable exctractsources was empty StrCpy "$EXTRACTSOURCES" "$0" ; save whether sources should be extracted or not !endif INIRegLicenseCompany: ReadINIStr $0 "$INIFILE" "${PNAME}" "RegLicenseCompany" StrCmp $0 "" INIRegLicenseName ; if emtpy use default StrCpy "$RegLicenseCompany" "$0" ; Registration license company INIRegLicenseName: ReadINIStr $0 "$INIFILE" "${PNAME}" "RegLicenseName" StrCmp $0 "" INIRegLicenseKey ; if emtpy use default StrCpy "$RegLicenseName" "$0" ; Registration license name INIRegLicenseKey: ReadINIStr $0 "$INIFILE" "${PNAME}" "RegLicenseKey" StrCmp $0 "" InitINIEnd ; if emtpy use default StrCpy "$RegLicenseKey" "$0" ; Save registration license key Goto InitINIEnd ; finished reading the INI file ; -------------------------------------------------------------------------- ; No allowed pathconfiguration was found ; -------------------------------------------------------------------------- InitINIEnd: ;simly the end of the function FunctionEnd ; ************************************************************************** ; * Function: Fill used variables with default values, if not set already ; ************************************************************************** Function InitVars ; -------------------------------------------------------------------------- ; Set default values for variables, when not set already ; -------------------------------------------------------------------------- StrCmp "$SPLASHSCREEN" "" 0 InitProgramEXE StrCpy "$SPLASHSCREEN" "enabled" ; enable splashscreen InitProgramEXE: StrCmp "$PROGRAMEXE" "" 0 InitProgramDIR StrCpy "$PROGRAMEXE" "${EXE}" ; use default setting InitProgramDIR: StrCmp "$PROGRAMDIR" "" 0 InitVarEnd ; no programdir set before (by INI file) ; -------------------------------------------------------------------------- ; Predefine default directory structure ; -------------------------------------------------------------------------- StrCpy "$DATADIR" "$EXEDIR\Data" StrCpy "$PROGRAMDIR" "$EXEDIR\App\${APP}" !if "${INSTALLSOURCES}" = "TRUE" StrCpy "$SOURCEDIR" "$EXEDIR\Other\${PNAME}Sources" !endif ; -------------------------------------------------------------------------- ; Check which directory configuration is used and set variables accordingly ; -------------------------------------------------------------------------- IfFileExists "$EXEDIR\${PNAME}\App\${APP}\*.*" 0 CheckPortableAppsDIR StrCpy "$PROGRAMDIR" "$EXEDIR\${PNAME}\App\${APP}" StrCpy "$DATADIR" "$EXEDIR\${PNAME}\Data" !if "${INSTALLSOURCES}" = "TRUE" StrCpy "$SOURCEDIR" "$EXEDIR\${PNAME}\Other\${PNAME}Sources" !endif Goto InitVarEnd CheckPortableAppsDIR: IfFileExists "$EXEDIR\PortableApps\${PNAME}\App\${APP}\*.*" 0 CheckAppsDIR StrCpy "$PROGRAMDIR" "$EXEDIR\PortableApps\${PNAME}\App\${APP}" StrCpy "$DATADIR" "$EXEDIR\PortableApps\${PNAME}\Data" !if "${INSTALLSOURCES}" = "TRUE" StrCpy "$SOURCEDIR" "$EXEDIR\PortableApps\${PNAME}\Other\${PNAME}Sources" !endif Goto InitVarEnd CheckAppsDIR: IfFileExists "$EXEDIR\Apps\${PNAME}\${APP}\*.*" 0 InitDataDIR StrCpy "$PROGRAMDIR" "$EXEDIR\Apps\${PNAME}\${APP}" StrCpy "$DATADIR" "$EXEDIR\Data\${PNAME}" !if "${INSTALLSOURCES}" = "TRUE" StrCpy "$SOURCEDIR" "$EXEDIR\Other\${PNAME}\${PNAME}Sources" !endif Goto InitVarEnd ; -------------------------------------------------------------------------- ; Check whether DataDirectory was set in the INI, only called, when ProgramDirectory was set in INI ; -------------------------------------------------------------------------- InitDataDIR: StrCmp "$DATADIR" "" 0 InitVarEnd MessageBox MB_OK|MB_ICONEXCLAMATION `"DataDirectory" was not set in INI file. Please check your configuration!` Abort ; terminate launcher InitVarEnd: FunctionEnd ; ************************************************************************** ; * Function: Installs additional files, e.g. launcher sources, INI files etc. ; ************************************************************************** Function InitInstall ; -------------------------------------------------------------------------- ; Install source files, i.e. copy them to sources folder ; -------------------------------------------------------------------------- !if "${INSTALLSOURCES}" = "TRUE" StrCmp "$EXTRACTSOURCES" "TRUE" 0 InitInstallEnd ; if variable correctly set in INI or by default extract SetOutPath $SOURCEDIR !ifdef SPLASHIMAGE File "${SPLASHIMAGE}" !endif !ifdef ICON File "${ICON}" !endif File "${__FILE__}" File /nonfatal "readme.txt" ; will give a warning when it does not exist !ifdef REGFILE !if ! "${REGFILE}" == "" SetOutPath "$EXEDIR\Data\Registry\" File /nonfatal "${REGFILE}" !endif !endif InitInstallEnd: !endif FunctionEnd ; ************************************************************************** ; * Function: Other initializations done before any registry, folder, or file operations ; ************************************************************************** Function Init ; -------------------------------------------------------------------------- ; Create folders that do not exist yet ; -------------------------------------------------------------------------- IfFileExists "$DATADIR\*.*" +2 CreateDirectory "$DATADIR" ; create data directory IfFileExists "$PROGRAMDIR\*.*" +2 CreateDirectory "$PROGRAMDIR" ; create program directory ; -------------------------------------------------------------------------- ; Check whether EXE exists, if not copy installed application into portable folder ; -------------------------------------------------------------------------- IfFileExists "$PROGRAMDIR\$PROGRAMEXE" FoundEXE ; executable was not found in portable folder, ask to copy local installation MessageBox MB_YESNO|MB_ICONEXCLAMATION `$PROGRAMEXE was not found.$\nDo you want to copy your local installation into your portable applications directory? (This could take some time)$\n$\nWhen you select "NO" this launcher will be terminated. In this case, please copy the necessary files yourself.` IDYES +2 Abort ; terminate launcher Dialogs::Folder "Select installation folder of ${APP} " 'Select the main folder where you installed "${APP}" on your harddrive:' "$PROGRAMFILES" ${VAR_R0} CopyFiles "$R0\*.*" "$PROGRAMDIR" MessageBox MB_YESNO|MB_ICONINFORMATION "Copying is finished now. You could now (or later) delete unneeded files.$\nDo you want to launch ${PNAME}?" IDYES +2 Abort ; terminate launcher ; Program executable not where expected IfFileExists "$PROGRAMDIR\$PROGRAMEXE" FoundEXE MessageBox MB_OK|MB_ICONEXCLAMATION `$PROGRAMEXE was not found. Please check your configuration!` Abort ; terminate Launcher ; -------------------------------------------------------------------------- ; Check whether EXE is launched a second time ; -------------------------------------------------------------------------- FoundEXE: ; Check if already running and set variable FindProcDLL::FindProc "$PROGRAMEXE" StrCmp $R0 "1" "" EndEXE StrCpy $SECONDLAUNCH "true" EndEXE: ; -------------------------------------------------------------------------- ; Check whether current user is admin (only when required) ; -------------------------------------------------------------------------- !if "${ADMINREQUIRED}" = "TRUE" userInfo::getAccountType pop $0 StrCmp $0 "Admin" InitAdminEnd messageBox MB_OK|MB_ICONEXCLAMATION "You must be logged in as a local administrator for this launcher to work!" Abort InitAdminEnd: !endif ; -------------------------------------------------------------------------- ; Display splashscreen when available ; -------------------------------------------------------------------------- !ifdef SPLASHIMAGE StrCmp "$SPLASHSCREEN" "enabled" 0 NoSplash InitPluginsDir File /oname=$PLUGINSDIR\splash.jpg "${SPLASHIMAGE}" newadvsplash::show /NOUNLOAD 2500 200 200 -1 /L $PLUGINSDIR\splash.jpg NoSplash: !endif ; -------------------------------------------------------------------------- ; Temporarily redirect USERPROFILE folder (application should write own data into that directory ; -------------------------------------------------------------------------- !if "${REDIRECTUSERPROFILE}" = "TRUE" IfFileExists "$DATADIR\UserProfile\*.*" +2 CreateDirectory "$DATADIR\UserProfile" ; create directory for portable user profile System::Call 'Kernel32::SetEnvironmentVariableA(t, t) i("USERPROFILE", "$DATADIR\UserProfile").r0' ; set new user profile folder StrCmp $0 0 ProfileError Goto ProfileDone ProfileError: MessageBox MB_ICONEXCLAMATION|MB_OK "Can't set environment variable for new Userprofile!$\nLauncher will be terminated." Abort ProfileDone: !endif FunctionEnd ; ************************************************************************** ; * Function: Backup registry keys, apply portable registry keys ; ************************************************************************** Function InitEnv !ifdef ENVUSERVARS !if ! "${ENVUSERVARS}" == "" StrCpy "$R0" "${ENVUSERVARS}" StrCpy "$R8" "0" InitEnvLoop: StrCmp "$R0" "" InitEnvEnd IntOp $R8 $R8 + 1 ${WordFind} "$R0" "||" "+01" $R9 ${WordFind} "$R0" "||" "+02*}" $R0 StrCmp "$R0" "$R9" LastInitEnvLoop Goto DoInitEnvWork LastInitEnvLoop: StrCpy "$R0" "" DoInitEnvWork: ${WordFind} "$R9" "+=" "E+01" $R1 IfErrors CheckEquals FoundAddVar CheckEquals: ${WordFind} "$R9" "=" "E+01" $R1 IfErrors 0 FoundVar StrCmp $R1 "1" InitEnvLoop ;delimiter not found. Don't know what else to do FoundAddVar: Push $R9 Push "+=" Call StrTok Pop $R4 ; Variable Pop $R5 ; Value StrCpy $R5 $R5 "" 1 ReadEnvStr $R6 "$R4" StrCpy $R5 "$R5;$R6" System::Call 'Kernel32::SetEnvironmentVariableA(t, t) i("$R4", R5).r2' Goto InitEnvLoop FoundVar: Push $R9 Push "+=" Call StrTok Pop $R4 ; Variable Pop $R5 ; Value System::Call 'Kernel32::SetEnvironmentVariableA(t, t) i("$R4", R5).r2' Goto InitEnvLoop InitEnvEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Backup registry keys, apply portable registry keys ; ************************************************************************** Function InitReg !ifdef REGKEYS !if ! "${REGKEYS}" = "" ; only do stuff if constant is not empty StrCmp $SECONDLAUNCH "true" InitRegEnd ; do not do anything if launched a second time IfFileExists "$DATADIR\Registry\*.*" +2 CreateDirectory "$DATADIR\Registry" ; create directory for portable registry entries, if it does not exist IfFileExists "$DATADIR\RegistryBackup\*.*" +2 CreateDirectory "$DATADIR\RegistryBackup" ; create backup directory, if it does not exist StrCpy "$R0" "${REGKEYS}" ; copy constant to working variable StrCpy "$R8" "0" ; reset variable InitRegLoop: ; -------------------------------------------------------------------------- ; Get single parameter out of list, i.e. obtain next single registry key ; -------------------------------------------------------------------------- StrCmp "$R0" "" InitRegEnd ; do not do registry parsing, when no keys given anymore IntOp $R8 $R8 + 1 ; increase counter ${WordFind} "$R0" "||" "+01" $R9 ; save first parameter to register ${WordFind} "$R0" "||" "+02*}" $R0 ; remove first part from saved value StrCmp "$R0" "$R9" LastInitRegLoop ; if register values are identical -> no more delimiters Goto DoInitRegWork ; do not delete value list LastInitRegLoop: StrCpy "$R0" "" ; if no delimiter available anymore empty working variable DoInitRegWork: ; -------------------------------------------------------------------------- ; Backup registry key ; -------------------------------------------------------------------------- ${registry::KeyExists} "$R9" $R7 ; check whether registry key exists StrCmp "$R7" "0" 0 InitRegApply ; registry key does not exist, do not save anything ${registry::SaveKey} "$R9" "$DATADIR\RegistryBackup\RegKey$R8.reg" "/G=1" $R7 ; Backup registry key ; -------------------------------------------------------------------------- ; Apply portable registry key, delete existing key at same time ; -------------------------------------------------------------------------- InitRegApply: IfFileExists "$DATADIR\Registry\RegKey$R8.reg" 0 InitRegLoop ; only apply if a registry file exists ${registry::RestoreKey} "$DATADIR\Registry\RegKey$R8.reg" $R7 ; Restore saved registry key Goto InitRegLoop InitRegEnd: !endif !endif FunctionEnd Function InitRegFiles !ifdef REGFILE !if ! "${REGFILE}" == "" StrCpy "$R8" "0" ; reset variable StrCpy "$R0" "${REGFILE}" ; copy constant to working variable ; First, get a list of all registry keys to be imported, and save them for the unloading later ; Loops through all $DATADIR\Registry\RegKey#.reg files InitRegFilesLoop: StrCmp "$R0" "" InitRegFilesEnd ; do not do registry parsing, when no keys given anymore IntOp $R8 $R8 + 1 ; increase counter ${WordFind} "$R0" "||" "+01" $R9 ; save first parameter to register ${WordFind} "$R0" "||" "+02*}" $R0 ; remove first part from saved value StrCmp "$R0" "$R9" LastInitRegFilesLoop ; if register values are identical -> no more delimiters Goto DoInitRegFilesWork ; do not delete value list LastInitRegFilesLoop: StrCpy "$R0" "" ; if no delimiter available anymore empty working variable DoInitRegFilesWork: IfFileExists "$DATADIR\Registry\$R9" 0 InitRegFilesEnd StrCpy "$CURREGFILE" "$DATADIR\Registry\RegKey$R8.reg" StrCpy "$CURBACKUPREGFILE" "$DATADIR\RegistryBackup\RegKey$R8.reg" StrCpy "$CURCONDENSEDREGFILE" "$CURREGFILE.txt" CopyFiles "$DATADIR\Registry\$R9" "$CURREGFILE" Call ReplaceRegVars IfFileExists $CURCONDENSEDREGFILE SkipPrepareRegFile Call BuildRegKeyList Call CollapseRegKeyList SkipPrepareRegFile: ; Great! Now backup the current values, then import the new ones! Call BackupRegKeys Call ImportRegKeysFromFile Goto InitRegFilesLoop InitRegFilesEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Backs up a list of registry keys, reading from $CURCONDENSEDREGFILE and putting them in $CURBACKUPREGFILE ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function BackupRegKeys Delete $CURBACKUPREGFILE ; Delete the backup file, if it exists ${LineFind} "$CURCONDENSEDREGFILE" /NUL "1:-1" .BackupRegKey Sleep 500 FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Gets one registry key and returns it to BackupRegKeys function ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function .BackupRegKey ${registry::KeyExists} "$R9" $R0 StrCmp "$R0" "0" 0 DontBackup ${registry::SaveKey} "$R9" "$CURBACKUPREGFILE" "/G=1 /A=1" $R0 ; Backup registry key DontBackup: FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Imports Registry keys from $CURREGFILE ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function ImportRegKeysFromFile IfFileExists "$CURREGFILE" 0 DoneImportingKeys ; only apply if a registry file exists ${registry::RestoreKey} "$CURREGFILE" $R0 ; Restore saved registry key Sleep 500 ; Give Regedit some more time to import the registry DoneImportingKeys: FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Takes the tmp Reg key list and collapses it to just the base keys ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function CollapseRegKeyList Var /GLOBAL MatchCount1 Var /GLOBAL MatchCount2 Var /GLOBAL StrLen1 Var /GLOBAL SubStr1 CopyFiles "$CURREGFILE.tmp.txt" "$CURREGFILE.tmp1.txt" ${LineFind} "$CURREGFILE.tmp.txt" $CURCONDENSEDREGFILE "1:-1" .CheckIfRegKeyIsBase Delete "$CURREGFILE.tmp1.txt" FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Callback for CollapseRegKeyList. Checks to see if the base has already been mentioned. If so, dont write it ; * Effectively weeding out the sub keys. ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function .CheckIfRegKeyIsBase ; $R4 = handle to $CURCONDENSEDREGFILE ; $R9 = Current line ; $R0 = handle to $CURREGFILE.tmp1.txt (a copy of $CURREGFILE.tmp.txt) ; $R3 = Length of current line from $CURREGFILE.tmp.txt ${TrimNewLines} "$R9" $R9 ; see if no other line is a substring of $R9 StrLen $R3 $R9 ; length of current line from $CURREGFILE.tmp.txt StrCpy $MatchCount1 0 StrCpy $MatchCount2 0 FileOpen $R0 "$CURREGFILE.tmp1.txt" "r" Loop: ClearErrors FileRead $R0 $R2 IfErrors Exit ${TrimNewLines} "$R2" $R2 ; no other line is a substring of $R9 StrLen $StrLen1 $R2 StrCpy $SubStr1 $R9 $StrLen1 StrCmp $SubStr1 $R2 0 +2 IntOp $MatchCount2 $MatchCount2 + 1 Goto Loop Exit: FileClose $R0 IntCmp $MatchCount2 1 0 +3 +3 StrCpy $R9 "$R9$\r$\n" Goto DoneCheckingRegKey StrCpy $0 SkipWrite DoneCheckingRegKey: Push $0 FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Takes a RegEdit4 file and parses out just the keys to a text file. ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function BuildRegKeyList ${LineFind} "$CURREGFILE" "$CURREGFILE.tmp.txt" "1:-1" .CheckIfRegKey FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Callback for GetRegKeyList. Checks to see if it's a regkey. If so, output it to the file. ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function .CheckIfRegKey ${TrimNewLines} "$R9" $R9 StrLen $R0 $R9 StrCmp $R0 0 NotRegKey ; If it's a blank line, skip right to not writing it StrCpy $R0 $R9 1 ; Get the first character StrCmp $R0 "[" 0 NotRegKey StrCpy $R0 $R9 "" -1 ; Get the last character StrCmp $R0 "]" 0 NotRegKey StrCpy $R9 $R9 "" 1 ; everything but the first character StrCpy $R9 $R9 -1 ; everything but the last character ( ] ) StrCpy $R9 '$R9$\r$\n' Goto DoneChecking NotRegKey: StrCpy $0 SkipWrite DoneChecking: Push $0 FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Rename current files, apply portable files ; ************************************************************************** Function InitFiles !ifdef SETTINGSFILES !if ! "${SETTINGSFILES}" = "" ; only do stuff if constant is not empty StrCmp $SECONDLAUNCH "true" InitFilesEnd ; do not do anything if launched a second time IfFileExists "$DATADIR\Settings\*.*" +2 CreateDirectory "$DATADIR\Settings" ; create directory for portable configuration files, if it does not exist StrCpy "$R0" "${SETTINGSFILES}" ; copy constant to working variable StrCpy "$R8" "0" ; reset variable InitFilesLoop: ; -------------------------------------------------------------------------- ; Get single parameter out of list, i.e. obtain next single settings file ; -------------------------------------------------------------------------- StrCmp "$R0" "" InitFilesEnd ; do not do file operations, when no files given anymore IntOp $R8 $R8 + 1 ; increase counter ${WordFind} "$R0" "||" "+01" $R9 ; save first parameter to register ${WordFind} "$R0" "||" "+02*}" $R0 ; remove first part from saved value StrCmp "$R0" "$R9" LastInitFilesLoop ; if register values are identical -> no more delimiters Goto DoInitFilesWork ; do not delete value list LastInitFilesLoop: StrCpy "$R0" "" ; if no delimiter available anymore empty working variable DoInitFilesWork: ; -------------------------------------------------------------------------- ; Delete backup file if it exists (otherwise rename won't work) ; -------------------------------------------------------------------------- IfFileExists "$R9-PortBak" 0 InitFilesBackup ; Tell user that backup files/folders already exist, YES - copy portable data, keep original backup, NO - delete backup file/folder MessageBox MB_ICONEXCLAMATION|MB_YESNOCANCEL "Backup file $\"R9-PortBak$\" already exists. Do you want to keep it?$\n$\nYES = simple copy portable data, keep backup of original file$\nNO = delete backup file, create new backup of actual file$\nCANCEL = exit launcher, and fix problem manually$\n$\nAttention: You will be asked for every found backup file." IDYES InitFilesApply IDNO InitFilesDelete ; CANCEL - exit launcher, fix problem Abort InitFilesDelete: Delete "$R9-PortBak" ; -------------------------------------------------------------------------- ; Backup file (in fact simply rename existing file) ; -------------------------------------------------------------------------- InitFilesBackup: IfFileExists "$R9" 0 InitFilesApply ; check whether file exists Rename "$R9" "$R9-PortBak" ; rename file for backup ; -------------------------------------------------------------------------- ; Apply portable settings file ; -------------------------------------------------------------------------- InitFilesApply: IfFileExists "$DATADIR\Settings\File$R8.dat" 0 InitFilesCopy ; only restore when available CopyFiles /SILENT "$DATADIR\Settings\File$R8.dat" "$R9" ; restore file Goto InitFilesLoop InitFilesCopy: CopyFiles /SILENT "$R9-PortBak" "$R9" ; copy existing settings file if no portable version exists Goto InitFilesLoop InitFilesEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Rename folder, apply portable folder ; ************************************************************************** Function InitFolders !ifdef SETTINGSDIRS !if ! "${SETTINGSDIRS}" = "" ; only do stuff if constant is not empty StrCmp $SECONDLAUNCH "true" InitFoldersEnd ; do not do anything if launched a second time IfFileExists "$DATADIR\Settings\*.*" +2 CreateDirectory "$DATADIR\Settings" ; create directory for portable configuration files, if it does not exist StrCpy "$R0" "${SETTINGSDIRS}" ; copy constant to working variable StrCpy "$R8" "0" ; reset variable InitFoldersLoop: ; -------------------------------------------------------------------------- ; Get single parameter out of list, i.e. obtain next single settings file ; -------------------------------------------------------------------------- StrCmp "$R0" "" InitFoldersEnd ; do not do file operations, when no directtories given anymore IntOp $R8 $R8 + 1 ; increase counter ${WordFind} "$R0" "||" "+01" $R9 ; save first parameter to register ${WordFind} "$R0" "||" "+02*}" $R0 ; remove first part from saved value StrCmp "$R0" "$R9" LastInitFoldersLoop ; if register values are identical -> no more delimiters Goto DoInitFoldersWork ; do not delete value list LastInitFoldersLoop: StrCpy "$R0" "" ; if no delimiter available anymore empty working variable DoInitFoldersWork: ; -------------------------------------------------------------------------- ; Delete backup folder if it exists (otherwise rename won't work) ; -------------------------------------------------------------------------- IfFileExists "$R9-PortBak\*.*" 0 InitFoldersBackup ; Tell user that backup files/folders already exist, YES - copy portable data, keep original backup, NO - delete backup file/folder MessageBox MB_ICONEXCLAMATION|MB_YESNOCANCEL "Backup folder $\"R9-PortBak$\" already exists. Do you want to keep it?$\n$\nYES = simple copy portable data, keep backup of original folder$\nNO = delete backup folder, create new backup of actual folder$\nCANCEL = exit launcher, and fix problem manually$\n$\nAttention: You will be asked for every found backup folder." IDYES InitFoldersApply IDNO InitFoldersDelete ; CANCEL - exit launcher, fix problem Abort InitFoldersDelete: RMDir "/r" "$R9-PortBak" ; delete folder ; -------------------------------------------------------------------------- ; Backup folder (in fact simply rename existing folder) ; -------------------------------------------------------------------------- InitFoldersBackup: IfFileExists "$R9\*.*" 0 InitFoldersApply ; check whether folder exists Rename "$R9" "$R9-PortBak" ; rename folder for backup ; -------------------------------------------------------------------------- ; Apply portable folder ; -------------------------------------------------------------------------- InitFoldersApply: IfFileExists "$DATADIR\Settings\Dir$R8.dat\*.*" 0 InitFoldersCopy ; check whether backup exists IfFileExists "$R9\*.*" +2 0 ; does target folder exist CreateDirectory "$R9" ; create target folder CopyFiles /SILENT "$DATADIR\Settings\Dir$R8.dat\*.*" "$R9" ; apply folder content Goto InitFoldersLoop InitFoldersCopy: CopyFiles /SILENT "$R9-PortBak\*.*" "$R9" ; copy existing settings folder if no portable vrsion exists Goto InitFoldersLoop InitFoldersEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Load DLLs ; ************************************************************************** Function InitDLLs !ifdef REGISTERDLLS !if ! "${REGISTERDLLS}" == "" StrCmp $SECONDLAUNCH "true" InitDllsEnd ; do not do anything if launched a second time StrCpy "$R0" "${REGISTERDLLS}" ; copy constant to working variable StrCpy "$R8" "0" ; reset variable InitDllsLoop: ; -------------------------------------------------------------------------- ; Get single parameter out of list, i.e. obtain next single settings file ; -------------------------------------------------------------------------- StrCmp "$R0" "" InitDllsEnd ; do not do file operations, when no directtories given anymore IntOp $R8 $R8 + 1 ; increase counter ${WordFind} "$R0" "||" "+01" $R9 ; save first parameter to register ${WordFind} "$R0" "||" "+02*}" $R0 ; remove first part from saved value StrCmp "$R0" "$R9" LastInitDllsLoop ; if register values are identical -> no more delimiters Goto DoInitDllsWork ; do not delete value list LastInitDllsLoop: StrCpy "$R0" "" ; if no delimiter available anymore empty working variable DoInitDllsWork: SetOutPath "$EXEDIR\App\${APP}" ExecWait "regsvr32.exe /s /i $\"$R9$\"" Goto InitDllsLoop InitDllsEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Run application ; ************************************************************************** Function RunApp ${GetParameters} $R0 ; obtain eventually provided commandline parameters StrCmp $SECONDLAUNCH "true" RunAppNoWait ; simply start if launched a second time ; -------------------------------------------------------------------------- ; Start program ; -------------------------------------------------------------------------- SetOutPath "$PROGRAMDIR" ExecWait "$PROGRAMDIR\$PROGRAMEXE $PROGRAMPARMS $R0" ; run program Goto RunAppEnd ; -------------------------------------------------------------------------- ; run application without waiting ; -------------------------------------------------------------------------- RunAppNoWait: Exec "$PROGRAMDIR\$PROGRAMEXE $PROGRAMPARMS $R0" ; run program RunAppEnd: FunctionEnd ; ************************************************************************** ; * Function: Copy portable folders, delete portable folders, restore original folders ; ************************************************************************** Function CleanFolders !ifdef SETTINGSDIRS !if ! "${SETTINGSDIRS}" = "" ; only do stuff if constant is not empty StrCmp $SECONDLAUNCH "true" CleanFoldersEnd ; do not do anything if launched a second time StrCpy "$R0" "${SETTINGSDIRS}" ; copy constant to working variable StrCpy "$R8" "0" ; reset variable CleanFoldersLoop: ; -------------------------------------------------------------------------- ; Get single parameter out of list, i.e. obtain next single settings file ; -------------------------------------------------------------------------- StrCmp "$R0" "" CleanFoldersEnd ; do not do file operations, when no directtories given anymore IntOp $R8 $R8 + 1 ; increase counter ${WordFind} "$R0" "||" "+01" $R9 ; save first parameter to register ${WordFind} "$R0" "||" "+02*}" $R0 ; remove first part from saved value StrCmp "$R0" "$R9" LastCleanFoldersLoop ; if register values are identical -> no more delimiters Goto DoCleanFoldersWork ; do not delete value list LastCleanFoldersLoop: StrCpy "$R0" "" ; if no delimiter available anymore empty working variable DoCleanFoldersWork: ; -------------------------------------------------------------------------- ; Copy actual folder to portable folder ; -------------------------------------------------------------------------- IfFileExists "$R9\*.*" 0 CleanFoldersRestore ; check whether source folder exists IfFileExists "$DATADIR\Settings\Dir$R8.dat" +2 0 ; does target folder exist? CreateDirectory "$DATADIR\Settings\Dir$R8.dat" ; create target folder CopyFiles /SILENT "$R9\*.*" "$DATADIR\Settings\Dir$R8.dat" ; backup folder ; -------------------------------------------------------------------------- ; Delete actual folder (with portable content) ; -------------------------------------------------------------------------- RMDir "/r" "$R9" ; delete directory ; -------------------------------------------------------------------------- ; Restore original folder ; -------------------------------------------------------------------------- CleanFoldersRestore: IfFileExists "$R9-PortBak\*.*" 0 CleanFoldersLoop ; check whether folder exists Rename "$R9-PortBak" "$R9"; rename folder back to original name Goto CleanFoldersLoop CleanFoldersEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Copy portable files, delete portable files restore original files ; ************************************************************************** Function CleanFiles !ifdef SETTINGSFILES !if ! "${SETTINGSFILES}" = "" ; only do stuff if constant is not empty StrCmp $SECONDLAUNCH "true" CleanFilesEnd ; do not do anything if launched a second time StrCpy "$R0" "${SETTINGSFILES}" ; copy constant to working variable StrCpy "$R8" "0" ; reset variable CleanFilesLoop: ; -------------------------------------------------------------------------- ; Get single parameter out of list, i.e. obtain next single settings file ; -------------------------------------------------------------------------- StrCmp "$R0" "" CleanFilesEnd ; do not do file operations, when no directtories given anymore IntOp $R8 $R8 + 1 ; increase counter ${WordFind} "$R0" "||" "+01" $R9 ; save first parameter to register ${WordFind} "$R0" "||" "+02*}" $R0 ; remove first part from saved value StrCmp "$R0" "$R9" LastCleanFilesLoop ; if register values are identical -> no more delimiters Goto DoCleanFilesWork ; do not delete value list LastCleanFilesLoop: StrCpy "$R0" "" ; if no delimiter available anymore empty working variable DoCleanFilesWork: ; -------------------------------------------------------------------------- ; Copy actual file to portable folder ; -------------------------------------------------------------------------- IfFileExists "$R9" 0 CleanFilesRestore ; check whether file exists CopyFiles /SILENT "$R9" "$DATADIR\Settings\File$R8.dat" ; backup file ; -------------------------------------------------------------------------- ; Delete actual file (with portable content) ; -------------------------------------------------------------------------- Delete "$R9" ; delete file ; -------------------------------------------------------------------------- ; Restore original file ; -------------------------------------------------------------------------- CleanFilesRestore: IfFileExists "$R9-PortBak" 0 CleanFilesLoop ; check whether file exists Rename "$R9-PortBak" "$R9"; rename file back to original name Goto CleanFilesLoop CleanFilesEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Copy registry key (portable), restore oroginal registry key ; ************************************************************************** Function CleanReg !ifdef REGKEYS !if ! "${REGKEYS}" = "" ; only do stuff if constant is not empty StrCmp $SECONDLAUNCH "true" CleanRegEnd ; do not do anything if launched a second time StrCpy "$R0" "${REGKEYS}" ; copy constant to working variable StrCpy "$R8" "0" ; reset variable CleanRegLoop: ; -------------------------------------------------------------------------- ; Get single parameter out of list, i.e. obtain next single settings file ; -------------------------------------------------------------------------- StrCmp "$R0" "" CleanRegEnd ; do not do file operations, when no directtories given anymore IntOp $R8 $R8 + 1 ; increase counter ${WordFind} "$R0" "||" "+01" $R9 ; save first parameter to register ${WordFind} "$R0" "||" "+02*}" $R0 ; remove first part from saved value StrCmp "$R0" "$R9" LastCleanRegLoop ; if register values are identical -> no more delimiters Goto DoCleanRegWork ; do not delete value list LastCleanRegLoop: StrCpy "$R0" "" ; if no delimiter available anymore empty working variable DoCleanRegWork: ; -------------------------------------------------------------------------- ; Copy actual registry key to portable folder ; -------------------------------------------------------------------------- ${registry::KeyExists} "$R9" $R7 ; check whether registry key exists StrCmp "$R7" "0" 0 CleanRegRestore ; registry key does not exist, do not save anything ${registry::SaveKey} "$R9" "$DATADIR\Registry\RegKey$R8.reg" "/G=1 /D=2" $R7 ; Backup registry key ; -------------------------------------------------------------------------- ; Delete actual actual registry key (with portable content) ; -------------------------------------------------------------------------- ${registry::DeleteKey} "$R9" $R7 ; Delete registry key ; -------------------------------------------------------------------------- ; Restore original registry key ; -------------------------------------------------------------------------- CleanRegRestore: IfFileExists "$DATADIR\RegistryBackup\RegKey$R8.reg" 0 CleanRegLoop ; only restore if a registry file exists ${registry::RestoreKey} "$DATADIR\RegistryBackup\RegKey$R8.reg" $R7 ; Restore saved registry key Goto CleanRegLoop CleanRegEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Delete all the keys in $CURBACKUPREGFILE and restore old registry files. ; ************************************************************************** Function CleanRegFiles !ifdef REGFILE !if ! "${REGFILE}" == "" ; Save the current list of registry values to the regkeys, delete them, then restore from backup CleanRegFilesLoop: IntOp $R8 $R8 + 1 ; increase counter IfFileExists "$DATADIR\Registry\RegKey$R8.reg" 0 CleanRegFilesEnd StrCpy "$CURREGFILE" "$DATADIR\RegistryBackup\RegKey$R8.reg" StrCpy "$CURBACKUPREGFILE" "$DATADIR\Registry\RegKey$R8.reg" StrCpy "$CURCONDENSEDREGFILE" "$CURBACKUPREGFILE.txt" CopyFiles "$CURBACKUPREGFILE" "$CURBACKUPREGFILE.backup" ; Great! Now backup the current values, then import the old ones! Call BackupRegKeys Call ImportRegKeysFromFile CopyFiles "$CURBACKUPREGFILE.backup" "$CURBACKUPREGFILE" Delete "$CURBACKUPREGFILE.backup" Goto CleanRegFilesLoop CleanRegFilesEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Unload DLLs ; ************************************************************************** Function CleanDLLs !ifdef REGISTERDLLS !if ! "${REGISTERDLLS}" == "" StrCmp $SECONDLAUNCH "true" CleanDllsEnd ; do not do anything if launched a second time StrCpy "$R0" "${REGISTERDLLS}" ; copy constant to working variable StrCpy "$R8" "0" ; reset variable CleanDllsLoop: ; -------------------------------------------------------------------------- ; Get single parameter out of list, i.e. obtain next single settings file ; -------------------------------------------------------------------------- StrCmp "$R0" "" CleanDllsEnd ; do not do anything when no directtories given anymore IntOp $R8 $R8 + 1 ; increase counter ${WordFind} "$R0" "||" "+01" $R9 ; save first parameter to register ${WordFind} "$R0" "||" "+02*}" $R0 ; remove first part from saved value StrCmp "$R0" "$R9" LastCleanDllsLoop ; if register values are identical -> no more delimiters Goto DoCleanDllsWork ; do not delete value list LastCleanDllsLoop: StrCpy "$R0" "" ; if no delimiter available anymore empty working variable DoCleanDllsWork: ; !insertmacro UnInstallLib REGDLL SHARED NOREBOOT_NOTPROTECTED "$EXEDIR\App\${APP}\$R9" ExecWait "regsvr32.exe /s /u $\"$EXEDIR\App\${APP}\$R9$\"" ;UnRegDll "$EXEDIR\App\${APP}\$R9" Goto CleanDllsLoop CleanDllsEnd: !endif !endif FunctionEnd ; ************************************************************************** ; * Function: Clean up stuff, The absolute last things to do ; ************************************************************************** Function Clean StrCmp $SECONDLAUNCH "true" CleanEnd ; do not do anything if launched a second time !ifdef REGFILE !if ! "${REGFILE}" == "" ${registry::Unload} ; unload registry functions from, memory Sleep 500 ; let REGEDIT read the registry file IfFileExists "$DATADIR\RegistryBackup\*.*" 0 CleanEnd ; check whether folder exists RMDir "/r" "$DATADIR\RegistryBackup" !endif !endif CleanEnd: FunctionEnd ; ************************************************************************** ; * Function: Replace Registry vars in $CURREGFILE, and output to $CURBACKUPREGFILE ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function ReplaceRegVars StrCmp $SECONDLAUNCH "true" ReplaceRegVarsEnd ; do not do anything if launched a second time StrCpy $MODIFIED_STR "" !insertmacro ReplaceSubStr "$EXEDIR\App\${APP}" "\" "\\" StrCpy $REGDESTPATH $MODIFIED_STR StrCpy $MODIFIED_STR "" !insertmacro ReplaceSubStr "$EXEDIR" "\" "\\" StrCpy $REGEXETPATH $MODIFIED_STR StrCpy $MODIFIED_STR "" !insertmacro ReplaceSubStr "$DATADIR" "\" "\\" StrCpy $REGDATAPATH $MODIFIED_STR StrCpy $MODIFIED_STR "" !insertmacro ReplaceSubStr "$DATADIR\UserProfile" "\" "\\" StrCpy $REGUSERDATAPATH $MODIFIED_STR Call GetRegComputerName IfFileExists "$CURREGFILE" 0 ReplaceRegVarsNoFile ; check whether reg file exists StrCpy $REGOUT "0" ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%DESTPATH%" "$REGDESTPATH" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%EXEPATH%" "$REGEXETPATH" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%DATAPATH%" "$REGDATAPATH" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%USERDATAPATH%" "$REGUSERDATAPATH" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%LICENSEKEY%" "$REGLICENSEKEY" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%LICENSENAME%" "$REGLICENSENAME" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%LICENSECOMPANY%" "$REGLICENSECOMPANY" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%COMPUTERNAME%" "$REGCOMPUTERNAME" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%COMPUTERNAMEFULL%" "$REGCOMPUTERNAMEFULL" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace Call GetUserSID ${textreplace::ReplaceInFile} "$CURREGFILE" "$CURREGFILE" "%USERSID%" "$REGUSERSID" "" $REGOUT IntCmp $REGOUT 0 0 BadRegReplace Goto ReplaceRegVarsEnd BadRegReplace: messageBox MB_OK|MB_ICONEXCLAMATION "There was an error of $REGOUT while trying to replace variables in $CURREGFILE" Abort ReplaceRegVarsNoFile: messageBox MB_OK|MB_ICONEXCLAMATION "File doesn't exist: $CURREGFILE" Abort ReplaceRegVarsEnd: FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Get the computer and put it in $REGCOMPUTERNAME ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function GetRegComputerName ; WinNT/XP ReadRegStr $REGCOMPUTERNAME HKLM "SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" "Hostname" StrCmp $REGCOMPUTERNAME "" Win9x ReadRegStr $REGCOMPUTERNAMEFULL HKLM "SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" "Domain" StrCpy $REGCOMPUTERNAMEFULL "$REGCOMPUTERNAME.$REGCOMPUTERNAMEFULL" Goto Done Win9x: ReadRegStr $REGCOMPUTERNAME HKLM "System\CurrentControlSet\Control\ComputerName\ComputerName" "ComputerName" StrCpy $REGCOMPUTERNAMEFULL $REGCOMPUTERNAME Done: FunctionEnd !endif !endif ; ************************************************************************** ; * Function: String Replace macro from http://nsis.sourceforge.net/Replace_Sub_String_%28macro%29 ; * (probably could be cleaned up for this script though) ; ************************************************************************** ; This script is derived of a script Written by dirtydingus : ; "Another String Replace (and Slash/BackSlash Converter)" ; ; for more information please see : ; http://nsis.sourceforge.net/Another_String_Replace_(and_Slash/BackSlash_Converter) !ifdef REGFILE !if ! "${REGFILE}" == "" Function StrRep ;Written by dirtydingus 2003-02-20 04:30:09 ; USAGE ;Push String to do replacement in (haystack) ;Push String to replace (needle) ;Push Replacement ;Call StrRep ;Pop $R0 result Exch $R4 ; $R4 = Replacement String Exch Exch $R3 ; $R3 = String to replace (needle) Exch 2 Exch $R1 ; $R1 = String to do replacement in (haystack) Push $R2 ; Replaced haystack Push $R5 ; Len (needle) Push $R6 ; len (haystack) Push $R7 ; Scratch reg StrCpy $R2 "" StrLen $R5 $R3 StrLen $R6 $R1 loop: StrCpy $R7 $R1 $R5 StrCmp $R7 $R3 found ;StrCpy $R7 $R1 1 ; - optimization can be removed if U know len needle=1 StrCpy $R7 $R1 1 StrCpy $R2 "$R2$R7" StrCpy $R1 $R1 $R6 1 StrCmp $R1 "" done loop found: StrCpy $R2 "$R2$R4" StrCpy $R1 $R1 $R6 $R5 StrCmp $R1 "" done loop done: StrCpy $R3 $R2 Pop $R7 Pop $R6 Pop $R5 Pop $R2 Pop $R1 Pop $R4 Exch $R3 FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Gets the SID of the current user ; ************************************************************************** !ifdef REGFILE !if ! "${REGFILE}" == "" Function GetUserSID UserInfo::GetName Pop $R0 System::Call '*(&w${NSIS_MAX_STRLEN})i.R8' System::Call 'advapi32::LookupAccountNameW(w "",w R0,i R8, *i ${NSIS_MAX_STRLEN}, w .R1, *i ${NSIS_MAX_STRLEN}, *i .r0)i .r1' System::Call 'advapi32::ConvertSidToStringSid(i R8,*t .R1)i .r0' System::Free $R8 StrCpy $REGUSERSID $R1 FunctionEnd !endif !endif ; ************************************************************************** ; * Function: Tokenizes a string ; * From: http://nsis.sourceforge.net/StrTok_function ; ************************************************************************** ;author bigmac666 Function StrTok Exch $R1 Exch 1 Exch $R0 Push $R2 Push $R3 Push $R4 Push $R5 ;R0 fullstring ;R1 tokens ;R2 len of fullstring ;R3 len of tokens ;R4 char from string ;R5 testchar StrLen $R2 $R0 IntOp $R2 $R2 + 1 loop1: IntOp $R2 $R2 - 1 IntCmp $R2 0 exit StrCpy $R4 $R0 1 -$R2 StrLen $R3 $R1 IntOp $R3 $R3 + 1 loop2: IntOp $R3 $R3 - 1 IntCmp $R3 0 loop1 StrCpy $R5 $R1 1 -$R3 StrCmp $R4 $R5 Found Goto loop2 Goto loop1 exit: ;Not found!!! StrCpy $R1 "" StrCpy $R0 "" Goto Cleanup Found: StrLen $R3 $R0 IntOp $R3 $R3 - $R2 StrCpy $R1 $R0 $R3 IntOp $R2 $R2 - 1 IntOp $R3 $R3 + 1 StrCpy $R0 $R0 $R2 $R3 Cleanup: Pop $R5 Pop $R4 Pop $R3 Pop $R2 Exch $R0 Exch 1 Exch $R1 FunctionEnd ; ########################################################################## ; # - README --- README --- README --- README --- README --- README - ; ########################################################################## ; The following section could be deleted for own launchers, or you might want ; to create a readme.txt out of it. /*------------------- cut here ------------------------------------------------ 2007/05/06 - Steve Babineau - babineau@gmail.com My modifications can be found here: http://thebabineaus.com/software/PAT/PAT_sab.nsi Changes to version 1.9.3: - Fixed a couple minor bugs - Added REGISTERDLLS constant. This is a simple "||"-delimited list of DLLs to be loaded before the program's execution, then unloaded right after. Use this for loading the VB6 runtime DLLs, for example. - Added external registry management via the REGFILE constant. By using this constant to list the external registry files, the template now contains the ability to manage more registry keys than is allowed by the 1024 character limit in String variables. Also, this gives the template the ability to replace keywords in the registry files that would be parsed out at runtime, letting the user insert path-specific variables into the registry as required. For example, in my external registry file I might have [HKEY_CURRENT_USER\Software\Software Company\Software Name\version1] "MyFilesPath"="%USERDATAPATH%\\My Files\\" At runtime, the wrapper will replace %USERDATAPATH% with $DATADIR\UserProfile, put the result in RegKey#.reg file, then let the existing template functionality to import those keys as normal. The variables that the template is currently replacing are: %DESTPATH% = "$EXEDIR\App\${APP}" %EXEPATH% = "$EXEDIR" %DATAPATH% = "$DATADIR" %USERDATAPATH% = "$DATADIR\UserProfile" %USERSID% = The SID (Security Identifier) of the current user %LICENSEKEY% = License key specified in the ini file (see below) %LICENSENAME% = License name specified in the ini file (see below) %LICENSECOMPANY% = License company specified in the ini file (see below) %COMPUTERNAME% = The computer name that the application is currently running on %COMPUTERNAMEFULL% = The fully qualified computer name that the application is currently running on (e.g. computer.domain.com) The idea with the external registry files is to allow the person using the template to drop in a REGEDIT4 registry file that they exported from the registry using something like the program "Total Uninstall", change the paths in the registry file to the variables I mentioned earlier, then configure and compile your template. Managing registry keys is a pain! I don't want to have to do it for every portable application I make and I think I made it easier. As such, I put some work into managing these exported registry files. When the compiled wrapper is first run with the REGFILE constant defined, it will: 1) Look in the Data\Registry directory for your registry files (configured with the REGFILES constant) 2) Copy it to a RegKey#.reg file 3) Replace the variables %DESTPATH%, %EXEPATH%, %USERDATAPATH%, %DATAPATH% %LICENSEKEY%, %LICENSENAME%, and %LICENSECOMPANY% 4) Extract just the keys into a temporary file. 5) Compress this list down to just the "parent" keys so that I know what needs to be deleted when I exit the program. This process takes a while on a large registry file. It takes about 5-6 minutes on my computer with my registry-keys file (from step 4) that is 2176 lines long. This process could be sped up a LOT if there were support from a plugin using a DLL. Since this is a rather lengthly process, there is a check in there to not re-do it if the RegKey#.reg.txt file exists (that is the file that stores the "parent" registry keys only). - Added the RegLicenseCompany, RegLicenseName, and RegLicenseKey options to the INI file. If your registry file requires a serial number, use these INI variables to set them automatically. - If you use the INSTALLSOURCES option, it will look for the REGFILE defines (if there is one) and in the source directory and extract it automatically to the $EXEDIR\Data\Registry directory. These new features, coupled with the INSTALLSOURCES function, allow you to package a .reg file with your portable application and keep it completely system independant. Portable Application Template ============================= Copyright (c) 2007 Karl Loncarek (for the template) Original version of this template could be downloaded at: http://www.loncarek.de/downloads/PortableApplicationTemplate.zip see http://portableapps.com/node/6709 for more information My modifications can be found here: http://thebabineaus.com/software/PAT/PAT_sab.nsi ABOUT Portable Application Template =================================== This template is intended to help developers or interested users to easily create launchers for applications to make them portable. Those applications could be open source or shareware or even commercial applications. The two latter ones may be interesting for personal use. Thus almost every application could be run from e.g. USB drive no matter what drive letter is used, i.e. without leaving any information or traces on the host PC. HOW TO ADAPT THE TEMPLATE TO YOUR NEEDS ======================================= The key problem of "normal" applications is that they leave traces on the computer that they are running on. Those traces could be registry keys, directories with files, or setting files (e.g. .INI files) themselves. This template is a NSIS source. It uses the same technique as all the applications that could be found on http://portableapps.com . First of all you have to trace what changes are done on your system when installing an application. Personally I use the freeware version of Total Uninstall, but you can use any other tracing software you like. You could also use Sysinternals Filemon or Regmon application for this task. Then list the changed/created folders, files, registry keys in the constants REGKEYS, SETTINGSFILES, SETTINGSDIRS. Separate multiple entries with "||". Folders or files written to the users profile directory are automatically redirected if the constant REDIRECTUSERPROFILE is set to true. So there is no need to add them in the above lists. Beware of applications that launch other applications. Those applications started by your portable program will also use the changed user profile folder. In such a case it is recommended to comment out REDIRECTUSERPROFILE or set it to something different than "TRUE". Then automatic redirection is disabled. If the application to launch does not create those files on the first start then they have to be copied manually into the "UserProfile" directory. The "UserProfile" could be found within your data directory depending on your directory structure (see below). On some applications additional changes in e.g. configuration files have to be done, mainly regarding path settings. You have to check if you could use relative paths (the easiest thing as you don't have to do anything else) or if you have to change a drive letter each time you start the launcher. This has to be configured manually and should be done only by intermediate/advanced users who have some knowledge on NSIS. If you want to create a launcher for a commercial/shareware application you have first to take care whether the license of that application allows it. This is in your responsibility. Do not create a package which contains the commercial files as this would be illegal in most cases. Since version 1.9 this template integrates some functions that help you in that task. You can simply share the EXE created from this source. All the application and data directories are created automatically. Even if your portable application folder is empty (or at least does not contain the EXE) you will be prompted for a folder that holds your locally installed application and copies all the files below it. Existing settings will be copied when the launcher runs for the first time. You should not uninstall the local application before that. If you want to share your sources with your launcher simple set the constant INSTALLSOURCES to "TRUE". Then those files will be packed into the launcher. The launcher then acts as launcher and installer! The source files will then be copied automatically into the appropriate folders. An existing "readme.txt" will be copied also to the sources. If it does not exist a warning will occur when compiling. Do not worry about this warning, it'S mor a kind of an information. Beware also of settings which are only possible with administrator rights. It might happen that you don't have adminstrator rights on the host computer. In such a case the application might not run. But you have the possibility to check whether the local user has administrator rights (necessary when e.g. writing to the HKLM registry key). Simply set ADMINREQUIRED to "TRUE". If not required comment it out or set it to something different than "TRUE". List of possible root keys for the registry value (use short version as much as possible to save space. The list is taken from the NSIS help) HKCC or HKEY_CURRENT_CONFIG HKCR or HKEY_CLASSES_ROOT HKCU or HKEY_CURRENT_USER HKDD or HKEY_DYN_DATA HKLM or HKEY_LOCAL_MACHINE HKPD or HKEY_PERFORMANCE_DATA HKU or HKEY_USERS SHCTX or SHELL_CONTEXT LICENSE OF TEMPLATE =================== Copyright? 2006-2007 Karl Loncarek All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. REQUIREMENTS ============ In order to compile this script with NSIS you need the following Plugins: - NewAdvSplash - Registry - FindProc - Dialogs INSTALLATION / DIRECTORY STRUCTURE ================================== This template uses the same directory structure as John T. Haller did with most of the packages at http://www.portableapps.com to keep some kind of uniformity. Below is used for your applications name. By default the program expects one of these directory structures: +-\ <--- Directory with Portable.exe +-\App\ | +-\\ <--- All files and directories of a local installation +-\Data\ (optional, will be created automatically when needed) | +-\Registry\ (optional, will be created automatically when needed) | +-\Settings\ (optional, will be created automatically when needed) | +-\RegistryBackup\ <--- will be created dynamically | +-\UserProfile\ (optional, will be created automatically when needed) +-\Other\ (optional) <--- contains additional information +-\Sources\ (optional) +-\PortableSources\ (optional) <--- contains source of launcher OR +-\ <--- Directory with Portable.exe +-\Portable\ +-\App\ | +-\\ <--- All files and directories of a local installation +-\Data\ (optional, will be created automatically when needed) | +-\Registry\ (optional, will be created automatically when needed) | +-\Settings\ (optional, will be created automatically when needed) | +-\RegistryBackup\ <--- will be created dynamically | +-\UserProfile\ (optional, will be created automatically when needed) +-\Other\ (optional) <--- contains additional information +-\Sources\ (optional) +-\PortableSources\ (optional) <--- contains source of launcher OR +-\ <--- Directory with Portable.exe +-\PortableApps\ +-\Portable\ +-\App\ | +-\\ <--- All files and directories of a local installation +-\Data\ (optional, will be created automatically when needed) | +-\Registry\ (optional, will be created automatically when needed) | +-\Settings\ (optional, will be created automatically when needed) | +-\RegistryBackup\ <--- will be created dynamically | +-\UserProfile\ (optional, will be created automatically when needed) +-\Other\ (optional) <--- contains additional information +-\Sources\ (optional) +-\PortableSources\ (optional) <--- contains source of launcher OR +-\ <--- Directory with Portable.exe +-\Apps\ | +-\Portable\ | +-\\ <--- All files and directories of a local installation +-\Data\ | +-\Portable\ | +-\Registry\ (optional, will be created automatically when needed) | +-\Settings\ (optional, will be created automatically when needed) | +-\RegistryBackup\ <--- will be created dynamically | +-\UserProfile\ (optional, will be created automatically when needed) +-\Other\ (optional) <--- contains additional information +-\Portable\ +-\Sources\ (optional) +-\PortableSources\ (optional) <--- contains source of launcher It can be used in other directory configurations by including the Portable.INI in the same directory as the launcher Portable.EXE. Details for the configuration settings in the INI files could be found below. The INI file may also be placed in the subdirectories "Portable", "PortableApps\Portable", "Apps\Portable", or "Data\Portable". Those directories are relative to the location of the Portable.EXE file. All paths given in the INI-file should be relative to the location of Portable.EXE. .INI FILE CONFIGURATION OPTIONS =============================== The launcher will look for an .INI file within it's directory. If you are happy with the default options, you won't need it. The INI file is formatted as follows (Below is used for your applications name.): [Portable] ProgramExecutable=.exe ProgramParameters= ProgramDirectory=App\ DataDirectory=Data SplashScreen=enabled Required entries are ProgramDirectory and DataDirectory. If others are missing or empty the default values will be used. All paths provided should be RELATIVE to the "Portable.exe". ProgramExecutable: Allows to launch a different EXE file of your application ProgramParameters: Additonal parameters that should be used when launching the application. This setting overrides te defaults. ProgramDirectory: The path to the EXE file of your application that should be launched. But this is only the path, thus must not contain a filename. DataDirectory: The path where all settings should be stored SplashScreen: Controls whether the splash screen is showed upon start of the launcher if one is defined by default. Anything but "enabled" disables it. CURRENT LIMITATIONS =================== WRITE ACCESS REQUIRED - The data directory must be writeable on your drive (e.g. USB drive) as all the settings and configurations are saved there. HISTORY ======= 1.0 - First release: Based on Quickport NSIS template developed by Deuce added capability to backup multiple files/registry keys 1.1 - INI files and some default directory structures are supported now. 1.2 - added sleep time when deleting registry keys; adopted new directory structure as recently used by John (AppPortable instead of PortableApp) 1.3 - fixed a bug 1.4 - added handling for second launch; some bugfixes 1.5 - complete rewrite for easier understanding of the source; original files/directories are now renamed instead of copied; error message when backup files/folders exist; Readme now included at end of the script 1.6 - no need to define any folder/file within SETTINGSFILES or SETTINGSDIRS that is normally stored within the users profile folder. Those folders/files will now be stored on the portable drive automatically (i.e. redirected)! 1.7 - added creation of other (maybe missing) data folders; made user profile redirection switchable, documentation updates 1.8 - added check whether user has local administrator rights; removed "Unused WordFunc" warning during compile time; optimized compiling when commenting out some constants was forgotten ( to get smallest EXE you should really comment out); bugfixes 1.9 - when no directories are found a default directory structure is created; when no EXE is found you are asked to select a folder from which to copy the files; added possibility to integrate the sources if you want to distribute only the exe, e.g. when creating commercial/shareware applications launchers -> launcher is an installer at the same time */########################################################################## # End of file ##########################################################################