NSIS安装包制作

安装包制作是软件发版的关键步骤,制作安装包的工具很多,总体分为两类:GUI工具(Advanced Installer等)、脚本工具(NSIS、Inno Setup等)。

本人在实际工作中深度使用NSIS,感受到了它的强悍,本文结合实例介绍打包AutoCAD插件、Revit插件和独立软件的脚本编写注意事项。

AutoCAD插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
# ========================= 按需修改 ========================= 
# 安装包相关信息宏定义
!define PRODUCT_NAME "<软件名称>"
!define RELEASEPATH "<生成文件夹>"
!define PRODUCT_VERSION "<软件版本>"

!define MAINDLL2015 "<主DLL相对路径>"
!define MAINDLL2016 "<主DLL相对路径>"
!define MAINDLL2017 "<主DLL相对路径>"
!define MAINDLL2018 "<主DLL相对路径>"
!define MAINDLL2019 "<主DLL相对路径>"
!define MAINDLL2020 "<主DLL相对路径>"
!define MAINDLL2021 "<主DLL相对路径>"
!define MAINDLL2022 "<主DLL相对路径>"
!define MAINDLL2023 "<主DLL相对路径>"
!define MAINDLL2024 "<主DLL相对路径>"

!define OUTDIR "<安装包输出路径>"
!define PRODUCT_PUBLISHER "<软件出版单位>"
# ========================= 按需修改 =========================


!define EXE_NAME "${PRODUCT_NAME}.exe"
!define PRODUCT_LEGAL ""

# ========================= 自定义宏 =========================
# 卸载注册表宏路径定义
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"

# CAD注册表路径宏定义
!define regACAD2015 "SOFTWARE\Autodesk\AutoCAD\R20.0\ACAD-E001:804"
!define regACAD2016 "SOFTWARE\Autodesk\AutoCAD\R20.1\ACAD-F001:804"
!define regACAD2017 "SOFTWARE\Autodesk\AutoCAD\R21.0\ACAD-0001:804"
!define regACAD2018 "SOFTWARE\Autodesk\AutoCAD\R22.0\ACAD-1001:804"
!define regACAD2019 "SOFTWARE\Autodesk\AutoCAD\R23.0\ACAD-2001:804"
!define regACAD2020 "SOFTWARE\Autodesk\AutoCAD\R23.1\ACAD-3001:804"
!define regACAD2021 "SOFTWARE\Autodesk\AutoCAD\R24.0\ACAD-4101:804"
!define regACAD2022 "SOFTWARE\Autodesk\AutoCAD\R24.1\ACAD-5101:804"
!define regACAD2023 "SOFTWARE\Autodesk\AutoCAD\R24.2\ACAD-6101:804"
!define regACAD2024 "SOFTWARE\Autodesk\AutoCAD\R24.3\ACAD-6101:804"


# ========================= 安装包版本 =========================
VIProductVersion "${PRODUCT_VERSION}"
VIAddVersionKey "ProductVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "ProductName" "${PRODUCT_NAME}"
VIAddVersionKey "CompanyName" "${PRODUCT_PUBLISHER}"
VIAddVersionKey "FileVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "InternalName" "${EXE_NAME}"
VIAddVersionKey "FileDescription" "${PRODUCT_NAME}"
VIAddVersionKey "LegalCopyright" "${PRODUCT_LEGAL}"


# ========================= NSIS属性 =========================
!define /date NOW "%Y%m%d"
SetFont "Segoe UI" 9
#SetCompressor lzma
SetCompressor zlib
Name "${PRODUCT_NAME}V${PRODUCT_VERSION}"
OutFile "${OUTDIR}\${NOW}-${PRODUCT_NAME}-V${PRODUCT_VERSION}.exe"
InstallDir "$PROGRAMFILES64\${PRODUCT_PUBLISHER}\${PRODUCT_NAME}"
ShowInstDetails show
ShowUnInstDetails show
RequestExecutionLevel admin


# ========================= 外部插件及宏引用 =========================
!include nsDialogs.nsh
!include LogicLib.nsh
!include "x64.nsh"
!include "Library.nsh"
!include "WordFunc.nsh"
!define LIBRARY_X64
!include "MUI.nsh"


# ========================= 变量定义 =========================
var PREVIOUS

Var SELECTCADNUMBER

Var Dialog
Var CheckBox1
Var CheckBox2
Var CheckBox3
Var CheckBox4
Var CheckBox5
Var CheckBox6
Var CheckBox7
Var CheckBox8
Var CheckBox9
Var CheckBox10
Var CheckBoxState1
Var CheckBoxState2
Var CheckBoxState3
Var CheckBoxState4
Var CheckBoxState5
Var CheckBoxState6
Var CheckBoxState7
Var CheckBoxState8
Var CheckBoxState9
Var CheckBoxState10


# ========================= MUI定义 =========================
; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
;!define MUI_WELCOMEFINISHPAGE_BITMAP ".\background.bmp"

; Welcome page
!define MUI_WELCOMEPAGE_TITLE "欢迎使用《${PRODUCT_NAME}》安装向导"
!define MUI_WELCOMEPAGE_TEXT "此程序将引导你安装《${PRODUCT_NAME}V${PRODUCT_VERSION}》。\r\n\r\n在安装前建议关闭AutoCAD,从而确保安装程序能够更新相关系统文件,避免在安装后需要重启AutoCAD。\r\n\r\n点击[下一步(N)]继续。"
!insertmacro MUI_PAGE_WELCOME

; 自定义窗体,用户可以选择要安装插件的CAD
Page custom CreateCustomPage ValidateCustomPage

; Directory page
!define MUI_DIRECTORYPAGE_TEXT_TOP "安装程序将把《${PRODUCT_NAME}V${PRODUCT_VERSION}》安装到以下目录。要安装到另一个目录,请点击[浏览(B)...]并选择其他的文件夹。$\r$\n$\r$\n点击[安装] 开始安装。"
!insertmacro MUI_PAGE_DIRECTORY
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!insertmacro MUI_PAGE_FINISH
; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES
; Language files
!insertmacro MUI_LANGUAGE "SimpChinese"
; Reserve files
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS


# ========================= 安装步骤 =========================
# 静默卸载前次安装
Section "" SecUninstallPrevious
${If} $PREVIOUS == ""
Goto Done
${EndIf}

;卸载已安装程序(和安装一起更安全)
RMDir /r "$INSTDIR"
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"
setRegView 64
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
DeleteRegKey HKCU "${regACAD2015}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2016}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2017}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2018}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2019}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2020}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2021}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2022}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2023}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2024}\Applications\${PRODUCT_NAME}"
SetRegView Lastused

Done:
SectionEnd


# .NET Framework版本检测和安装
Section "" PRESEC
;先检测.net framework版本
Call GetNetFrameworkVersion
Pop $R1
${VersionCompare} $R1 '4.8' $1
${If} $1 == "2"
;安装net framework 4.8
SetDetailsPrint listonly
SetOutPath "$TEMP"
SetOverwrite on
File ".\NDP48-x86-x64-AllOS-ENU.exe"
ExecWait '$TEMP\NDP48-x86-x64-AllOS-ENU.exe /q /norestart /ChainingPackage FullX64Bootstrapper' $R1
Delete "$TEMP\NDP48-x86-x64-AllOS-ENU.exe"
${EndIf}
SectionEnd


# 安装程序
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
SetOverwrite ifnewer
File /r /x *.pdb "${RELEASEPATH}\*.*"

SetRegView 64
; 为CAD2015安装
${If} $CheckBoxState1 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2015}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2015}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2015}"
WriteRegDWORD HKCU "${regACAD2015}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2015}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

; 为CAD2016安装
${If} $CheckBoxState2 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2016}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2016}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2016}"
WriteRegDWORD HKCU "${regACAD2016}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2016}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

; 为CAD2017安装
${If} $CheckBoxState3 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2017}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2017}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2017}"
WriteRegDWORD HKCU "${regACAD2017}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2017}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

; 为CAD2018安装
${If} $CheckBoxState4 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2018}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2018}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2018}"
WriteRegDWORD HKCU "${regACAD2018}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2018}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

; 为CAD2019安装
${If} $CheckBoxState5 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2019}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2019}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2019}"
WriteRegDWORD HKCU "${regACAD2019}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2019}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

; 为CAD2020安装
${If} $CheckBoxState6 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2020}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2020}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2020}"
WriteRegDWORD HKCU "${regACAD2020}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2020}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

; 为CAD2021安装
${If} $CheckBoxState7 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2021}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2021}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2021}"
WriteRegDWORD HKCU "${regACAD2021}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2021}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

; 为CAD2022安装
${If} $CheckBoxState8 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2022}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2022}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2022}"
WriteRegDWORD HKCU "${regACAD2022}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2022}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

; 为CAD2023安装
${If} $CheckBoxState9 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2023}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2023}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2023}"
WriteRegDWORD HKCU "${regACAD2023}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2023}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

; 为CAD2024安装
${If} $CheckBoxState10 == ${BST_CHECKED}
WriteRegStr HKCU "${regACAD2024}\Applications\${PRODUCT_NAME}" "DESCRIPTION" "${PRODUCT_NAME}"
WriteRegStr HKCU "${regACAD2024}\Applications\${PRODUCT_NAME}" "LOADER" "$INSTDIR\${MAINDLL2024}"
WriteRegDWORD HKCU "${regACAD2024}\Applications\${PRODUCT_NAME}" "LOADCTRLS" 0x2
WriteRegDWORD HKCU "${regACAD2024}\Applications\${PRODUCT_NAME}" "MANAGED" 0x1
${EndIf}

SetRegView Lastused
SectionEnd

Section -AdditionalIcons
SetOutPath $INSTDIR
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\uninst.exe"
SectionEnd

# 写入卸载相关的注册表信息
Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"

setRegView 64
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" ""
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SetRegView Lastused

AccessControl::GrantOnFile "$INSTDIR" "(BU)" "FullAccess"
Pop $0
SectionEnd

# 卸载程序
Section Uninstall
RMDir /r "$INSTDIR"
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"

setRegView 64
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"

DeleteRegKey HKCU "${regACAD2015}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2016}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2017}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2018}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2019}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2020}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2021}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2022}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2023}\Applications\${PRODUCT_NAME}"
DeleteRegKey HKCU "${regACAD2024}\Applications\${PRODUCT_NAME}"

SetRegView Lastused

SetAutoClose true
SectionEnd

# ========================= 回调函数 =========================
; 安装初始化事件处理
Function .onInit
System::Call 'kernel32::CreateMutex(p 0, i 0, t "myMutex") p .r1 ?e'
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "安装程序已运行。"
Abort

;检测是否已安装
setRegView 64
ReadRegStr $PREVIOUS ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
SetRegView Lastused

${If} $PREVIOUS == ""
Goto Done
${EndIf}

MessageBox MB_YESNO "检测已安装《${PRODUCT_NAME}》,是否覆盖安装?" IDYES true IDNO false
true:
Goto Done
false:
Abort

Done:
FunctionEnd

; 卸载初始化事件处理
Function un.onInit
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "是否确定彻底卸载 $(^Name)?" IDYES +2
Abort
FunctionEnd

; 卸载完成事件处理
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name)已从您的电脑上彻底卸载。"
FunctionEnd


# ========================= 用户函数 =========================
;.NET Framework版本检测
Function GetNetFrameworkVersion
Push $1
Push $0
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "InstallSuccess"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "2.0.50727.832"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "1.1.4322.573"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\.NETFramework\policy\v1.0" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\.NETFramework\policy\v1.0" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "1.0.3705.0"
StrCmp $0 1 KnowNetFrameworkVersion +1
StrCpy $1 "not .NetFramework"
KnowNetFrameworkVersion:
Pop $0
Exch $1
FunctionEnd


# ========================= 自定义窗体(选择AutoCAD的窗体) =========================
Function CreateCustomPage
nsDialogs::Create 1018
Pop $Dialog

${If} $Dialog == error
Abort
${EndIf}

; 标题
${NSD_CreateLabel} 0 0 100% 24u "请选择要安装此插件的AutoCAD版本:"
Pop $0
SetCtlColors $0 0x000000 transparent

; 第一列 (左侧)
${NSD_CreateCheckbox} 0 40 32% 24 "AutoCAD 2015"
Pop $CheckBox1
${NSD_CreateCheckbox} 0 80 32% 24 "AutoCAD 2016"
Pop $CheckBox2
${NSD_CreateCheckbox} 0 120 32% 24 "AutoCAD 2017"
Pop $CheckBox3
${NSD_CreateCheckbox} 0 160 32% 24 "AutoCAD 2024"
Pop $CheckBox10

; 第二列 (中间)
${NSD_CreateCheckbox} 33% 40 32% 24 "AutoCAD 2018"
Pop $CheckBox4
${NSD_CreateCheckbox} 33% 80 32% 24 "AutoCAD 2019"
Pop $CheckBox5
${NSD_CreateCheckbox} 33% 120 32% 24 "AutoCAD 2020"
Pop $CheckBox6

; 第三列 (右侧)
${NSD_CreateCheckbox} 66% 40 32% 24 "AutoCAD 2021"
Pop $CheckBox7
${NSD_CreateCheckbox} 66% 80 32% 24 "AutoCAD 2022"
Pop $CheckBox8
${NSD_CreateCheckbox} 66% 120 32% 24 "AutoCAD 2023"
Pop $CheckBox9

; 批量设置各复选框的状态
Call ComputeDefaultSelection

; 添加全选/全不选按钮
${NSD_CreateButton} 0 200 100 24 "全选"
Pop $0
${NSD_OnClick} $0 SelectAll

${NSD_CreateButton} 120 200 100 24 "全不选"
Pop $0
${NSD_OnClick} $0 DeselectAll

nsDialogs::Show
FunctionEnd


Function SelectAll
${NSD_Uncheck} $CheckBox1
${NSD_Uncheck} $CheckBox2
${NSD_Uncheck} $CheckBox3
${NSD_Uncheck} $CheckBox4
${NSD_Uncheck} $CheckBox5
${NSD_Uncheck} $CheckBox6
${NSD_Uncheck} $CheckBox7
${NSD_Uncheck} $CheckBox8
${NSD_Uncheck} $CheckBox9
${NSD_Uncheck} $CheckBox10

SetRegView 64
; CAD2015
!ifdef MAINDLL2015
ReadRegStr $1 HKLM "${regACAD2015}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox1
${EndIf}
!endif

; CAD2016
!ifdef MAINDLL2016
ReadRegStr $1 HKLM "${regACAD2016}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox2
${EndIf}
!endif

; CAD2017
!ifdef MAINDLL2017
ReadRegStr $1 HKLM "${regACAD2017}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox3
${EndIf}
!endif

; CAD2018
!ifdef MAINDLL2018
ReadRegStr $1 HKLM "${regACAD2018}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox4
${EndIf}
!endif

; CAD2019
!ifdef MAINDLL2019
ReadRegStr $1 HKLM "${regACAD2019}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox5
${EndIf}
!endif

; CAD2020
!ifdef MAINDLL2020
ReadRegStr $1 HKLM "${regACAD2020}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox6
${EndIf}
!endif

; CAD2021
!ifdef MAINDLL2021
ReadRegStr $1 HKLM "${regACAD2021}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox7
${EndIf}
!endif

; CAD2022
!ifdef MAINDLL2022
ReadRegStr $1 HKLM "${regACAD2022}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox8
${EndIf}
!endif

; CAD2023
!ifdef MAINDLL2023
ReadRegStr $1 HKLM "${regACAD2023}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox9
${EndIf}
!endif

; CAD2024
!ifdef MAINDLL2023
ReadRegStr $1 HKLM "${regACAD2024}" "AcadLocation"
${If} $1 != ""
${NSD_Check} $CheckBox10
${EndIf}
!endif

SetRegView Lastused
FunctionEnd


Function DeselectAll
${NSD_Uncheck} $CheckBox1
${NSD_Uncheck} $CheckBox2
${NSD_Uncheck} $CheckBox3
${NSD_Uncheck} $CheckBox4
${NSD_Uncheck} $CheckBox5
${NSD_Uncheck} $CheckBox6
${NSD_Uncheck} $CheckBox7
${NSD_Uncheck} $CheckBox8
${NSD_Uncheck} $CheckBox9
${NSD_Uncheck} $CheckBox10
FunctionEnd


Function ValidateCustomPage
${NSD_GetState} $CheckBox1 $CheckBoxState1
${NSD_GetState} $CheckBox2 $CheckBoxState2
${NSD_GetState} $CheckBox3 $CheckBoxState3
${NSD_GetState} $CheckBox4 $CheckBoxState4
${NSD_GetState} $CheckBox5 $CheckBoxState5
${NSD_GetState} $CheckBox6 $CheckBoxState6
${NSD_GetState} $CheckBox7 $CheckBoxState7
${NSD_GetState} $CheckBox8 $CheckBoxState8
${NSD_GetState} $CheckBox9 $CheckBoxState9
${NSD_GetState} $CheckBox10 $CheckBoxState10

StrCpy $SELECTCADNUMBER 0
${If} $CheckBoxState1 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
${If} $CheckBoxState2 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
${If} $CheckBoxState3 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
${If} $CheckBoxState4 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
${If} $CheckBoxState5 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
${If} $CheckBoxState6 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
${If} $CheckBoxState7 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
${If} $CheckBoxState8 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
${If} $CheckBoxState9 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
${If} $CheckBoxState10 == ${BST_CHECKED}
IntOp $SELECTCADNUMBER $SELECTCADNUMBER + 1
${EndIf}
FunctionEnd


Function ComputeDefaultSelection
Call DeselectAll
${NSD_AddStyle} $CheckBox1 ${WS_DISABLED}
${NSD_AddStyle} $CheckBox2 ${WS_DISABLED}
${NSD_AddStyle} $CheckBox3 ${WS_DISABLED}
${NSD_AddStyle} $CheckBox4 ${WS_DISABLED}
${NSD_AddStyle} $CheckBox5 ${WS_DISABLED}
${NSD_AddStyle} $CheckBox6 ${WS_DISABLED}
${NSD_AddStyle} $CheckBox7 ${WS_DISABLED}
${NSD_AddStyle} $CheckBox8 ${WS_DISABLED}
${NSD_AddStyle} $CheckBox9 ${WS_DISABLED}
${NSD_AddStyle} $CheckBox10 ${WS_DISABLED}

SetRegView 64
; CAD2015
!ifdef MAINDLL2015
ReadRegStr $1 HKLM "${regACAD2015}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox1 ${WS_DISABLED}
${NSD_Check} $CheckBox1
${EndIf}
!endif

; CAD2016
!ifdef MAINDLL2016
ReadRegStr $1 HKLM "${regACAD2016}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox2 ${WS_DISABLED}
${NSD_Check} $CheckBox2
${EndIf}
!endif

; CAD2017
!ifdef MAINDLL2017
ReadRegStr $1 HKLM "${regACAD2017}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox3 ${WS_DISABLED}
${NSD_Check} $CheckBox3
${EndIf}
!endif

; CAD2018
!ifdef MAINDLL2018
ReadRegStr $1 HKLM "${regACAD2018}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox4 ${WS_DISABLED}
${NSD_Check} $CheckBox4
${EndIf}
!endif

; CAD2019
!ifdef MAINDLL2019
ReadRegStr $1 HKLM "${regACAD2019}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox5 ${WS_DISABLED}
${NSD_Check} $CheckBox5
${EndIf}
!endif

; CAD2020
!ifdef MAINDLL2020
ReadRegStr $1 HKLM "${regACAD2020}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox6 ${WS_DISABLED}
${NSD_Check} $CheckBox6
${EndIf}
!endif

; CAD2021
!ifdef MAINDLL2021
ReadRegStr $1 HKLM "${regACAD2021}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox7 ${WS_DISABLED}
${NSD_Check} $CheckBox7
${EndIf}
!endif

; CAD2022
!ifdef MAINDLL2022
ReadRegStr $1 HKLM "${regACAD2022}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox8 ${WS_DISABLED}
${NSD_Check} $CheckBox8
${EndIf}
!endif

; CAD2023
!ifdef MAINDLL2023
ReadRegStr $1 HKLM "${regACAD2023}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox9 ${WS_DISABLED}
${NSD_Check} $CheckBox9
${EndIf}
!endif

; CAD2024
!ifdef MAINDLL2024
ReadRegStr $1 HKLM "${regACAD2024}" "AcadLocation"
${If} $1 != ""
${NSD_RemoveStyle} $CheckBox10 ${WS_DISABLED}
${NSD_Check} $CheckBox10
${EndIf}
!endif

SetRegView Lastused
FunctionEnd

以上脚本可以实现AutoCAD插件系统的一键打包,主要功能如下:

  • 常规安装步骤:复制文件、添加开始菜单项和桌面快捷方式、写删除信息、卸载
  • 设置压缩方式:平衡速度和安装包体积
  • 设置安装界面语言和字体(默认字体不美观)
  • 检测软件是否已安装
  • 操作(读、写、删)注册表
  • 检测指定版本的.NET Framework是否安装
  • 禁止启动多个安装进程:使用Windows API创建内核对象,实现进程之间的互斥
  • 设置安装目录的用户权限(使用NSIS自带的权限设置工具)
  • 添加界面供用户选择为哪些AutoCAD安装插件(使用NSIS提供的GUI开发功能,非常有利于提升用户安装体验)

Revit插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# ========================= 按需修改 ========================= 
# 安装包相关信息宏定义
!define PRODUCT_NAME "<软件名称>"
!define RELEASEPATH "<生成文件夹>"
!define PRODUCT_VERSION "<软件版本>"

!define MAINDLL2018 "<主DLL相对路径>"
!define MAINDLL2020 "<主DLL相对路径>"

!define PRODUCT_GUID "<软件GUID>"
!define START_CLASS2018 "<启动类字符串>"
!define START_CLASS2020 "<启动类字符串>"

!define OUTDIR "<安装包输出路径>"
!define PRODUCT_PUBLISHER "<软件出版单位>"
# ========================= 按需修改 =========================

!define PRODUCT_LEGAL ""
!define EXE_NAME "${PRODUCT_NAME}.exe"
!define ADDINPATH2020 "C:\ProgramData\Autodesk\Revit\Addins\2020"
!define ADDINPATH2018 "C:\ProgramData\Autodesk\Revit\Addins\2018"

# ========================= 自定义宏 =========================
# 卸载注册表宏路径定义
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"


# ========================= 安装包版本 =========================
VIProductVersion "${PRODUCT_VERSION}"
VIAddVersionKey "ProductVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "ProductName" "${PRODUCT_NAME}"
VIAddVersionKey "CompanyName" "${PRODUCT_PUBLISHER}"
VIAddVersionKey "FileVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "InternalName" "${EXE_NAME}"
VIAddVersionKey "FileDescription" "${PRODUCT_NAME}"
VIAddVersionKey "LegalCopyright" "${PRODUCT_LEGAL}"


# ========================= NSIS属性 =========================
!define /date NOW "%Y%m%d"
SetFont "Segoe UI" 9
#SetCompressor lzma
SetCompressor zlib
Name "${PRODUCT_NAME}V${PRODUCT_VERSION}"
OutFile "${OUTDIR}\${NOW}-${PRODUCT_NAME}-V${PRODUCT_VERSION}.exe"
InstallDir "$PROGRAMFILES64\${PRODUCT_PUBLISHER}\${PRODUCT_NAME}"
ShowInstDetails show
ShowUnInstDetails show
RequestExecutionLevel admin

# ========================= 外部插件及宏引用 =========================
!include LogicLib.nsh
!include "x64.nsh"
!include "Library.nsh"
!include "WordFunc.nsh"
!define LIBRARY_X64
!include "MUI.nsh"


# ========================= 变量定义 =========================
var PREVIOUS


# ========================= MUI定义 =========================
; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
;!define MUI_WELCOMEFINISHPAGE_BITMAP ".\background.bmp"
; Welcome page
!define MUI_WELCOMEPAGE_TITLE "欢迎使用《${PRODUCT_NAME}》安装向导"
!define MUI_WELCOMEPAGE_TEXT "此程序将引导你安装《${PRODUCT_NAME}V${PRODUCT_VERSION}》。\r\n\r\n在安装前建议关闭Revit,从而确保安装程序能够更新相关系统文件,避免在安装后需要重启Revit。\r\n\r\n点击[下一步(N)]继续。"
!insertmacro MUI_PAGE_WELCOME
; Directory page
!define MUI_DIRECTORYPAGE_TEXT_TOP "安装程序将把《${PRODUCT_NAME}V${PRODUCT_VERSION}》安装到以下目录。要安装到另一个目录,请点击[浏览(B)...]并选择其他的文件夹。$\r$\n$\r$\n点击[安装] 开始安装。"
!insertmacro MUI_PAGE_DIRECTORY
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!insertmacro MUI_PAGE_FINISH
; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES
; Language files
!insertmacro MUI_LANGUAGE "SimpChinese"
; Reserve files
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS


# ========================= 安装步骤 =========================
# 静默卸载前次安装
Section "" SecUninstallPrevious
${If} $PREVIOUS == ""
Goto Done
${EndIf}

;卸载已安装程序(和安装一起更安全)
RMDir /r "$INSTDIR"
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"
Delete "${ADDINPATH2020}\${PRODUCT_GUID}.addin"
Delete "${ADDINPATH2018}\${PRODUCT_GUID}.addin"

Done:
SectionEnd


# .NET Framework版本检测和安装
Section "" PRESEC
;先检测.net framework版本
Call GetNetFrameworkVersion
Pop $R1
${VersionCompare} $R1 '4.8' $1
${If} $1 == "2"
;安装net framework 4.8
SetDetailsPrint listonly
SetOutPath "$TEMP"
SetOverwrite on
File ".\NDP48-x86-x64-AllOS-ENU.exe"
ExecWait '$TEMP\NDP48-x86-x64-AllOS-ENU.exe /q /norestart /ChainingPackage FullX64Bootstrapper' $R1
Delete "$TEMP\NDP48-x86-x64-AllOS-ENU.exe"
${EndIf}
SectionEnd


# 安装程序
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
SetOverwrite ifnewer
File /r /x *.pdb "${RELEASEPATH}\*.*"

!ifdef MAINDLL2018
SetOutPath "${ADDINPATH2018}"
File "RevitAddinDeploy.addin"
Rename "${ADDINPATH2018}\RevitAddinDeploy.addin" "${ADDINPATH2018}\${PRODUCT_GUID}.addin"

nsisXML::create
nsisXML::load ${ADDINPATH2018}\${PRODUCT_GUID}.addin

nsisXML::select '/RevitAddIns/AddIn/Name'
nsisXML::setText "${PRODUCT_NAME}"
nsisXML::select '/RevitAddIns/AddIn/Assembly'
nsisXML::setText "$INSTDIR\${MAINDLL2018}"
nsisXML::select '/RevitAddIns/AddIn/ClientId'
nsisXML::setText "${PRODUCT_GUID}"
nsisXML::select '/RevitAddIns/AddIn/FullClassName'
nsisXML::setText "${START_CLASS2018}"

nsisXML::save "${ADDINPATH2018}\${PRODUCT_GUID}.addin"
nsisXML::release $0
!endif

!ifdef MAINDLL2020
SetOutPath "${ADDINPATH2020}"
File "RevitAddinDeploy.addin"
Rename "${ADDINPATH2020}\RevitAddinDeploy.addin" "${ADDINPATH2020}\${PRODUCT_GUID}.addin"

nsisXML::create
nsisXML::load ${ADDINPATH2020}\${PRODUCT_GUID}.addin

nsisXML::select '/RevitAddIns/AddIn/Name'
nsisXML::setText "${PRODUCT_NAME}"
nsisXML::select '/RevitAddIns/AddIn/Assembly'
nsisXML::setText "$INSTDIR\${MAINDLL2020}"
nsisXML::select '/RevitAddIns/AddIn/ClientId'
nsisXML::setText "${PRODUCT_GUID}"
nsisXML::select '/RevitAddIns/AddIn/FullClassName'
nsisXML::setText "${START_CLASS2020}"

nsisXML::save "${ADDINPATH2020}\${PRODUCT_GUID}.addin"
nsisXML::release $0
!endif
SectionEnd

Section -AdditionalIcons
SetOutPath $INSTDIR
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\uninst.exe"
SectionEnd

# 写入卸载相关的注册表信息
Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"

setRegView 64
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" ""
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SetRegView Lastused

AccessControl::GrantOnFile "$INSTDIR" "(BU)" "FullAccess"
SectionEnd


# 卸载程序
Section Uninstall
RMDir /r "$INSTDIR"
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"

!ifdef MAINDLL2020
Delete "${ADDINPATH2020}\${PRODUCT_GUID}.addin"
!endif

!ifdef MAINDLL2018
Delete "${ADDINPATH2018}\${PRODUCT_GUID}.addin"
!endif

setRegView 64
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
SetRegView Lastused

SetAutoClose true
SectionEnd

# ========================= 回调函数 =========================
; 安装初始化事件处理
Function .onInit
System::Call 'kernel32::CreateMutex(p 0, i 0, t "myMutex") p .r1 ?e'
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "安装程序已运行。"
Abort

;检测是否已安装
setRegView 64
ReadRegStr $PREVIOUS ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
SetRegView Lastused

${If} $PREVIOUS == ""
Goto Done
${EndIf}

MessageBox MB_YESNO "检测已安装《${PRODUCT_NAME}》,是否覆盖安装?" IDYES true IDNO false
true:
Goto Done
false:
Abort

Done:
FunctionEnd

; 卸载初始化事件处理
Function un.onInit
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "是否确定彻底卸载 $(^Name)?" IDYES +2
Abort
FunctionEnd

; 卸载完成事件处理
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name)已从您的电脑上彻底卸载。"
FunctionEnd


# ========================= 用户函数 =========================
;.NET Framework版本检测
Function GetNetFrameworkVersion
Push $1
Push $0
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "InstallSuccess"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "2.0.50727.832"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "1.1.4322.573"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\.NETFramework\policy\v1.0" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\.NETFramework\policy\v1.0" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "1.0.3705.0"
StrCmp $0 1 KnowNetFrameworkVersion +1
StrCpy $1 "not .NetFramework"
KnowNetFrameworkVersion:
Pop $0
Exch $1
FunctionEnd

相比于AutoCAD的插件打包,Revit插件打包的核心是动态修改addin配置文件,使用nsisxml插件进行操作,非常好用!

独立软件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# ========================= 按需修改 ========================= 
!define PRODUCT_NAME "<软件名称>"
!define RELEASEPATH "<生成文件夹>"
!define PRODUCT_VERSION "<软件版本>"
!define MAINEXE "<主EXE路径>"

!define OUTDIR "<安装包输出路径>"
!define PRODUCT_PUBLISHER "<软件出版单位>"
# ========================= 按需修改 =========================

!define EXE_NAME "${PRODUCT_NAME}.exe"
!define PRODUCT_LEGAL ""

# ========================= 自定义宏 =========================
# 卸载注册表宏路径定义
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"


# ========================= 安装包版本 =========================
VIProductVersion "${PRODUCT_VERSION}"
VIAddVersionKey "ProductVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "ProductName" "${PRODUCT_NAME}"
VIAddVersionKey "CompanyName" "${PRODUCT_PUBLISHER}"
VIAddVersionKey "FileVersion" "${PRODUCT_VERSION}"
VIAddVersionKey "InternalName" "${EXE_NAME}"
VIAddVersionKey "FileDescription" "${PRODUCT_NAME}"
VIAddVersionKey "LegalCopyright" "${PRODUCT_LEGAL}"


# ========================= NSIS属性 =========================
!define /date NOW "%Y%m%d"
SetFont "Segoe UI" 9
#SetCompressor lzma
SetCompressor zlib
Name "${PRODUCT_NAME}V${PRODUCT_VERSION}"
OutFile "${OUTDIR}\${NOW}-${PRODUCT_NAME}-V${PRODUCT_VERSION}.exe"
InstallDir "$PROGRAMFILES64\${PRODUCT_PUBLISHER}\${PRODUCT_NAME}"
ShowInstDetails show
ShowUnInstDetails show
RequestExecutionLevel admin


# ========================= 外部插件及宏引用 =========================
!include LogicLib.nsh
!include "x64.nsh"
!include "Library.nsh"
!include "WordFunc.nsh"
!define LIBRARY_X64
!include "MUI.nsh"


# ========================= 变量定义 =========================
var PREVIOUS


# ========================= MUI定义 =========================
; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
;!define MUI_WELCOMEFINISHPAGE_BITMAP ".\background.bmp"
; Welcome page
!define MUI_WELCOMEPAGE_TITLE "欢迎使用《${PRODUCT_NAME}》安装向导"
!define MUI_WELCOMEPAGE_TEXT "此程序将引导你安装《${PRODUCT_NAME}V${PRODUCT_VERSION}》。\r\n\r\n点击[下一步(N)]继续。"
!insertmacro MUI_PAGE_WELCOME
; Directory page
!define MUI_DIRECTORYPAGE_TEXT_TOP "安装程序将把《${PRODUCT_NAME}V${PRODUCT_VERSION}》安装到以下目录。要安装到另一个目录,请点击[浏览(B)...]并选择其他的文件夹。$\r$\n$\r$\n点击[安装] 开始安装。"
!insertmacro MUI_PAGE_DIRECTORY
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!insertmacro MUI_PAGE_FINISH
; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES
; Language files
!insertmacro MUI_LANGUAGE "SimpChinese"
; Reserve files
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS


# ========================= 安装步骤 =========================
# 静默卸载前次安装
Section "" SecUninstallPrevious
${If} $PREVIOUS == ""
Goto Done
${EndIf}

RMDir /r "$INSTDIR"
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"

Done:
SectionEnd

# 安装程序
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
SetOverwrite ifnewer
File /r /x *.pdb "${RELEASEPATH}\*.*"
SectionEnd

Section -AdditionalIcons
; 创建开始菜单项
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall.lnk" "$INSTDIR\uninst.exe"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${MAINEXE}"
; 创建桌面快捷项
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${MAINEXE}"
SectionEnd

# 写入卸载相关的注册表信息
Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"

setRegView 64
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" ""
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SetRegView Lastused

AccessControl::GrantOnFile "$INSTDIR" "(BU)" "FullAccess"
SectionEnd


# 卸载程序
Section Uninstall
RMDir /r "$INSTDIR"
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"

setRegView 64
DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
SetRegView Lastused

SetAutoClose true
SectionEnd

# ========================= 回调函数 =========================
; 安装初始化事件处理
Function .onInit
System::Call 'kernel32::CreateMutex(p 0, i 0, t "myMutex") p .r1 ?e'
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "安装程序已运行。"
Abort

;检测是否已安装
setRegView 64
ReadRegStr $PREVIOUS ${PRODUCT_UNINST_ROOT_KEY} ${PRODUCT_UNINST_KEY} "UninstallString"
SetRegView Lastused

${If} $PREVIOUS == ""
Goto Done
${EndIf}

MessageBox MB_YESNO "检测已安装《${PRODUCT_NAME}》,是否覆盖安装?" IDYES true IDNO false
true:
Goto Done
false:
Abort

Done:
FunctionEnd

; 卸载初始化事件处理
Function un.onInit
MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "是否确定彻底卸载 $(^Name)?" IDYES +2
Abort
FunctionEnd

; 卸载完成事件处理
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name)已从您的电脑上彻底卸载。"
FunctionEnd

对于独立软件的打包就非常简单了,全部是常规操作。

延伸

可以基于NSIS进一步封装,实现NSIS打包脚本对开发人员的隐藏(界面输入关键信息,后台自动调用NSIS实现打包),这种方式更易用,且更容易实现部门软件安装包风格的统一。本人已实现该功能,界面如下:

(转载本站文章请注明作者和出处lihaohello.top,请勿用于任何商业用途)

评论