A Simple Installer for Palm Applications

From NSIS Wiki
Jump to navigationJump to search
Author: ellocogato (talk, contrib)


Disclaimer

I am not particularly experienced with NSIS. I don't fully understand InstallOptions, the System plugin, or the Modern Interface. I needed an installer for a Palm application, and I could find no such existing NSIS example, so after hacking up an installer myself, I wrote an example based on what I did. It's probably not the most elegant, and it may not even be entirely correct. But it works.

If you find a mistake, find a better way of doing something, or add functionality that others might find useful, send me comments so that I can improve this example

The Script

; DESCRIPTION: Example Installer for simple (i.e. no conduit program) Palm applications
; AUTHOR: Gus Scheidt
; EMAIL: ellocogato <AT> gmail <DOT> com
; REQUIRES:  - The InstAide and UserData DLLs from the Palm Conduit Development Kit
;              (copy to the same directory as the .nsi file)
;            - Originally compiled with NSIS v2.11
;
;  This script supports installation of one or more files (PRC,PDB) to a Palm OS
;  handheld's internal memory plus one or more files to a memory slot (internal
;  expansion or external flash memory card).  It does not demonstrate installation
;  of any kind of conduit.  It does not demonstrate the ability to install for
;  multiple Palm Users at once.
;
;  If you don't need to install any files to a memory slot, then you won't need the
;  following:
;    - The "Page custom ShowSlotList" line
;    - The ShowSlotList function
;    - The ShowSlotList.ini file
;    - The GetPalmSlotList function
;    - The CheckSlotMgrSupport function
;    - The "INSTALL FILES TO EXPANSION MEMORY" section of the InstallFiles function
;    - The second half of the RemoveInstallFiles function
;
 
;Script loosely based on the MUI Basic Example Script Written by Joost Verburg
 
; declare variables
Var PalmUserList  ; Used to dynamically populate dropdown on User Selection page
Var PalmSlotList  ; Used to dynamically populate dropdown on Slot Selection page
Var UserChoice    ; Selected Palm Username
Var UserChoiceID  ; ID of selected Palm Username
Var NumSlots      ; How many memory slots available on the selected Palm User's handheld
Var SlotChoice    ; Selected memory slot
Var SlotChoiceID  ; ID of selected memory slot
Var SlotSupport   ; Does the selected Palm User's hanheld support slot management?
Var InstallStatus ; Defined when the installer is aborting
 
; declare constants
!define MY_PRODUCT "My Palm App" ;Define your own software name here
!define MY_VERSION "v1.0" ;Define your own software version here
!define MY_BUILD "1234"
Name "${MY_PRODUCT} ${MY_VERSION} (build ${MY_BUILD})" ;Display name for installer
 
!include "MUI.nsh"
 
;--------------------------------
;Configuration
 
  ; Name of the resulting executable installer file
  OutFile "${MY_PRODUCT}.exe"
 
  ; I don't intend to install any files permenantly on the PC, so
  ; use the system temporary directory
  InstallDir "$TEMP\MyAppSetup"
;--------------------------------
;Modern UI Configuration
 
  ; Show a license agreement page
  !define MUI_LICENSEPAGE
  ; Show a warning when the user cancels the install
  !define MUI_ABORTWARNING
  ; I'm using some custom pages
  !define MUI_CUSTOMPAGECOMMANDS
  ; Show a finish page at the end of the installation
  !define MUI_FINISHPAGE
  ; Don't present the option to reboot the computer on the finish page
  !define MUI_FINISHPAGE_NOREBOOTSUPPORT
  ; The icon for the installer title bar and .exe file
  ;!define MUI_ICON "myapp.ico"
  ; I want to use my product logo in the installer
  ;!define MUI_HEADERIMAGE
  ; Here is my product logo
  ;!define MUI_HEADERIMAGE_BITMAP "myapp.bmp"
  ; I want the logo to be on the right side of the installer, not the left
  ;!define MUI_HEADERIMAGE_RIGHT
  ; I've written a function that I want to be called when the user cancels the installation
  !define MUI_CUSTOMFUNCTION_ABORT myOnAbort
  ; Override the text on the Finish Page
  !define MUI_FINISHPAGE_TITLE "Installation Complete"
  !define MUI_FINISHPAGE_TEXT "${MY_PRODUCT} has been installed to your Palm device.\r\n\r\nClick Finish now to close the installation program."
 
;--------------------------------
;Pages
 
  ; These are the pages that I want my installer to consist of, in the order
  ; I want them to appear
  !insertmacro MUI_PAGE_LICENSE "License.txt" ; License agreement page.  Use license text from License.txt
  !insertmacro MUI_PAGE_INSTFILES ; File installation page
  Page custom ShowUserList ; My custom page for choosing the Palm user
  Page custom ShowSlotList ; My custom page for choosing the Palm external memory slot
  Page custom InstallFiles ; My custom page for scheduling files to be installed
                           ; and informing the user that a HotSync is required
 
  ; I've written a function that I want to be called just before the finish page is displayed
  !define MUI_PAGE_CUSTOMFUNCTION_PRE myFinishPre
  !insertmacro MUI_PAGE_FINISH ; the Finish page
 
;--------------------------------
;Languages
 
  ; I don't know anything about Internationalization.  But I speak English
  !insertmacro MUI_LANGUAGE "English"
 
;--------------------------------
;Language Strings
 
  ;Description text that I want to appear while files are being copied
  LangString DESC_SecCopyUI ${LANG_ENGLISH} "Copying the Palm files to your computer so they can be installed to your device."
 
  ;Header text for all pages in the installer
  LangString TEXT_IO_TITLE ${LANG_ENGLISH} "My Palm App"
  LangString TEXT_IO_SUBTITLE ${LANG_ENGLISH} "Install the Application and Data Files to your Device"
 
;--------------------------------
;Data
 
  ; Is this redundant?
  LicenseData "License.txt"
 
;--------------------------------
;Reserve Files
 
  ;Things that need to be extracted first (keep these lines before any File command!)
  ;Required for proper behavior when using BZIP2 compression
 
  ReserveFile "showUserList.ini"
  ReserveFile "showSlotList.ini"
  ReserveFile "installFiles.ini"
  !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
 
;--------------------------------
;Installer Sections
 
Section "MyApp.prc" SecCopyUI
 
  ; Define what files go where
  SetOutPath "$INSTDIR"
  ; Copy the files that we will use
  File "UserData.dll"
  File "InstAide.dll"
  File "MyApp.prc"
  File "MyAppData.dat"
 
SectionEnd
 
;--------------------------------
;Descriptions
 
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
  !insertmacro MUI_DESCRIPTION_TEXT ${SecCopyUI} $(DESC_SecCopyUI)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
 
;--------------------------------
;Installer Functions
 
Function .onInit
 
  ;Extract InstallOptions INI Files
  !insertmacro MUI_INSTALLOPTIONS_EXTRACT "showUserList.ini"
  !insertmacro MUI_INSTALLOPTIONS_EXTRACT "showSlotList.ini"
  !insertmacro MUI_INSTALLOPTIONS_EXTRACT "installfiles.ini"
 
FunctionEnd
 
Function ShowUserList ; This function shows our custom "Select Palm User" page
  ; Get a list of all Palm users on the PC
  Call GetPalmUserList
  ; Use this list to populate the dropdown on our custom page
  !insertmacro MUI_INSTALLOPTIONS_WRITE "showUserList.ini" "Field 3" "ListItems" "$PalmUserList"
 
  !insertmacro MUI_HEADER_TEXT "$(TEXT_IO_TITLE)" "$(TEXT_IO_SUBTITLE)"
  !insertmacro MUI_INSTALLOPTIONS_DISPLAY "showUserList.ini"
 
  ; if we are aborting, do not check the value of UserChoice (if we do, then
  ; if the user cancels the install without selecting a user, it will insist
  ; that the user choose a user before exiting)
  StrCmp $InstallStatus "aborting_ir_install" End 0
    !insertmacro MUI_INSTALLOPTIONS_READ $UserChoice "showUserList.ini" "Field 3" "State"
    ; Verify that the user chose a valid user.  If not, display the ShowUserList
    ; screen again.
    ; Why not simply set MinLen of the dropdown to 1?  This will prevent the user
    ; from continuing without selecting a user, but it will not present any
    ; messagebox or other dialog.  That is confusing to the user!
    StrCmp $UserChoice "" 0 Continue
      MessageBox MB_OK|MB_ICONINFORMATION "You must choose a user to proceed!"
      Call ShowUserList
      GoTo End
    Continue:
    ; Also store the Palm user ID of the chosen user
    StrCpy $1 $UserChoice
    System::Call 'UserData::UmGetIDFromName(t r1, *i r4r4) i .r5'
    StrCpy $UserChoiceID $4
  End:
 
FunctionEnd
 
Function ShowSlotList
 
  ; Determine whether the chosen Palm user's device supports slot management
  ; If not, we can't use any *Slot* calls
  Call CheckSlotMgrSupport
  IntCmp $SlotSupport 0 0 HasSlotSupport HasSlotSupport
    MessageBox MB_OK|MB_ICONINFORMATION "The device used by the selected user does not appear to support External Memory.  Installation will abort."
    Call myOnAbort
    Quit
  HasSlotSupport:
 
  ; Get a list of all Palm external memory slots
  Call GetPalmSlotList
  ; If there are no slots (less than 1), abort.
  ; If there is only one slot, default to Slot 0 and skip the slot selection screen
  IntCmp $NumSlots 1 OneSlot NoSlots MultiSlots
  NoSlots:
    MessageBox MB_OK|MB_ICONINFORMATION "The device used by the selected user does not appear to have any External Memory slots.  Installation will abort."
    Call myOnAbort
    Quit
  OneSlot:
    StrCpy $SlotChoiceID 0
    Goto End
  MultiSlots:
 
  ; Use this list to populate the dropdown on our custom page
  !insertmacro MUI_INSTALLOPTIONS_WRITE "showSlotList.ini" "Field 3" "ListItems" "$PalmSlotList"
 
  !insertmacro MUI_HEADER_TEXT "$(TEXT_IO_TITLE)" "$(TEXT_IO_SUBTITLE)"
  ; Use the _RETURN macro so that we can get the InstallOptions return value
  ; to detect the Back button
  !insertmacro MUI_INSTALLOPTIONS_DISPLAY_RETURN "showSlotList.ini"
  ; Pop return value from the stack
  Pop $1
  ; If back button was selected, skip the rest of this function (we don't
  ; want to inforce selection if they are going back)
  StrCmp $1 "back" End
 
  ; if we are aborting, do not check the value of SlotChoice (if we do, then
  ; if the user cancels the install without selecting a slot, it will insist
  ; that the user choose a slot before exiting)
  StrCmp $InstallStatus "aborting_ir_install" End
    !insertmacro MUI_INSTALLOPTIONS_READ $SlotChoice "showSlotList.ini" "Field 3" "State"
    ; Verify that the user chose a valid slot  If not, display the ShowSlotList
    ; screen again.
    ; Why not simply set MinLen of the dropdown to 1?  This will prevent the user
    ; from continuing without selecting a user, but it will not present any
    ; messagebox or other dialog.  That is confusing to the user!
    StrCmp $SlotChoice "" 0 Continue
      MessageBox MB_OK|MB_ICONINFORMATION "You must choose a memory slot to proceed!"
      Call ShowSlotList
      GoTo End
    Continue:
    ; Extract and Store the Slot ID of the user's choice
    ; (Assume that the slot ID, in parenthesis, is the last 3 characters of the string)
    StrLen $1 $SlotChoice
    IntOp $1 $1 - 2
    StrCpy $SlotChoiceID $SlotChoice 1 $1;
    ;MessageBox MB_OK|MB_ICONINFORMATION "Extraced Slot ID $SlotChoiceID from selection $SlotChoice"
  End:
 
FunctionEnd
 
Function InstallFiles
 
  ; ====================  INSTALL PRC/PDB FILES  ==========================
  ; Set the PRC file to be installed to internal memory on next HotSync
  StrCpy $1 $UserChoice
  StrCpy $2 "$INSTDIR\MyApp.prc"
  ;MessageBox MB_OK|MB_ICONINFORMATION "Installing $2 for user $1"
  System::Call 'InstAide::PltInstallFile(t r1, t r2) i .r3'
  ;MessageBox MB_OK|MB_ICONINFORMATION "PltInstallFile returned value of $3"
  ; StrCpy $2 "$INSTDIR\AnotherFile.pdb"
  ; System::Call 'InstAide::PltInstallFile(t r1, t r2) i .r3'
 
  ; ====================  INSTALL FILES TO EXPANSION MEMORY  ==============
  ; Say our data file is big or is a file type that PalmOS doesn't allow on internal
  ; memory. Schedule the data file to be installed to the Expansion Card on next HotSync
  ; (Note that PltInstallFile uses username while PlmSlotInstallFile uses userID.)
  StrCpy $4 $UserChoiceID
  StrCpy $6 $SlotChoiceID
  ; Set the data file to be installed to external memory
  StrCpy $2 "$INSTDIR\MyAppData.dat"
  ;MessageBox MB_OK|MB_ICONINFORMATION "Installing $2 for user $4 ($1) to slot $6"
  System::Call 'InstAide::PlmSlotInstallFile(i r4, i r6, t r2) i .r5'
  ;MessageBox MB_OK|MB_ICONINFORMATION "PlmSlotInstallFile returned value of $5"
  ; StrCpy $2 "$INSTDIR\AnotherDataFile.dat"
  ; System::Call 'InstAide::PlmSlotInstallFile(i r4, i r6, t r2) i .r5'
 
  ; ====================  SHOW CUSTOM PAGE  ==========================
  !insertmacro MUI_HEADER_TEXT "$(TEXT_IO_TITLE)" "$(TEXT_IO_SUBTITLE)"
  !insertmacro MUI_INSTALLOPTIONS_DISPLAY "installfiles.ini"
FunctionEnd
 
 
Function GetPalmUserList
  ; NSIS has to be using the directory containing the dll for it to find it
  SetOutPath "$INSTDIR"
  File "UserData.dll" ; copy dll there
  System::Call 'UserData::UmGetUserCount(v) i .r1'
  ;MessageBox MB_OK|MB_ICONINFORMATION "There seem to be $1 Palm users"
 
  IntCmp $1 0 0 0 foundInstaller ; if return value of PltGetUserCount is 0 or negative, then we can't continue
    MessageBox MB_OK|MB_ICONINFORMATION "No Palm Installer was found on your system.  The installation cannot complete.  Please ensure that the latest version of the Palm Desktop Software is installed on your computer before installing."
    Abort
  foundInstaller:
 
  ; Compile the list of Palm Users
  StrCpy $2 0 ; Initialize counter
  StrCpy $PalmUserList "" ; Initialize list
  Loop:
    StrCpy $4 ${NSIS_MAX_STRLEN} ; $4 will tell the function the buffer length of $3
    ; $3 will be set to the username of the user indexed by $2
    System::Call 'UserData::UmGetUserID(i r2, *i r6r6) i .r5'
    System::Call 'UserData::UmGetUserName(i r6, t .r3, *i r4) i .r5'
    ;MessageBox MB_OK|MB_ICONINFORMATION "User indexed at $2 has ID $6 and name $3"
    StrCpy $PalmUserList "$PalmUserList$3|"
    IntOp $2 $2 + 1 ; Incriment the counter
    IntCmp $2 $1 0 Loop 0
 
  ;user list built, but it has an extra | on the end of it
  StrCpy $PalmUserList "$PalmUserList" -1
 
FunctionEnd
 
Function GetPalmSlotList
  ; NSIS has to be using the directory containing the dll for it to find it
  SetOutPath "$INSTDIR"
  File "UserData.dll" ; copy dll there
 
  StrCpy $3 $UserChoice
  StrCpy $6 $UserChoiceID
  ;MessageBox MB_OK|MB_ICONINFORMATION "Checking Slots for Palm user $6 ($3)"
  System::Call 'UserData::UmSlotGetSlotCount(i r6, *i r1r1) i .r5'
  StrCpy $NumSlots $1
  ;MessageBox MB_OK|MB_ICONINFORMATION "There seem to be $NumSlots Slot(s) (rv $5)"
 
  ; Compile the list of Slots
  StrCpy $2 0 ; Initialize counter
  StrCpy $PalmSlotList "" ; Initialize list
  Loop:
    StrCpy $4 ${NSIS_MAX_STRLEN} ; $4 will tell the function the buffer length of $3
    ; $3 will be set to the name of the slot indexed by $2
    System::Call 'UserData::UmSlotGetDisplayName(i r6, i r2, t .r3, *i r4) i .r5'
    ; We need to keep track of the slot ID of each slot, so append it to the display
    ; string so we can extract it later.
    StrCpy $PalmSlotList "$PalmSlotList$3 (Slot $2)|"
    IntOp $2 $2 + 1 ; Incriment the counter
    IntCmp $2 $1 0 Loop 0
 
  ;user list built, but it has an extra | on the end of it
  StrCpy $PalmSlotList "$PalmSlotList" -1
 
FunctionEnd
 
Function CheckSlotMgrSupport
  StrCpy $1 $UserChoiceID
  System::Call 'UserData::UmSlotGetExpMgrVersion(i r1, *i r2r2) i .r5'
  IntCmp $5 0 Success
    StrCpy $SlotSupport 0
    MessageBox MB_OK|MB_ICONINFORMATION "Error ($5).  No Slot Support."
    Goto End
  Success:
  StrCpy $SlotSupport $2
  ;MessageBox MB_OK|MB_ICONINFORMATION "Slot Support (v $SlotSupport)."
  End:
FunctionEnd
 
Function RemoveFiles
  ; We need to Unload the dll so that it can be deleted
  System::Call 'InstAide::PltGetUserCount(v) i .r1 ?u'
  System::Call 'UserData::UmGetUserCount(v) i .r1 ?u'
  ; I read somewhere that you should do this.  Don't know what it does
  SetPluginUnload manual
  System::Free 0
  ; Remove all of the files
  SetOutPath $TEMP
  Delete "$INSTDIR\InstAide.dll"
  Delete "$INSTDIR\UserData.dll"
  RMDir /r "$INSTDIR"
FunctionEnd
 
Function RemoveInstallFiles
  ; Un-schedule the PRC file for installation to internal memory
  StrCpy $1 $UserChoice
  StrCpy $2 "MyApp.prc"
  System::Call 'InstAide::PltRemoveInstallFile(t r1, t r2) i .r3'
  ;MessageBox MB_OK|MB_ICONINFORMATION "PltRemoveInstallFile returned value of $3"
  ; StrCpy $2 "AnotherFile.pdb"
  ; System::Call 'InstAide::PltRemoveInstallFile(t r1, t r2) i .r3'
 
  ; Un-schedule the data file for installation to external memory
  StrCpy $4 $UserChoiceID
  StrCpy $6 $SlotChoiceID
  StrCpy $2 "$INSTDIR\MyAppData.dat"
  System::Call 'InstAide::PlmSlotRemoveInstallFile(i r4, i r6, t r2) i .r5'
  ; StrCpy $2 "$INSTDIR\AnotherDataFile.dat"
  ; System::Call 'InstAide::PlmSlotRemoveInstallFile(i r4, i r6, t r2) i .r5'
 
FunctionEnd
 
Function myFinishPre
  ; Before we exit the installer, clean up all of the files that we copied
  ; to the PC
  Call RemoveFiles
FunctionEnd
 
Function myOnAbort
  ;MessageBox MB_OK|MB_ICONINFORMATION "WE ARE ABORTING !!!"
  ; Let other parts of the installer know that we are aborting
  StrCpy $InstallStatus "aborting_ir_install"
  ; In case we've already scheduled files for installation on the next HotSync,
  ; we should un-schedule them
  Call RemoveInstallFiles
  ; Clean up all of the files that we copied to the PC
  Call RemoveFiles
FunctionEnd

INI Files

Here are the .ini files that are referenced in the script

showUserList.ini

[Settings]
NumFields=4
 
[Field 1]
Type=Label
Text=After you have selected a user, click "Next".
Left=0
Right=171
Top=123
Bottom=132
 
[Field 2]
Type=Label
Text=STEP 1 -- Choose the User for Installation
Left=85
Right=226
Top=4
Bottom=12
 
[Field 3]
Type=Droplist
Text=droplist
ListItems=
Left=60
Right=157
Top=74
Bottom=116
 
[Field 4]
Type=Label
Text=Select the user for whom you wish to install My Palm App from the dropdown box below.  (Note that this script only supports installing for one user at a time.  If you wanted to be able to select multiple users and have your program scheduled for installation on the next HotSync, you would have to change the dropdown box to a listbox and change the script to call the InstAide functions for each user selected.)
Left=0
Right=272
Top=24
Bottom=69

showSlotList.ini

[Settings]
NumFields=4
 
[Field 1]
Type=Label
Text=After you have selected a slot, click "Next".
Left=0
Right=171
Top=123
Bottom=132
 
[Field 2]
Type=Label
Text=STEP 1 -- Choose the Memory Slot for Installation
Left=85
Right=226
Top=4
Bottom=12
 
[Field 3]
Type=Droplist
Text=droplist
ListItems=
Left=60
Right=176
Top=73
Bottom=116
 
[Field 4]
Type=Label
Text=Select the memory slot to which you wish to install the program's data files.
Left=0
Right=272
Top=24
Bottom=68

installFiles.ini

[Settings]
NumFields=3
 
[Field 1]
Type=Label
Text=You must now perform a HotSync to install My Palm App to your device.\r\n\r\nPerform a HotSync now.
Left=0
Right=278
Top=24
Bottom=94
 
[Field 2]
Type=Label
Text=When the HotSync is complete, click "Next" below.
Left=0
Right=181
Top=121
Bottom=129
 
[Field 3]
Type=Label
Text=STEP 2 -- Perform a HotSync
Left=85
Right=185
Top=4
Bottom=12

Credits

This example is roughly based on the MUI Basic Example Script written by Joost Verburg

The ini files were created with HM NIS Edit.