수성컴전자방입니다. 3월 한 달간 NSIS 시리즈를 진행하고 있는데요, 정작 기초적인 프로그래밍을 안 알려드린 것 같아서 이 글로 정리하려고 합니다.
목차
1. NSIS + HM NIS Edit 설치
2. 기본적인 Windows용 설치 프로그램(설치기) 만들기
3. Section과 SectionGroup
4. 환영 이미지, 헤드 이미지, 브랜딩 텍스트, 스플래시 이미지
5. 프로그래밍(변수, 사칙연산, 분기, 조건문, 반복문)(현재 글)
5.1. MessageBox 띄우기
5.2. 변수와 사칙연산
5.3. 상대 분기
5.4. label을 사용한 분기
5.5. StrCmp를 사용한 분기
5.6. If, ElseIf, Else
5.7. AndIf와 OrIf
5.8. Switch…Case
5.9. While
5.10. For
5.11. Do…LoopUntil
5.12. 글 마무리
5.13. 참고 자료
5.1. MessageBox 띄우기
프로그래밍 결과를 출력하기 위해 MessageBox를 간단하게 공부하겠습니다.
5.1.1. 오늘은 설치기를 만드는 것이 목적이 아니기 때문에 프로그램 정보, 설치 경로 등의 코드는 싹 다 지우고 Modern UI도 쓰지 않은 채로 Section 하나만 가지고 진행하겠습니다. 아래와 같이 코드를 작성해 봅시다.
Section "MainSection" SEC01
MessageBox MB_OK "Hello, world!"
SetAutoClose true
SectionEnd
[Line 2]
MessageBox MB_OK “Hello, world!“
MessageBox를 띄우는 코드입니다. HM NIS Edit에서 도구(T)→코드 템플릿→msgbox1을 클릭하여 작성할 수도 있습니다.
[Line 3]
SetAutoClose true
설치기 창을 자동으로 닫는 코드입니다. MessageBox의 확인 단추를 클릭했을 때 바로 창이 닫히도록 이 코드를 썼습니다.
5.1.2. 컴파일 및 실행합니다.
5.1.3. “Hello, world!”라고 적힌 창이 떴습니다. 확인 단추가 하나 있습니다. 확인을 클릭하면 설치 프로그램이 종료됩니다.
MB_OK 대신 다른 옵션을 쓸 수도 있습니다.
HM NIS Edit에서 도움말(?)→NSIS 도움말로 들어가서 MessageBox를 검색하면 여러 옵션을 확인할 수 있습니다.
여러 옵션을 동시에 사용할 때는 |(Shift+\)를 사용합니다.
5.2. 변수와 사칙연산
Var num1
Var num2
Var result
Section "MainSection" SEC01
MessageBox MB_OK "사칙연산 보여드리겠습니다."
StrCpy $num1 "15"
StrCpy $num2 "6"
IntOp $result $num1 + $num2
MessageBox MB_OK "$num1 + $num2 = $result"
IntOp $result $num1 - $num2
MessageBox MB_OK "$num1 - $num2 = $result"
IntOp $result $num1 * $num2
MessageBox MB_OK "$num1 × $num2 = $result"
IntOp $result $num1 / $num2
MessageBox MB_OK "$num1 ÷ $num2 = $result"
SetAutoClose true
SectionEnd
[Line 1~3 변수 선언]
Var 변수명
[변수를 사용할 때]
$변수명
위에서 선언한 변수를 사용하려면 변수명 앞에 $를 붙입니다.
[Line 8~9 변수 초기화]
StrCpy $변수명 ”값”
StrCpy라서 문자열 복사에만 쓸 것 같지만, 정수 값으로 초기화할 때도 StrCpy를 씁니다. 문자열을 쓰듯이 값을 큰따옴표(“)로 감싸 주어야 합니다.
8행으로 예를 들어 설명해 드리겠습니다.
StrCpy $num1 ”15”
이 코드는 num1 변수에 15를 넣습니다.
[Line 11, 14, 17, 20 사칙연산]
IntOp $result $num1 연산자 $num2
(주의: 변수와 연산자 사이 띄어쓰기 반드시 할 것!!)
- 연산자
- +: 덧셈
- -: 뺄셈
- *: 곱셈
- /: 나눗셈(나머지는 버림)
11행으로 예를 들면
IntOp $result $num1 + $num2
이 코드는 C언어의 result = num1 + num2;와 같습니다.
[출력 결과]
사칙연산이 잘 진행되었습니다.
정수 연산이므로 /(나눗셈)의 나머지는 버려졌습니다.
[참고 - 레지스터]
$0, $1, $2, $3, $4, $5, $6, $7, $8, $9
$R0, $R1, $R2, $R3, $R4, $R5, $R6, $R7, $R8, $R9
이 변수들은 레지스터입니다. 따로 선언할 필요 없이 사용할 수 있습니다. 위의 예제를 레지스터만으로 구현하면 아래와 같습니다.
Section "MainSection" SEC01
MessageBox MB_OK "사칙연산 보여드리겠습니다."
StrCpy $0 "15"
StrCpy $1 "6"
IntOp $2 $0 + $1
MessageBox MB_OK "$0 + $1 = $2"
IntOp $2 $0 - $1
MessageBox MB_OK "$0 - $1 = $2"
IntOp $2 $0 * $1
MessageBox MB_OK "$0 * $1 = $2"
IntOp $2 $0 / $1
MessageBox MB_OK "$0 + $1 = $2"
SetAutoClose true
SectionEnd
출력 결과는 같으니 생략하겠습니다.
5.3. 상대 분기
Section "MainSection" SEC01
MessageBox MB_OK "Hello, world!"
goto +2
MessageBox MB_OK "Hello, world!(+1)"
MessageBox MB_OK "Hello, world!(+2)"
SetAutoClose true
SectionEnd
[Line 3 goto]
goto +2
이 코드에 의해 2줄 아래인 5행으로 이동합니다.(4행은 건너뜀.)
+2의 부호와 숫자를 조정하여 이동할 위치를 지정할 수 있습니다.
[출력 결과]
5.4. label을 사용한 분기
어셈블리어처럼 label을 사용할 수도 있습니다.
Section "MainSection" SEC01
MessageBox MB_OK "레이블 분기"
goto B
A:
MessageBox MB_OK "A 레이블 실행됨."
B:
MessageBox MB_OK "B 레이블 실행됨."
C:
MessageBox MB_OK "C 레이블 실행됨."
SetAutoClose true
SectionEnd
[Line 5~10 label 설정]
label이름: 실행할 내용(다음 줄 이하에 써도 됨.)
[Line 3 goto]
goto B
B에 해당하는 7행으로 이동합니다.
[출력 결과]
3행의 goto문에 의해 B에 해당하는 7행으로 이동하여 7~10행의 코드들이 실행되었습니다.
5.5. StrCmp를 사용한 분기
일반적으로 프로그래밍 언어에서 strcmp는 문자열을 비교하는 함수이지만 NSIS에서는 정수를 비교하여 분기시킬 때 StrCmp를 사용합니다.
Var num
Section "MainSection" SEC01
MessageBox MB_OK "num==5인가?"
StrCpy $num "5"
StrCmp $num 5 A B
A:
MessageBox MB_OK "그렇다."
goto next
B:
MessageBox MB_OK "아니다."
next:
SetAutoClose true
SectionEnd
[Line 7 StrCmp]
StrCmp $num 5 A B
num==5이면 A를 실행하고, num!=5이면 B를 실행합니다.
[Line 10 goto]
goto next
B를 건너뛰기 위해 ‘next’라는 label을 만들고 B 직전에 next로 건너뛰게 만들었습니다.
[출력 결과]
6행에서 num에 5를 넣었으므로 num==5라서 A에 해당하는 내용이 실행되었습니다. 그 다음 next로 건너뛰었으므로 B는 실행되지 않았습니다.
5.6. If, ElseIf, Else
StrCmp와 goto만으로 모든 조건문과 반복문을 구현하는 것은 어렵습니다. 따라서 LogicLib이라는 라이브러리를 사용하여 If, ElseIf, Else, Switch, Case, While, For, LoopUntil 등을 사용해 보겠습니다. 우선 이 문단에서는 If, ElseIf, Else문부터 다룰 것입니다.
Modern UI를 사용하는 경우 LogicLib은 자동으로 탑재됩니다. 그러나 오늘은 Modern UI를 사용하지 않고 있으므로 !include LogicLib.nsh를 따로 써 주겠습니다.
!include LogicLib.nsh ;MUI.nsh include 시 LogicLib.nsh는 따로 include하지 않아도 됨.
Section "MainSection" SEC01
MessageBox MB_OK "3과 5의 대소 비교"
${If} 3 > 5
MessageBox MB_OK "3이 5보다 크다."
${ElseIf} 3 == 5
MessageBox MB_OK "3은 5와 같다."
${Else}
MessageBox MB_OK "3이 5보다 작다."
${EndIf}
SetAutoClose true
SectionEnd
${If}와 ${ElseIf} 오른쪽에 각각의 조건을 적어 주시면 됩니다. 이때 조건식의 수와 연산자 사이는 꼭 띄어써야 합니다.
컴파일 및 실행해 보겠습니다.
6행에서 3>5가 거짓입니다. 8행에서 3==5도 거짓입니다. 따라서 Else문의 내용인 10행이 실행되었습니다.
5.7. AndIf와 OrIf
NSIS는 조건식에 &&, || 등의 논리연산자를 사용할 수 없습니다. 그 대신 AndIf와 OrIf를 사용합니다.
!include LogicLib.nsh ;MUI.nsh include 시 LogicLib.nsh는 따로 include하지 않아도 됨.
Section "MainSection" SEC01
MessageBox MB_OK "AndIf와 OrIf 예제"
;AndIf 예제
${If} 3 <= 5
${AndIf} 1 != 1
MessageBox MB_OK "3 <= 5 && 1 != 1"
${Else}
MessageBox MB_OK "!(3 <= 5 && 1 != 1)"
${EndIf}
;OrIf 예제
${If} 3 <= 5
${OrIf} 1 != 1
MessageBox MB_OK "3 <= 5 || 1 != 1"
${Else}
MessageBox MB_OK "!(3 <= 5 || 1 != 1)"
${EndIf}
SetAutoClose true
SectionEnd
[Line 7~8 AndIf]
${If} 3 <= 5
${AndIf} 1 != 1
이 코드는 C언어로 치면 3 <= 5 && 1 != 1과 같습니다.
[Line 15~16 OrIf]
${If} 3 <= 5
${OrIf} 1 != 1
이 코드는 C언어로 치면 3 <= 5 || 1 != 1과 같습니다.
[출력 결과]
7~8행에서 3 <= 5는 참이지만 1 != 1은 거짓이므로 7~8행 AndIf문은 거짓이 되어 10~11행 Else문이 실행되었습니다.
15~16행에서 3 <= 5가 참이고 1 != 1이 거짓이므로 15~16행 OrIf문은 참이 되어 17행이 실행되었습니다.
5.8. Switch…Case
NSIS로 Switch…Case문도 사용할 수 있습니다.
!include LogicLib.nsh ;MUI.nsh include 시 LogicLib.nsh는 따로 include하지 않아도 됨.
Var score
Section "MainSection" SEC01
MessageBox MB_OK "등급에 따른 장학금 지급"
StrCpy $score "B"
${Switch} $score
${Case} 'A'
MessageBox MB_OK "A 등급 200만 원"
${Break}
${Case} 'B'
MessageBox MB_OK "B 등급 150만 원"
${Break}
${Case} 'C'
MessageBox MB_OK "C 등급 100만 원"
${Break}
${Case} 'D'
MessageBox MB_OK "D 등급 50만 원"
${Break}
${Default}
MessageBox MB_OK "불합격자이므로 미지급입니다."
${Break}
${EndSwitch}
SetAutoClose true
SectionEnd
[Line 10 Switch]
${Switch} $score
score 변수를 기준으로 나눕니다.
[Line 11, 14, 17, 20 Case]
${Case} 값
score가 값과 일치하면 해당 Case문의 코드를 실행합니다.
예) score가 ‘A’이면 12행 이하 실행
[Line 23 Default]
${Default}
위의 아무 조건도 만족시키지 못할 때 실행시킬 코드를 작성합니다.
[Line 13, 16, 19, 22, 25 Break]
${Break}
Switch…Case문은 If…ElseIf…Else문과 달리 특정 조건을 만족하여 해당 코드를 실행하면 그 이하 다른 조건의 코드도 모두 실행합니다. 따라서 ${Break}를 작성하여 Switch…Case문에서 탈출시켜야 합니다.
[출력 결과]
8행에서 score에 ‘B’를 넣었으므로 14행 ${Case} B 이하가 실행됩니다. 15행에 의해 MessageBox가 실행되고, 16행 ${Break}에 의해 Switch…Case문을 탈출합니다.
5.9. While
While은 반복문입니다.
!include LogicLib.nsh ;MUI.nsh include 시 LogicLib.nsh는 따로 include하지 않아도 됨.
Section "MainSection" SEC01
StrCpy $R0 "0"
${While} $R0 < 5
MessageBox MB_OK "R0 값은 $R0"
IntOp $R0 $R0 + 1
${EndWhile}
SetAutoClose true
SectionEnd
[Line 4]
StrCpy $R0 “0“
$R0에 0을 넣었습니다.
[Line 5]
${While} $R0 < 5
$R0 < 5 조건이 성립하는 동안 While문의 내용을 반복합니다.
[Line 6]
MessageBox MB_OK “R0 값은 $R0”
반복문의 진행을 눈으로 볼 수 있도록 While문의 내용을 실행할 때마다 MessageBox에 $R0 값을 띄웠습니다.
[Line 7]
IntOp $R0 $R0 + 1
While문의 내용을 실행할 때마다 $R0을 1씩 증가시킵니다.
[출력 결과]
$R0 값이 0부터 4까지 1씩 증가하며 While문이 총 5번 실행되었습니다.
5.10. For
For문은 IntOp문을 따로 작성하지 않아도 반복할 때마다 counter가 1씩 증가하는 반복문입니다.
!include LogicLib.nsh ;MUI.nsh include 시 LogicLib.nsh는 따로 include하지 않아도 됨.
Section "MainSection" SEC01
${For} $R0 0 4
MessageBox MB_OK "R0 값은 $R0"
${Next}
SetAutoClose true
SectionEnd
[Line 4]
${For} $R0 0 4
예제에서는 $R0을 counter로 사용할 것입니다. 0에서 시작하고 4에서 끝납니다.
[Line 5]
MessageBox MB_OK “R0 값은 $R0”
반복문의 진행을 눈으로 볼 수 있도록 For문의 내용을 실행할 때마다 MessageBox에 $R0 값을 띄웠습니다.
[Line 6]
For문을 닫을 때는 ${Next}라고 작성합니다.
[출력 결과]
출력 결과는 5.9절 While문과 같습니다.
5.11. Do…LoopUntil
Do…LoopUntil은 정해진 조건이 성립되면 탈출하는 반복문입니다.
!include LogicLib.nsh ;MUI.nsh include 시 LogicLib.nsh는 따로 include하지 않아도 됨.
Section "MainSection" SEC01
StrCpy $R0 "0"
${Do}
MessageBox MB_OK "R0 값은 $R0"
IntOp $R0 $R0 + 1
${LoopUntil} $R0 == 5
SetAutoClose true
SectionEnd
[Line 4]
StrCpy $R0 “0“
$R0에 0을 넣었습니다.
[Line 5~8]
${Do}와 ${LoopUntil} 사이의 내용(예제에서는 6~7행)을 반복합니다.
[Line 6]
MessageBox MB_OK “R0 값은 $R0”
반복문의 진행을 눈으로 볼 수 있도록 Do…LoopUntil문의 내용을 실행할 때마다 MessageBox에 $R0 값을 띄웠습니다.
[Line 7]
IntOp $R0 $R0 + 1
Do…LoopUntil문의 내용을 실행할 때마다 $R0을 1씩 증가시킵니다.
[Line 8]
${LoopUntil} $R0 == 5
$R0 == 5이면 반복문을 탈출합니다.
[출력 결과]
6~7행 실행(0 출력)→1!=5이므로 반복→6~7행 실행(1 출력)→2!=5이므로 반복→6~7행 실행(2 출력)→3!=5이므로 반복→6~7행 실행(3 출력)→4!=5이므로 반복→6~7행 실행(4 출력)→5==5이므로 탈출입니다.
5.12. 글 마무리
제 글을 읽어 주셔서 감사합니다. 다음에 만나요!
5.13. 참고 자료
1) sam 외 5명. 2018. “4.2 변수”, Opentutorials. (2024. 03. 16. 방문). https://opentutorials.org/module/3650/21919
2) sam 외 5명. 2018. “4.3 레이블”, Opentutorials. (2024. 01. 12. 방문). https://opentutorials.org/module/3650/21922
3) sam 외 5명. 2018. “4.4 상대 분기(Relative Jumps)”, Opentutorials. (2024. 03. 16. 방문). https://opentutorials.org/module/3650/21923
4) ospace. 2008. “[NSIS] LogicLib이용한 if, switch, while, for, loopuntil문 사용하기”, JaPa2. (2024. 03. 16. 방문). https://ospace.tistory.com/118