NSIS (Nullsoft Scriptable Install System): Add a Function to Check Hardware Specification


Saya baru mulai berkenalan dengan NSIS di bulan Agustus. Di suatu hari yang cerah *literally cerah karena masih summer dan sedang panas-panasnya*, manajer datang dan berkata, “From now on, these installation scripts are yours. Please maintain it. It should be easy because it’s quite similar with Java.”

Saya, yang belum melihat bentuk kodenya seperti apa, cuma mengangguk saja. Kemudian mulailah saya melihat-lihat seperti apakah NSIS itu. Kesan pertama saya adalah “eh, kok seperti tugas orkom dulu ya? Dimana miripnya sama Java?”. Saya kembali berpikir jangan-jangan si manajer ini mengatakan NSIS mirip Java karena dibuka di Eclipse. Manajer pun datang lagi dan memberikan tugas pertama terkait NSIS, “Please add a function to check hardware specification before start installing this program.”

Karena sudah diberi tugas, saya pun mulai menggunakan kekuatan Google untuk memahami NSIS. Setelah menghabiskan beberapa saat dengan paman Google, saya pun membuat kesimpulan mengenai hal-hal yang harus saya lakukan untuk membuat fungsi ini:

  1. Instalasi plugin NSIS yang dibutuhkan
  2. Membuat custom page untuk menampilkan hardware specification
  3. Membuat fungsi untuk menonaktifkan tombol Next jika spesifikasi tidak memenuhi batas minimal

Saya pun mencari cara bagaimana membuat custom page dan menemukan NSIS Dialog Designer. Software tersebut sangat membantu dalam pembuatan custom page. Langsung saja beranjak ke bagian kode:

#library
!include WMI.nsh
!include CPUFeatures.nsh
!insertmacro GetTime
!insertmacro GetDrives

#Page custom to show the hardware specification
page custom CheckHWSpecs

#Function to show the hardware specification on custom page
Function CheckHWSpecs
    !addplugindir "${NSISDIR}\Plugins"
    
    Var /GLOBAL CPUSpeed
    Var /GLOBAL RAM
    Var /GLOBAL VGA1
    Var /GLOBAL VGA2
    Var /GLOBAL WinEdition
    Var /GLOBAL CPUCore
    Var /GLOBAL VGATotal
    
    Var /GLOBAL CPUSpeedStatus
    Var /GLOBAL RAMStatus
    Var /GLOBAL VGA1Status
    Var /GLOBAL VGA2Status
    Var /GLOBAL VGAStatus
    Var /GLOBAL WinEditionStatus
    Var /GLOBAL CPUCoreStatus
    
    Var /GLOBAL Iteration
    
    Var /GLOBAL FileDate
    
    #check Hardware specification
    HwInfo::GetCpuSpeed
    StrCpy $CPUSpeed $0
    
    HwInfo::GetSystemMemory
    StrCpy $RAM $0
    
    ${GetWMI} root\CIMV2 Win32_VideoController Description callback_Function
    
    ReadRegStr $WinEdition HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "ProductName"
    
    ${CPUFeatures.GetCount} $CPUCore
    
    ${GetTime} "" "L" $0 $1 $2 $3 $4 $5 $6
    
    StrCpy $FileDate "$2-$1-$0"
    
    ; handle variables, it comes from NSIS Dialog Designer
    Var /GLOBAL hCtl_CheckHWSpecs
    Var /GLOBAL hCtl_CheckHWSpecs_lblWindowsEditionVar
    Var /GLOBAL hCtl_CheckHWSpecs_lblGraphicCardVar1
    Var /GLOBAL hCtl_CheckHWSpecs_lblGraphicCardVar2
    Var /GLOBAL hCtl_CheckHWSpecs_lblRAMVar
    Var /GLOBAL hCtl_CheckHWSpecs_lblCPUSpeedVar
    Var /GLOBAL hCtl_CheckHWSpecs_lblCPUCoreVar
    Var /GLOBAL hCtl_CheckHWSpecs_lblWindowsEditionStatus
    Var /GLOBAL hCtl_CheckHWSpecs_lblGraphicCardStatus
    Var /GLOBAL hCtl_CheckHWSpecs_lblRAMStatus
    Var /GLOBAL hCtl_CheckHWSpecs_lblCPUSpeedStatus
    Var /GLOBAL hCtl_CheckHWSpecs_lblCPUCoreStatus
    Var /GLOBAL hCtl_CheckHWSpecs_lblGraphicCard1
    Var /GLOBAL hCtl_CheckHWSpecs_lblGraphicCard2
    Var /GLOBAL hCtl_CheckHWSpecs_lblWindowsEdition
    Var /GLOBAL hCtl_CheckHWSpecs_lblCPUCore
    Var /GLOBAL hCtl_CheckHWSpecs_lblCPUSpeed
    Var /GLOBAL hCtl_CheckHWSpecs_lblRAM
    Var /GLOBAL hCtl_CheckHWSpecs_lblStatus
    Var /GLOBAL hCtl_CheckHWSpecs_lblHardware

    nsDialogs::Create 1018
    Pop $hCtl_CheckHWSpecs
    ${If} $hCtl_CheckHWSpecs == error
      Abort
    ${EndIf}
    !insertmacro MUI_HEADER_TEXT "Checking Hardware Specification" "Your hardware specification must meet the minimum requirements"
          
    ; === lblGraphicCard1 (type: Label) ===
    ${NSD_CreateLabel} 30.94u 78.77u 65.82u 14.15u "Graphic Card 1:"
    Pop $hCtl_CheckHWSpecs_lblGraphicCard1
      
    ; === lblGraphicCard2 (type: Label) ===
    ${NSD_CreateLabel} 30.94u 92.92u 65.82u 14.15u "Graphic Card 2:"
    Pop $hCtl_CheckHWSpecs_lblGraphicCard2
    
    ; === lblWindowsEdition (type: Label) ===
    ${NSD_CreateLabel} 30.94u 107.07u 65.82u 14.15u "Windows Edition:"
    Pop $hCtl_CheckHWSpecs_lblWindowsEdition
      
    ; === lblCPUCore (type: Label) ===
    ${NSD_CreateLabel} 30.94u 36.31u 65.82u 14.15u "CPU Core:"
    Pop $hCtl_CheckHWSpecs_lblCPUCore
      
    ; === lblCPUSpeed (type: Label) ===
    ${NSD_CreateLabel} 30.94u 50.46u 65.82u 14.15u "CPU Speed:"
    Pop $hCtl_CheckHWSpecs_lblCPUSpeed
      
    ; === lblRAM (type: Label) ===
    ${NSD_CreateLabel} 30.94u 64.62u 65.82u 14.15u "RAM:"
    Pop $hCtl_CheckHWSpecs_lblRAM
      
    ; === lblStatus (type: Label) ===
    ${NSD_CreateLabel} 229.72u 14.77u 25.01u 14.15u "Status"
    Pop $hCtl_CheckHWSpecs_lblStatus
      
    ; === lblHardware (type: Label) ===
    ${NSD_CreateLabel} 90.84u 14.77u 37.52u 14.15u "Hardware"
    Pop $hCtl_CheckHWSpecs_lblHardware
      
    ; === HLine1 (type: HLine) ===
    ${NSD_CreateHLine} 19.75u 29.69u 261.34u 1.23u ""

    ; === VLine1 (type: VLine) ===
    ${NSD_CreateVLine} 205.37u 4.92u 1.32u 120.30u ""
      
    ; === lblWindowsEditionVar (type: Label) ===
    ${NSD_CreateLabel} 99.39u 107.07u 95.44u 14.15u "$WinEdition"
    Pop $hCtl_CheckHWSpecs_lblWindowsEditionVar
      
    ; === lblGraphicCardVar (type: Label) ===
    ${NSD_CreateLabel} 99.39u 92.92u 95.44u 14.15u "$VGA2"
    Pop $hCtl_CheckHWSpecs_lblGraphicCardVar2
    ${If} $VGA2Status == "not supported"
        SetCtlColors $hCtl_CheckHWSpecs_lblGraphicCardVar2 0xFF0000 0xF0F0F0
    ${EndIf}
    
    ; === lblGraphicCardVar (type: Label) ===
    ${NSD_CreateLabel} 99.39u 78.77u 95.44u 14.15u "$VGA1"
    Pop $hCtl_CheckHWSpecs_lblGraphicCardVar1
    ${If} $VGA1Status == "not supported"
        SetCtlColors $hCtl_CheckHWSpecs_lblGraphicCardVar1 0xFF0000 0xF0F0F0
    ${EndIf}
      
    ; === lblRAMVar (type: Label) ===
    ${NSD_CreateLabel} 99.39u 64.62u 95.44u 14.15u "$RAM MB"
    Pop $hCtl_CheckHWSpecs_lblRAMVar
    
    ; === lblCPUSpeedVar (type: Label) ===
    ${NSD_CreateLabel} 99.39u 50.46u 95.44u 14.15u "$CPUSpeed Mhz"
    Pop $hCtl_CheckHWSpecs_lblCPUSpeedVar
      
    ; === lblCPUCoreVar (type: Label) ===
    ${NSD_CreateLabel} 99.39u 36.31u 95.44u 14.15u "$CPUCore Core"
    Pop $hCtl_CheckHWSpecs_lblCPUCoreVar
    
    ; === lblCPUCoreStatus (type: Label) ===
    ${If} $CPUCore >= 4
        ${NSD_CreateLabel} 206.0u 36.31u 90.84u 12.31u "Requirement met"
        Pop $hCtl_CheckHWSpecs_lblCPUCoreStatus
        ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblCPUCoreStatus ${SS_CENTER}
        StrCpy $CPUCoreStatus "supported"
    ${Else}
        ${NSD_CreateLabel} 206.0u 36.31u 90.84u 12.31u "Requirement not met"
        Pop $hCtl_CheckHWSpecs_lblCPUCoreStatus
        ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblCPUCoreStatus ${SS_CENTER}
        SetCtlColors $hCtl_CheckHWSpecs_lblCPUCoreStatus 0xFF0000 0xF0F0F0 ;change the text color to red
        StrCpy $CPUCoreStatus "not supported"
    ${EndIf}
    
    ; === lblCPUSpeedStatus (type: Label) ===
    ${If} $CPUSpeed >= 2048
        ${NSD_CreateLabel} 206.0u 50.46u 90.84u 12.31u "Requirement met"
        Pop $hCtl_CheckHWSpecs_lblCPUSpeedStatus
        ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblCPUSpeedStatus ${SS_CENTER}
        StrCpy $CPUSpeedStatus "supported"
    ${Else}
        ${NSD_CreateLabel} 206.0u 50.46u 90.84u 12.31u "Requirement not met"
        Pop $hCtl_CheckHWSpecs_lblCPUSpeedStatus
        ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblCPUSpeedStatus ${SS_CENTER}
        SetCtlColors $hCtl_CheckHWSpecs_lblCPUSpeedStatus 0xFF0000 0xF0F0F0 ;change the text color to red
        StrCpy $CPUSpeedStatus "not supported"
    ${EndIf}
    
    ; === lblRAMStatus (type: Label) ===
    ${If} $RAM >= 4096
        ${NSD_CreateLabel} 206.0u 64.62u 90.84u 12.31u "Requirement met"
        Pop $hCtl_CheckHWSpecs_lblRAMStatus
        ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblRAMStatus ${SS_CENTER}
        StrCpy $RAMStatus "supported"
    ${Else}
        ${NSD_CreateLabel} 206.0u 64.62u 90.84u 12.31u "Requirement not met"
        Pop $hCtl_CheckHWSpecs_lblRAMStatus
        ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblRAMStatus ${SS_CENTER}
        SetCtlColors $hCtl_CheckHWSpecs_lblRAMStatus 0xFF0000 0xF0F0F0 ;change the text color to red
        StrCpy $RAMStatus "not supported"
    ${EndIf}
    
    ; === lblGraphicCardStatus (type: Label) ===
    ${If} $VGATotal > 1
        ${If} $VGA1Status == "not supported"
        ${AndIf} $VGA2Status == "not supported"
            StrCpy $VGAStatus "not supported"
            ${NSD_CreateLabel} 206.0u 78.77u 90.84u 12.31u "Requirement not met"
            Pop $hCtl_CheckHWSpecs_lblGraphicCardStatus
            ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblGraphicCardStatus ${SS_CENTER}
            SetCtlColors $hCtl_CheckHWSpecs_lblGraphicCardStatus 0xFF0000 0xF0F0F0 ;change the text color to red
        ${Else}
            StrCpy $VGAStatus "supported"
            ${If} $VGA1Status == "not supported"
            ${AndIf} $VGA2Status == "supported"
                ${NSD_CreateLabel} 206.0u 78.77u 90.84u 12.31u "Requirement partially met"
            ${EndIf}
            ${If} $VGA2Status == "not supported"
            ${AndIf} $VGA1Status == "supported"
                ${NSD_CreateLabel} 206.0u 78.77u 90.84u 12.31u "Requirement partially met"
            ${EndIf}
            ${If} $VGA1Status == "supported"
            ${AndIf} $VGA2Status == "supported"
                ${NSD_CreateLabel} 206.0u 78.77u 90.84u 12.31u "Requirement met"
            ${EndIf}  
            Pop $hCtl_CheckHWSpecs_lblGraphicCardStatus
            ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblGraphicCardStatus ${SS_CENTER}
        ${EndIf}
    ${Else}
        ${If} $VGA1Status == "not supported"
            StrCpy $VGAStatus "not supported"
            ${NSD_CreateLabel} 206.0u 78.77u 90.84u 12.31u "Requirement not met"
            Pop $hCtl_CheckHWSpecs_lblGraphicCardStatus
            ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblGraphicCardStatus ${SS_CENTER}
            SetCtlColors $hCtl_CheckHWSpecs_lblGraphicCardStatus 0xFF0000 0xF0F0F0
        ${Else}
            StrCpy $VGAStatus "supported"
            ${NSD_CreateLabel} 206.0u 78.77u 90.84u 12.31u "Requirement met"
            Pop $hCtl_CheckHWSpecs_lblGraphicCardStatus
            ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblGraphicCardStatus ${SS_CENTER}
        ${EndIf}
    ${EndIf}
    
    ; === lblWindowsEditionStatus (type: Label) === 
    ${If} $WinEdition == "Windows 7 Professional"
    ${OrIf} $WinEdition == "Windows 7 Enterprise"
    ${OrIf} $WinEdition == "Windows 7 Ultimate"
    ${OrIf} $WinEdition == "Windows 7 Ultimate N"
    ${OrIf} $WinEdition == "Windows 7 Professional N"
        ${NSD_CreateLabel} 206.0u 107.07u 90.84u 12.31u "Requirement met"
        Pop $hCtl_CheckHWSpecs_lblWindowsEditionStatus
        ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblWindowsEditionStatus ${SS_CENTER}
        StrCpy $WinEditionStatus "supported"
    ${Else}
        ${NSD_CreateLabel} 206.0u 107.07u 90.84u 12.31u "Requirement not met"
        Pop $hCtl_CheckHWSpecs_lblWindowsEditionStatus
        ${NSD_AddStyle} $hCtl_CheckHWSpecs_lblWindowsEditionStatus ${SS_CENTER}
        SetCtlColors $hCtl_CheckHWSpecs_lblWindowsEditionStatus 0xFF0000 0xF0F0F0 ;change the text color to red
        StrCpy $WinEditionStatus "not supported"
    ${EndIf}
    
    ${If} $CPUCoreStatus == "not supported"
    ${OrIf} $CPUSpeedStatus == "not supported"
    ${OrIf} $RAMStatus == "not supported"
    ${OrIf} $VGAStatus == "not supported"
    ${OrIf} $WinEditionStatus == "not supported"
        Call DisableNextBackButton
    ${Else}
        Call EnableNextBackButton
    ${EndIf}
    
    nsDialogs::Show
FunctionEnd

#Function to check VGA cards
Function callback_Function
    #$R0 = result number, $R1 = total results, $R2 = result name
    StrCpy $Iteration $R0
    StrCpy $VGATotal $R1
    
    ${If} $VGATotal == 1
        StrCpy $VGA2 "-"
    ${EndIf}
    
    ${If} $Iteration == 1
    ${AndIf} $Iteration <= $R1
        StrCpy $VGA1 $R2
        Push $VGA1
        Push "Intel"
        Call StrContains
        Pop $0
        StrCmp $0 "" notfound
            StrCpy $VGA1Status "not supported"
            Goto done
        notfound:
            StrCpy $VGA1Status "supported"
        done:
    ${EndIf}
    
    ${If} $Iteration == 2
    ${AndIf} $Iteration <= $R1
        StrCpy $VGA2 $R2
        Push $VGA2
        Push "Intel"
        Call StrContains
        Pop $0
        StrCmp $0 "" notfoundIntel
            StrCpy $VGA2Status "not supported"
            Goto doneCheck
        notfoundIntel:
            StrCpy $VGA2Status "supported"
        doneCheck:
    ${EndIf}
    #MessageBox MB_OK "$R0/$R1=$R2"
FunctionEnd

Function DisableNextBackButton
    ; === disable Next button
    GetDlgItem $0 $HWNDPARENT 1
    EnableWindow $0 0

    ; === disable Back button
    GetDlgItem $1 $HWNDPARENT 3
    EnableWindow $1 0
FunctionEnd

Function EnableNextBackButton
    ; === enable Next button
    GetDlgItem $0 $HWNDPARENT 1
    EnableWindow $0 1

    ; === enable Back button
    GetDlgItem $1 $HWNDPARENT 3
    EnableWindow $1 1
FunctionEnd

Kodenya ternyata cukup panjang karena desain custom page harus dilakukan secara manual (pengaturan posisi, pengaturan warna tulisan, dll). Penjelasan singkat dari kode di atas ialah:

  • CheckHWSpecs ialah callback function yang dipanggil ketika akan membuat custom page. Oleh karena itu, pengaturan posisi label, warna tulisan, dan fungsi untuk menonaktifkan/mengaktifkan tombol harus ditempatkan di dalam fungsi tersebut
  • HwInfo merupakan salah satu plugin yang digunakan. Jenis plugin yang digunakan di dalam NSIS ada dua tipe: .nsh dan .dll. Jika menggunakan .nsh, maka harus memasukkan !include namafile.nsh sedangkan jika hanya ada .dll, maka sebaiknya memasukkan !addplugindir "${NSISDIR}\Plugins" untuk memastikan ketika di-compile, script bisa menemukan plugin yang dimaksud
  • Tombol Back, Next, dan Cancel di NSIS memiliki kode masing-masing. 1 untuk Next, 2 untuk Cancel, dan 3 untuk Back

Setelah selesai, ternyata ada dua versi yang diinginkan oleh manajer:

  1. Kalau spesifikasi tidak memenuhi dan versi installer-nya Professional, maka tombol Next dinonaktifkan sehingga user tidak dapat melanjutkan proses instalasi
  2. Kalau spesifikasi tidak memenuhi dan versi installer-nya Standard, maka akan ada peringatan mengenai isu performansi dan user tetap dapat melanjutkan proses instalasi

Versi pertama sudah ditangani oleh kode yang dibuat di atas sedangkan untuk versi kedua perlu dilakukan beberapa perubahan termasuk menambah sebuah fungsi untuk memunculkan peringatan.

#add leave callback function
page custom CheckHWSpecs ShowNotMeetRequirementDialog

Function ShowNotMeetRequirementDialog
    ${If} $CPUCoreStatus == "not supported"
    ${OrIf} $CPUSpeedStatus == "not supported"
    ${OrIf} $RAMStatus == "not supported"
    ${OrIf} $VGAStatus == "not supported"
    ${OrIf} $WinEditionStatus == "not supported"
        !define msg1 "Your computer does not meet the hardware requirements and may cause performance issue.$\r$\n"
        !define msg2 "Do you wish to continue installation?$\r$\n"
        MessageBox MB_YESNO|MB_ICONQUESTION  "${msg1}${msg2}" IDYES cont IDNO stopInst
        stopInst:
          Abort
        cont:
          Goto nextstep
        nextstep:
    ${EndIf}
FunctionEnd

Perubahan yang terjadi adalah:

  • Penambahan callback function ShowNotMeetRequirementDialog di page custom. Fungsi ini akan dipanggil ketika user menekan tombol Next
  • Fungsi DisableNextBackButton dan EnableNextBackButton juga sudah tidak dibutuhkan lagi untuk versi kedua

Saya sendiri menghabiskan waktu berjam-jam untuk membuat seluruh kode di atas (sebagian besar waktunya dihabiskan dengan paman Google untuk memahami NSIS). Semoga penjelasan ini bisa membantu memahami NSIS😀

再見!

signatureblack

 

 

 

One thought on “NSIS (Nullsoft Scriptable Install System): Add a Function to Check Hardware Specification

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s