Search code examples
nsismodern-ui

How to create shortcut for desktop and startmenu after installation in NSIS?


enter image description here

I am new to NSIS. I am trying to add this below style of the page in post-installation (i.e. before the finish page). Could anyone suggest, whether we can do a single custom page and add the necessary checkbox, radio button, and program groups ? or any other suggestions.

Below is my page sequence:

!insertmacro MUI_PAGE_DIRECTORY
  
  ;Custom page for selecting service name to restart.
  Page custom nsDialogsSelectService ngDialogSelectServicePageLeave 
  
  ; variable and text for the app data dir
  !define MUI_DIRECTORYPAGE_VARIABLE $appDataDir
  !define MUI_PAGE_HEADER_TEXT "Choose Data Directory"
  !define MUI_PAGE_HEADER_SUBTEXT "Choose the folder in which to install application data for ${PRODUCT_FULL} ${PVERSION}."
  !define MUI_DIRECTORYPAGE_TEXT_TOP "Setup will install data directory need todo.  To install in a different folder, click Browse and select another folder.  Click Next to Continue."
  !define MUI_DIRECTORYPAGE_TEXT_DESTINATION "Destination Folder"
  !insertmacro MUI_PAGE_DIRECTORY 
  !insertmacro MUI_PAGE_INSTFILES
  
  !define MUI_FINISHPAGE_TITLE_3LINES
  
  Page custom nsDialogShortcut ngDialogShortcutPageLeave ;Page contain checkbox , based on the selection of checkbox I am showing Startmenu and then finish page.
  
  !insertmacro MUI_PAGE_STARTMENU 0 $SMDir  
  !insertmacro MUI_PAGE_FINISH

Solution

  • Giving the user the choice of user/machine shortcuts is in conflict with how UAC works. When a non-admin user elevates with an administrator account the installer will end up running with the "wrong" profile.

    The Windows guidelines say that only application suites (with individual major applications, like MS Office) should create Start menu folders. Regular applications should create their (single) shortcut directly in $SMPrograms. You should not create shortcuts to the uninstaller nor help-files. You should also refrain from creating a desktop shortcut.

    This means you can simply use the components page to provide the shortcut option(s):

    !include MUI2.nsh
    !insertmacro MUI_PAGE_WELCOME
    !insertmacro MUI_PAGE_COMPONENTS
    !insertmacro MUI_PAGE_DIRECTORY
    !insertmacro MUI_PAGE_INSTFILES
    !insertmacro MUI_PAGE_FINISH
    !insertmacro MUI_LANGUAGE English
    
    Section "Program files"
    SectionIn RO
    SetOutPath $InstDir
    File "MyApp.exe"
    SectionEnd
    
    Section "Start menu shortcut"
    CreateShortcut "$SMPrograms\$(^Name).lnk" "$InstDir\MyApp.exe"
    SectionEnd
    
    Section /o "Desktop shortcut"
    CreateShortcut "$Desktop\$(^Name).lnk" "$InstDir\MyApp.exe"
    SectionEnd
    

    or as a checkbox on the Finish page:

    !include MUI2.nsh
    !insertmacro MUI_PAGE_WELCOME
    !insertmacro MUI_PAGE_DIRECTORY
    !insertmacro MUI_PAGE_INSTFILES
    !define MUI_FINISHPAGE_SHOWREADME ""
    !define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Start menu shortcut"
    !define MUI_FINISHPAGE_SHOWREADME_FUNCTION CreateShortcuts
    !insertmacro MUI_PAGE_FINISH
    !insertmacro MUI_LANGUAGE English
    
    Function CreateShortcuts
    CreateShortcut "$SMPrograms\$(^Name).lnk" "$InstDir\MyApp.exe"
    FunctionEnd
    

    If you actually have a suite of applications then you can use the Start menu page to prompt for a folder name:

    Var SMFolder
    
    !include MUI2.nsh
    !insertmacro MUI_PAGE_WELCOME
    !insertmacro MUI_PAGE_DIRECTORY
    !insertmacro MUI_PAGE STARTMENU Suite $SMFolder
    !insertmacro MUI_PAGE_INSTFILES
    !insertmacro MUI_PAGE_FINISH
    !insertmacro MUI_LANGUAGE English
    
    Section
    !insertmacro MUI_STARTMENU_WRITE_BEGIN Suite
    CreateDirectory "$SMPrograms\$SMFolder"
    CreateShortcut "$SMPrograms\$SMFolder\App1.lnk" "$InstDir\MyApp1.exe"
    CreateShortcut "$SMPrograms\$SMFolder\App2.lnk" "$InstDir\MyApp2.exe"
    ; TODO: Write $SMFolder to the registry or a .ini so your uninstaller can delete the folder
    !insertmacro MUI_STARTMENU_WRITE_END
    SectionEnd
    

    In the unlikely event that you have a suite of applications and you also want to create desktop shortcuts then yes, you need to use a custom page:

    Var SMDir
    Var SMCheck
    Var DeskCheck
    Var SMList
    Var SMDirEdit
    
    !include LogicLib.nsh
    !include nsDialogs.nsh
    !include MUI2.nsh
    !insertmacro MUI_PAGE_WELCOME
    !insertmacro MUI_PAGE_DIRECTORY
    Page Custom MyShortcutsPageCreate MyShortcutsPageLeave
    !insertmacro MUI_PAGE_INSTFILES
    !insertmacro MUI_PAGE_FINISH
    !insertmacro MUI_LANGUAGE English
    
    Function .onInit
    StrCpy $SMDir "$(^Name)" ; Default
    StrCpy $SMCheck ${BST_CHECKED}
    FunctionEnd
    
    Function MyShortcutsPageCreate
    !insertmacro MUI_HEADER_TEXT "Shortcuts" "Shortcuts blah blah blah"
    nsDialogs::Create 1018
    Pop $0
    ${IfThen} $0 == error ${|} Abort ${|}
    
    ${NSD_CreateCheckbox} 0 0u 50% 12u "Create Start menu shortcuts"
    Pop $R8
    SendMessage $R8 ${BM_SETCHECK} $SMCheck ""
    
    ${NSD_CreateCheckbox} 0 14u 50% 12u "Create desktop shortcuts"
    Pop $R9
    SendMessage $R9 ${BM_SETCHECK} $DeskCheck ""
    
    ${NSD_CreateSortedListBox} 0 28u 100% -43u ""
    Pop $SMList
    
    ${NSD_CreateText} 0 -13u 100% 12u "$SMDir"
    Pop $SMDirEdit
    
    ${NSD_LB_AddString} $SMList "(Default)"
    ${NSD_LB_SetItemData} $SMList 0 1 ; Mark as special
    SetShellVarContext Current
    Call FillSMList
    SetShellVarContext All
    Call FillSMList
    SetShellVarContext ? ; TODO: Restore to what you actually are installing as
    ${NSD_OnChange} $SMList OnSMListChanged
    
    ${NSD_OnClick} $R8 OnSMCheckChanged
    Push $R8
    Call OnSMCheckChanged
    nsDialogs::Show
    FunctionEnd
    
    Function FillSMList
    FindFirst $0 $1 "$SMPrograms\*"
    loop:
        StrCmp $1 "" done
        ${If} ${FileExists} "$SMPrograms\$1\*.*"
        ${AndIf} $1 != "."
        ${AndIf} $1 != ".."
            ${NSD_LB_FindStringExact} $SMList "$1" $2
            ${If} $2 < 0
                ${NSD_LB_AddString} $SMList $1
            ${EndIf}
        ${EndIf}
        FindNext $0 $1
        Goto loop
    done:
    FindClose $0
    FunctionEnd
    
    Function OnSMCheckChanged
    Pop $0
    ${NSD_GetChecked} $0 $0
    EnableWindow $SMList $0
    EnableWindow $SMDirEdit $0
    FunctionEnd
    
    Function OnSMListChanged
    Pop $0
    ${NSD_LB_GetSelection} $SMList $0
    ${NSD_SetText} $SMDirEdit "$0\$(^Name)"
    ${NSD_LB_GetSelectionIndex} $SMList $0
    ${NSD_LB_GetItemData} $SMList $0 $0
    ${If} $0 <> 0
        ${NSD_SetText} $SMDirEdit "$(^Name)"
    ${EndIf}
    FunctionEnd
    
    Function MyShortcutsPageLeave
    ${NSD_GetChecked} $R8 $SMCheck
    ${NSD_GetChecked} $R9 $DeskCheck
    ${NSD_GetText} $SMDirEdit $SMDir
    FunctionEnd
    
    
    Section
    ${If} $SMCheck <> 0
        CreateDirectory "$SMPrograms\$SMDir"
        CreateShortcut "$SMPrograms\$SMDir\App1.lnk" "$InstDir\App1.exe"
        CreateShortcut "$SMPrograms\$SMDir\App2.lnk" "$InstDir\App2.exe"
    ${EndIf}
    
    ${If} $DeskCheck <> 0
        CreateShortcut "$Desktop\App1.lnk" "$InstDir\App1.exe"
        CreateShortcut "$Desktop\App2.lnk" "$InstDir\App2.exe"
    ${EndIf}
    SectionEnd