SHMessageBoxCheck

From NSIS Wiki
Jump to navigationJump to search

Description

SHMessageBoxCheck basically works like MessageBox but includes a checkbox that gives the user the option not to show the message box again.

For example this can be used to give the user a simple Yes / No / Always Yes / Always No choice, e.g. when asking whether to remove files that weren't part of the original installation or have been modified.

SHMessageBoxCheck is based on the Windows API function of the same name called via the NSIS System Plug-in.

Usage

!include SHMessageBoxCheck.nsh
 
${SHMessageBoxCheckInit} unique_string
${SHMessageBoxCheck} caption text type
${SHMessageBoxCheckCleanup}

Initialization

To initialize call ${SHMessageBoxCheckInit} unique_string to create a unique name by which to identify your message box. Microsoft recommends to use a GUID as unique_string but any reasonably distinct string will do.

Showing a Message Box

To show a message box call ${SHMessageBoxCheck} caption text type.

caption and text are simple strings specifying the caption of the window and the content text.

type is used to define which buttons are shown and whether to include an image in the message box. For possible choices refer to the the script's code below. To combine two options use the "or" operator, e.g. ${MB_YESNO}|${MB_ICONINFORMATION}

Return value

The macro returns the user's choice (i.e. the button he/she clicked in the dialog). If the user chooses not to see the dialog again the macro will always return the last choice without displaying a message box.

The return value is stored as the first value on the stack, e.g. a Pop $0 will store the result in the $0 user variable.

Return values are integers. For clarity there are defines (e.g. ${IDOK} for the OK button) which can be used in comparisons. Please refer to the script's code below for the full list of possible return values.

Finalization

After you're done call ${SHMessageBoxCheckCleanup} which will reset the state of the message box (i.e. the users choice wether or not to see the message box again will be reset).

Example

SHMessageBoxCheck.png

!include macros\SHMessageBoxCheck.nsh
 
!include FileFunc.nsh
!insertmacro Locate
 
 
Section Uninstall
    StrCpy $INSTDIR "C:\Your\Installation\Directory"
 
    # initialize SHMessageBoxCheck
    ${SHMessageBoxCheckInit} "unique_application_string"
 
    # locate leftover files
    ${Locate} "$INSTDIR" "/L=F /M=*.*" "un.DeleteFile"
 
    # finalize SHMessageBoxCheck
    ${SHMessageBoxCheckCleanup}
SectionEnd
 
Function un.DeleteFile
  # show the message box
  ${SHMessageBoxCheck} "Remove additional files" \
                       "File `$R9` was not part of initial installation. Delete anyway?" \
                       ${MB_YESNO}|${MB_ICONQUESTION}
 
  # get the result
  Pop $0
 
  # delete if user clicked "Yes"
  StrCmp $0 ${IDYES} 0 +2
  Delete $R9
 
  Push 0 # required by ${Locate}
FunctionEnd

The Script

# SHMessageBoxCheck
#   Works like MessageBox but includes a checkbox that gives the user the option not to show the message box again.
#   In that case the return value (first value on the stack) is always set to the last user choice
#
# See
#   http://nsis.sourceforge.net/SHMessageBoxCheck   (documentation)
#   https://msdn.microsoft.com/de-de/library/windows/desktop/bb773836.aspx   (implementation details)
#
 
# types to indicate the buttons displayed in the message box
!define MB_OK                0x00000000
!define MB_OKCANCEL          0x00000001
!define MB_ABORTRETRYIGNORE  0x00000002 # not officially supported, use at your own risk!
!define MB_YESNOCANCEL       0x00000003 # not officially supported, use at your own risk!
!define MB_YESNO             0x00000004
!define MB_RETRYCANCEL       0x00000005 # not officially supported, use at your own risk!
!define MB_CANCELTRYCONTINUE 0x00000006 # not officially supported, use at your own risk!
!define MB_HELP              0x00004000 # not officially supported, use at your own risk!
 
# types to display an icon in the message box
!define MB_ICONHAND          0x00000010
!define MB_ICONQUESTION      0x00000020 # MS bug: Same as MB_ICONEXCLAMATION
!define MB_ICONEXCLAMATION   0x00000030
!define MB_ICONINFORMATION   0x00000040
 
 
# return values
!define IDOK        1
!define IDCANCEL    2
!define IDABORT     3
!define IDRETRY     4
!define IDIGNORE    5
!define IDYES       6
!define IDNO        7
!define IDCONTINUE 11
!define IDTRYAGAIN 10
 
 
 
# the user's previous choice (i.e. the button clicked in the message box)
Var _lastReturnValue
 
# The value that the call to SHMessageBoxCheck should return when the user chose not to display the message box again
!define _DEFAULT 9999
 
# Windows XP does not expose the function name, so we have to specify the function by ordinal value
!ifdef NSIS_UNICODE
    !define _SHMessageBoxCheck_Ordinal 191
!else
    !define _SHMessageBoxCheck_Ordinal 185
!endif
 
 
 
!macro SHMessageBoxCheckInit _UNIQUE_STRING
    # SHMessageBoxCheck stores the user's choice not to display the message box again in the registry, see
    #   HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain
    !ifdef _PSZ_REG_VAL
        !error "Only call SHMessageBoxCheckInit once and make sure to call SHMessageBoxCheckCleanup before using it again"
    !else
        # the unique string used to identify this message (and name of the registry value used to store the checkbox status)
        !define _PSZ_REG_VAL ${_UNIQUE_STRING}
    !endif
 
    # make sure the registry value is not yet set (for whatever reason)
    ${SHMessageBoxCheckCleanup}
!macroend
!define SHMessageBoxCheckInit "!insertmacro SHMessageBoxCheckInit"
 
 
!macro SHMessageBoxCheckCleanup
    # delete the registry key that is used to store the checkbox status so we can start fresh next time
    DeleteRegValue HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain" "${_PSZ_REG_VAL}"
!macroend
!define SHMessageBoxCheckCleanup "!insertmacro SHMessageBoxCheckCleanup"
 
 
!macro SHMessageBoxCheck _CAPTION _TEXT _TYPE
    # this would be the simple way (by name)
    # System::Call "shlwapi::SHMessageBoxCheck(p $HWNDPARENT, t '${_TEXT}', t '${_CAPTION}', i ${_TYPE}, i ${_DEFAULT}, t '${_PSZ_REG_VAL}') i .r0"
 
    # for backwards-compatibility we get the process address by specifying the function's ordinal value
    System::Call "kernel32::GetModuleHandle(t 'shlwapi.dll') p .s"
    System::Call "kernel32::GetProcAddress(p s, i ${_SHMessageBoxCheck_Ordinal}) p .r0"
    System::Call "::$0(p $HWNDPARENT, t '${_TEXT}', t '${_CAPTION}', i ${_TYPE}, i ${_DEFAULT}, t '${_PSZ_REG_VAL}') i .r0"
 
    # save the user's choice (unless the default value was returned - then don't update and return the saved choice)
    StrCmp $0 ${_DEFAULT} +2 0
        StrCpy $_lastReturnValue $0
 
    # push the return value to the stack
    Push $_lastReturnValue
!macroend
!define SHMessageBoxCheck "!insertmacro SHMessageBoxCheck"