Boot Into
From NSIS Wiki
Jump to navigationJump to search
Description
The script displays the boot entries in a list view and boots into the selected boot entry in case the Boot button is going to be clicked.
Script
; Licensed under the zlib/libpng license (same as NSIS) ; ; Unicode builds: ; x86 unicode ; makensis "-XTarget x86-unicode" script.nsi ; amd64 unicode ; makensis "-XTarget amd64-unicode" script.nsi Name "Boot Into" !include MUI2.nsh !include bootcfg.nsh ; Include required functions ${BOOTCFG_ConnectWMI} ${BOOTCFG_GetObject} ${BOOTCFG_OpenDefaultBcdStore} ${BOOTCFG_GetBcdObject} ${BOOTCFG_GetBcdElement} ${BOOTCFG_GetObjectPropertyValue} ${BOOTCFG_GetElementFromBcd} !define BOOTCFG_ENUMERATION_CALLBACK AddBootEntry ${BOOTCFG_EnumerateBcdObjectList} ${BOOTCFG_SetActiveBootEntry} !ifndef LVM_GETITEMTEXT !define /math LVM_GETITEMTEXTA ${LVM_FIRST} + 45 !define /math LVM_GETITEMTEXTW ${LVM_FIRST} + 115 ${_NSIS_DEFAW} LVM_GETITEMTEXT !endif !ifndef LVIS_FOCUSED !define LVIS_FOCUSED 0x1 !endif !ifndef LVIS_SELECTED !define LVIS_SELECTED 0x2 !endif !define NSD_CreateListView "nsDialogs::CreateControl SysListView32 \ ${DEFAULT_STYLES}|${LVS_REPORT}|${LVS_SHOWSELALWAYS}|${LVS_SINGLESEL} \ ${WS_EX_WINDOWEDGE}|${WS_EX_CLIENTEDGE}" !define NSD_LV_InsertColumn `!insertmacro __NSD_LV_InsertColumn` !macro __NSD_LV_InsertColumn CONTROL COLUMN WIDTH STRING ; Fill LVCOLUMN structure System::Call "*(i${LVCF_WIDTH}|${LVCF_TEXT},i,i${WIDTH}, \ t'${STRING}',i${NSIS_MAX_STRLEN},i,i,i,i,i,i) p.s" Exch $0 SendMessage ${CONTROL} ${LVM_INSERTCOLUMN} ${COLUMN} $0 System::Free $0 Pop $0 !macroend !define NSD_LV_InsertItem `!insertmacro __NSD_LV_InsertItem` !macro __NSD_LV_InsertItem CONTROL INDEX STRING System::Call "*(i${LVIF_TEXT},i${INDEX},i,i,&i${NSIS_PTR_SIZE},\ t'${STRING}',i${NSIS_MAX_STRLEN},i,p,i,i,i,i,i,i) p.s" Exch $0 SendMessage ${CONTROL} ${LVM_INSERTITEM} 0 $0 System::Free $0 Pop $0 !macroend !define NSD_LV_SetItemState `!insertmacro __NSD_LV_SetItemState` !macro __NSD_LV_SetItemState CONTROL INDEX STATE System::Call "*(i${LVIF_STATE},i${INDEX},i,\ i${STATE},&i${NSIS_PTR_SIZE} ${STATE},\ t,i,i,p,i,i,i,i,i,i) p.s" Exch $0 SendMessage ${CONTROL} ${LVM_SETITEMSTATE} ${INDEX} $0 System::Free $0 Pop $0 !macroend !define NSD_LV_SetItemText `!insertmacro __NSD_LV_SetItemText` !macro __NSD_LV_SetItemText CONTROL INDEX SUBINDEX STRING System::Call "*(i${LVIF_TEXT},i${INDEX},i${SUBINDEX},i,\ &i${NSIS_PTR_SIZE},t'${STRING}',i${NSIS_MAX_STRLEN},\ i,p,i,i,i,i,i,i) p.s" Exch $0 SendMessage ${CONTROL} ${LVM_SETITEMTEXT} ${INDEX} $0 System::Free $0 Pop $0 !macroend Var ListView Var Services Var Locator Var BaseBcdStore Var BaseBcdObject Var BcdStore !define MUI_CUSTOMFUNCTION_ABORT CleanUp Page custom BootSelector Boot !define MUI_PAGE_CUSTOMFUNCTION_PRE Skip !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_LANGUAGE "English" Function .onInit Push $0 ${BOOTCFG_ConnectWMI} $Locator $Services $0 ${If} $0 != 0 IntFmt $0 "0x%08X" $0 MessageBox MB_OK "$Services: $0" StrCpy $Services "" StrCpy $Locator "" ${EndIf} Pop $0 FunctionEnd Function .onRebootFailed MessageBox MB_OK|MB_ICONSTOP "Reboot failed. Please reboot manually." \ /SD IDOK FunctionEnd ; Skip page Function Skip Quit FunctionEnd ; Determine the boot entries and present them via the list view control Function SetUp ; Save registers Push $0 Push $1 Push $2 Push $3 Push $4 Push $5 ${If} $Services != "" ${BOOTCFG_GetObject} $Services "BcdStore" $BaseBcdStore $0 ${If} $0 != 0 StrCpy $1 $BaseBcdStore StrCpy $BaseBcdStore "" ${Else} ${BOOTCFG_GetObject} $Services "BcdObject" $BaseBcdObject $0 ${If} $0 != 0 StrCpy $1 $BaseBcdObject StrCpy $BaseBcdObject "" ${Else} ${BOOTCFG_OpenDefaultBcdStore} $Services $BaseBcdStore \ $BcdStore $0 ${If} $0 != 0 StrCpy $1 $BcdStore StrCpy $BcdStore "" ${Else} ${BOOTCFG_GetBcdObject} $Services $BaseBcdStore \ $BcdStore ${BOOTCFG_CURRENT_GUID} $2 $0 ; bcdobject=$5 ${If} $0 != 0 StrCpy $1 $2 ${Else} ${BOOTCFG_GetBcdElement} $Services $BaseBcdObject \ $2 ${BOOTCFG_BCDE_AUTORECOVERYENABLED} \ $1 $0 ${If} $0 == 0 ${BOOTCFG_GetObjectPropertyValue} $1 "Boolean" $3 $0 ; Release element [auto recovery enabled] ${BOOTCFG_ReleaseObject} $1 ${If} $0 == 0 System::Call "*$3(i .r0, i, i .r1)" System::Free $3 IntOp $0 $0 & 0xFFFF IntOp $0 $0 - ${VT_BOOL} ${If} $0 == 0 ${AndIf} $1 != 0 ${BOOTCFG_GetBcdElement} $Services $BaseBcdObject \ $2 ${BOOTCFG_BCDE_RECOVERYSEQUENCE} $3 $0 ${EndIf} ${EndIf} ${EndIf} ; Release bcdobject ${BOOTCFG_ReleaseObject} $2 ${EndIf} ${If} $0 != 0 StrCpy $3 "" ${EndIf} ${BOOTCFG_GetElementFromBcd} $Services $BaseBcdStore \ $BcdStore $BaseBcdObject ${BOOTCFG_BOOTMGR_GUID} \ ${BOOTCFG_DISPLAY_ORDER} $2 $4 $0 ; element=$4 ; bcdobject=$2 ${If} $0 != 0 StrCpy $1 $4 ${Else} ; Release bcdobject ${BOOTCFG_ReleaseObject} $2 ${If} $3 != "" ; recovery boot entries ${BOOTCFG_EnumerateBcdObjectList} $Services \ $BaseBcdStore $BcdStore $BaseBcdObject $3 $1 ; Release object list element ${BOOTCFG_ReleaseObject} $3 ${EndIf} ${BOOTCFG_EnumerateBcdObjectList} $Services $BaseBcdStore \ $BcdStore $BaseBcdObject $4 $1 ; Release object list element ${BOOTCFG_ReleaseObject} $4 ${EndIf} ${EndIf} ${EndIf} ${EndIf} ${Else} StrCpy $1 "Invalid $$Services variable" StrCpy $0 ${ERROR_INVALID_DATA} ${EndIf} ${If} $0 != 0 IntFmt $0 "0x%08X" $0 MessageBox MB_OK "$1: $0" ${EndIf} ; Restore registers Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd Function CleanUp ${BOOTCFG_ReleaseObject} $BcdStore ${BOOTCFG_ReleaseObject} $BaseBcdObject ${BOOTCFG_ReleaseObject} $BaseBcdStore ${BOOTCFG_ReleaseObject} $Services ${BOOTCFG_ReleaseObject} $Locator FunctionEnd ; Add boot entry to list view control ; Parameters ; name - description of boot entry ; GUID - unique identifier of boot entry Function AddBootEntry Exch $0 Exch Exch $1 Push $2 ; name=$1 GUID=$0 SendMessage $ListView ${LVM_GETITEMCOUNT} "" "" $2 ${NSD_LV_InsertItem} $ListView $2 $1 ${NSD_LV_SetItemText} $ListView $2 1 $0 Pop $2 Pop $1 Pop $0 FunctionEnd ; Dialog to select boot entry Function BootSelector !insertmacro MUI_HEADER_TEXT "Boot Into" "Select an entry from below list to boot" nsDialogs::Create 1018 Pop $0 ${If} $0 != error ; Change the label of the "Next" button GetDlgItem $1 $HWNDPARENT 1 SendMessage $1 ${WM_SETTEXT} 0 "STR:Boot" ; Create list view control ${NSD_CreateListView} 0 0 100% 100% "ListView" Pop $ListView ; Explicitly set full row selection SendMessage $ListView ${LVM_SETEXTENDEDLISTVIEWSTYLE} 0 ${LVS_EX_FULLROWSELECT} ; Calculate width of columns System::Call "user32::GetClientRect(p $ListView, @r2) i.r0" ${If} $0 != 0 ; 2/5 (40%) of width for first column and 3/5 (60%) for second column System::Call "*$2(i.r0, i, i.r3, i)" IntOp $3 $3 - $0 IntOp $2 $3 / 5 IntOp $2 $2 + $2 IntOp $3 $3 - $2 ${Else} ; Fall back to default values StrCpy $2 100 StrCpy $3 200 ${EndIf} ${NSD_LV_InsertColumn} $ListView 0 $2 "Label" ${NSD_LV_InsertColumn} $ListView 1 $3 "GUID" Call SetUp ; Select first item in list ${NSD_LV_SetItemState} $ListView 0 ${LVIS_FOCUSED}|${LVIS_SELECTED} nsDialogs::Show ${Else} Abort ${EndIf} FunctionEnd Function Boot ; Go through the items and find the selected one SendMessage $ListView ${LVM_GETITEMCOUNT} "" "" $1 ${If} $1 > 0 IntOp $1 $1 - 1 ${For} $0 0 $1 SendMessage $ListView ${LVM_GETITEMSTATE} $0 ${LVIS_SELECTED} $2 ${If} $2 != 0 System::Call '*(&t${NSIS_MAX_STRLEN})p.r3' ; Choose second column => subindex = 1 System::Call "*(i${LVIF_TEXT},i,i 1,i,&i${NSIS_PTR_SIZE},\ pr3,i${NSIS_MAX_STRLEN},i,p,i,i,i,i,i,i) p.r4" SendMessage $ListView ${LVM_GETITEMTEXT} $0 $4 $2 ${If} $2 > 0 System::Call "*$3(&t$2.r2)" ${BOOTCFG_SetActiveBootEntry} $Services $BaseBcdStore \ $BcdStore $BaseBcdObject $2 $6 $5 ${If} $5 != 0 IntFmt $5 "0x%08X" $5 MessageBox MB_OK "$6: $5" ${Else} ${BOOTCFG_ReleaseObject} $6 SetRebootFlag true ${EndIf} ${EndIf} System::Free $4 System::Free $3 ${EndIf} ${Next} ${EndIf} Call CleanUp ${If} ${RebootFlag} Reboot ${EndIf} FunctionEnd Section -Ignore SectionEnd