;iss #define MyAppName "基础环境" #define MyAppVersion "1.0.2" #define MyAppPublisher "X" #define MyAppURL "~" #define MyAppExeName "command" #define MyAppExeNameHump "Command" #define MyAppExeNameUpper "COMMAND" #define MyAppAssocName MyAppName + " File" #define MyAppAssocExt ".myp" #define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt #define MyAppFirstPath "XManage" #define MyAppResources "Resources" #define InstallPassword GetDateTimeString('yyyymmdd', '', '') ; 默认配置 [Setup] ; 不得复用,每个应用必须有唯一AppId AppId={{517C2278-C7C9-43D0-8E94-BFD2039C1690} ; 默认安装路径 DefaultDirName={localappdata}\{#MyAppFirstPath} ; 是否加密 ;Encryption=yes ; 安装密码 ;Password={#InstallPassword} ; ICO图标 ; SetupIconFile=Resources\startup.ico UninstallFilesDir={app} UninstallDisplayIcon={app}\{#MyAppExeName}.exe VersionInfoCompany= VersionInfoCopyright=CopyRight © 2025 VersionInfoDescription=装配DB、IO、MQ、Command等 VersionInfoProductVersion={#MyAppVersion} VersionInfoProductName=基础环境(支持Windows7及以上) ; 默认配置 AppName={#MyAppName} AppVersion={#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} ChangesAssociations=yes DisableDirPage=no DisableProgramGroupPage=yes Compression=lzma2 SolidCompression=yes WizardStyle=modern ChangesEnvironment=yes PrivilegesRequired=admin ArchitecturesAllowed=x64compatible ArchitecturesInstallIn64BitMode=x64compatible OutputDir=Release\ OutputBaseFilename=Setup-{#MyAppVersion} ; 语言选择 [Languages] Name: "Chinese"; MessagesFile: "compiler:Languages\Chinese.isl"; ;; 任务 [Tasks] ; 是否创建桌面图标 ; checkablealone 默认选中 ; unchecked 默认不选中 ;Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked ;; 程序 ICO [Icons] ;Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}.exe" ;Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}.exe"; Tasks: desktopicon ;; 组件安装方式 [Types] Name: "full"; Description: "完整组件安装"; Name: "mini"; Description: "最小安装"; ;; ============================================== 自定义组件 必须启用 iscustom 作为 Flags ============================================== ; iscustom 开启自定义选项 Name: "custom"; Description: "自定义组件安装"; Flags: iscustom; ;; 组件列表选择项 [Components] ;; ============================================== 自定义组件的 Types 可以不参与任何固定式 ============================================== Name: "MainApp"; Description: "{#MyAppName}主程序"; Types: full mini; Flags: checkablealone; Name: "JDK"; Description: "JDK"; Types: full; Flags: checkablealone; Name: "Redis"; Description: "Redis"; Types: full; Flags: checkablealone; Name: "MinIO"; Description: "MinIO"; Types: full; Flags: checkablealone; Name: "RocketMQ"; Description: "RocketMQ"; Types: full; Flags: checkablealone; Name: "Nginx"; Description: "Nginx"; Types: full; Flags: checkablealone; Name: "MySQL"; Description: "MySQL"; Types: full; Flags: checkablealone; ;; 引入文件列表 [Files] ;; ============================================== 共享文件 禁止使用 ignoreversion 作为 Flags ============================================== ;; Readme ; 安装完的 readme 信息 ;Source: "{#MyAppResources}\Readme.md"; DestDir: "{app}"; Flags: isreadme; ;; 文件 Source: "{#MyAppResources}\grep.exe"; DestDir: "{app}\usr"; DestName: "grep.exe"; Flags: ignoreversion; Components: MainApp; ;; 文件夹 ; recursesubdirs createallsubdirs 递归复制整个目录 Source: "{#MyAppResources}\usr\*"; DestDir: "{app}\usr\"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: MainApp; ; onlyifdoesntexist 当文件不存在时安装 Source: "{#MyAppResources}\jdk\*"; DestDir: "{app}\jdk\"; Flags: ignoreversion recursesubdirs createallsubdirs onlyifdoesntexist; Components: JDK; Source: "{#MyAppResources}\redis\*"; DestDir: "{app}\redis\"; Flags: onlyifdoesntexist recursesubdirs createallsubdirs; Components: Redis; Source: "{#MyAppResources}\io\*"; DestDir: "{app}\io\"; Flags: onlyifdoesntexist recursesubdirs createallsubdirs; Components: MinIO; Source: "{#MyAppResources}\rocketmq\*"; DestDir: "{app}\rocketmq\"; Flags: onlyifdoesntexist recursesubdirs createallsubdirs; Components: RocketMQ; Source: "{#MyAppResources}\nginx\*"; DestDir: "{app}\nginx\"; Flags: onlyifdoesntexist recursesubdirs createallsubdirs; Components: Nginx; Source: "{#MyAppResources}\mysql\*"; DestDir: "{app}\mysql\"; Flags: onlyifdoesntexist recursesubdirs createallsubdirs; Components: MySQL; ;; 创建必要目录 [Dirs] Name: "{app}\nginx\logs"; Permissions: users-modify; Components: Nginx; Name: "{app}\nginx\dist"; Permissions: users-modify; Components: Nginx; ;; 安装前后删除文件 [InstallDelete] ;Type: files; Name: "{app}\mysql\template.ini"; ;Type: files; Name: "{app}\mysql\template-initialize-and-start-mysql.bat"; ;Type: files; Name: "{app}\mysql\template-register-mysql-with-data.bat"; ;; 安装时注册表与环境变量列表 [Registry] ; uninsdeletevalue 卸载时删除值 ; uninsdeletekey 卸载时删除键 ; preservestringtype 保留原值类型 ;; ============================================== Path 禁止使用任何 *delete*、*remove* 作为 Flags 运行 ============================================== ;; 写入普通注册表项 Root: HKLM; Subkey: "Software\X\Command"; ValueType: string; ValueName: "InstallPath"; ValueData: "{app}"; Flags: uninsdeletevalue ; Root: HKLM; Subkey: "Software\X\Command"; ValueType: string; ValueName: "Version"; ValueData: "{#SetupSetting("AppVersion")}"; Flags: uninsdeletevalue ; ;; 默认注册信息 Root: HKA; Subkey: "Software\Classes\{#MyAppAssocExt}\OpenWithProgids"; ValueType: string; ValueName: "{#MyAppAssocKey}"; ValueData: ""; Flags: uninsdeletevalue; Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppAssocName}"; Flags: uninsdeletevalue; Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName}.exe,0"; Flags: uninsdeletevalue; Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}.exe"" ""%1"""; Flags: uninsdeletevalue; ;; 安装路径 Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; Components: MainApp; Flags: uninsdeletevalue; \ ValueName: "X_COMMAND"; ValueData: "{app}"; ;; 【Java】追加到 PATH 变量 Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; Components: JDK; \ ValueName: "JAVA_HOME"; ValueData: "{app}\jdk"; Flags: uninsdeletevalue ; Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; Components: JDK; \ ValueName: "JDK_21"; ValueData: "{app}\jdk"; Flags: uninsdeletevalue ; Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; Components: JDK; Flags: preservestringtype; AfterInstall: RefreshEnvironment; \ ValueName: "Path"; ValueData: "{olddata};%JAVA_HOME%\bin"; Check: NeedsAddPath('%JAVA_HOME%\bin'); ;; 【grep、awk、sed、head、sort、ls、rm、xargs、rar、zip、tar、md5sum、sha1sum...】 Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; Components: MainApp; Flags: preservestringtype; AfterInstall: RefreshEnvironment; \ ValueName: "Path"; ValueData: "{olddata};%X_COMMAND%\usr"; Check: NeedsAddPath('%X_COMMAND%\usr'); ;; 【Redis】追加到 PATH 变量 Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; Components: Redis; Flags: preservestringtype; AfterInstall: RefreshEnvironment; \ ValueName: "Path"; ValueData: "{olddata};%X_COMMAND%\redis"; Check: NeedsAddPath('%X_COMMAND%\redis'); ;; 【MinIO】追加到 PATH 变量 Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; Components: MinIO; Flags: preservestringtype; AfterInstall: RefreshEnvironment; \ ValueName: "Path"; ValueData: "{olddata};%X_COMMAND%\io"; Check: NeedsAddPath('%X_COMMAND%\io'); ;; 【Nginx】追加到 PATH 变量 Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; Components: Nginx; Flags: preservestringtype; AfterInstall: RefreshEnvironment; \ ValueName: "Path"; ValueData: "{olddata};%X_COMMAND%\nginx"; Check: NeedsAddPath('%X_COMMAND%\nginx'); ;; 【MySQL】环境变量【注册服务使用】 ; 数据库服务注册后服务名 ;Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; Components: MySQL; Flags: uninsdeletevalue; AfterInstall: RefreshEnvironment; \ ValueName: "X_MANAGE_DB_SC"; ValueData: "x_database"; ; 这两个值,将作为后续判断程序是否已经安装做依据 ;Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; Components: MySQL; Flags: uninsdeletevalue; AfterInstall: RefreshEnvironment; \ ValueName: "X_MANAGE_DB_EXE"; ValueData: "{app}\mysql\bin\mysqld.exe"; ;Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; Components: MySQL; Flags: uninsdeletevalue ; AfterInstall: RefreshEnvironment; \ ValueName: "X_MANAGE_DB_COF"; ValueData: "{app}\mysql\my.ini"; ; MySQL【通用服务】,导入脚本数据使用 Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; Components: MySQL; Flags: uninsdeletevalue; \ ValueName: "X_COMMAND_MYSQL"; ValueData: "{app}\mysql\"; ; 追加到 PATH 变量 Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; Components: MySQL; Flags: preservestringtype; AfterInstall: RefreshEnvironment; \ ValueName: "Path"; ValueData: "{olddata};%X_COMMAND_MYSQL%\bin"; Check: NeedsAddPath('%X_COMMAND_MYSQL%\bin'); ;; 【RocketMQ】追加到 PATH 变量 Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; Components: RocketMQ; Flags: uninsdeletevalue; \ ValueName: "ROCKETMQ_HOME"; ValueData: "{app}\rocketmq"; Check: CheckRegistryPath('{app}\rocketmq'); Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: string; Components: RocketMQ; Flags: uninsdeletevalue; \ ValueName: "ROCKETMQ_SBIN"; ValueData: "{app}\rocketmq\sbin"; Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; Components: RocketMQ; Flags: preservestringtype; AfterInstall: RefreshEnvironment; \ ValueName: "Path"; ValueData: "{olddata};%ROCKETMQ_SBIN%"; Check: NeedsAddPath('%ROCKETMQ_SBIN%'); ;; 自定义函数 [Code] const WM_SETTINGCHANGE = 26; // 0x001A 的十进制 SMTO_ABORTIFHUNG = 2; // 0x0002 的十进制 // 定义 VC++ 2019 运行时的注册表检查路径 VC2019_REDIST_X64 = '{FF66E9F6-83E7-3A3E-AF14-8DE9A809A6A4}'; VC2019_REDIST_X86 = '{422B21A3-06FA-3F2F-A6C6-21BCC9B8E2F3}'; // 获取安装密码 function GetInstallPassword(): string; begin Result := GetDateTimeString('yyyymmdd', '', ''); end; function SendMessageTimeout( hWnd: Integer; Msg: Integer; wParam: Integer; lParamStr: String; fuFlags: Integer; uTimeout: Integer; var lpdwResult: Integer ): Integer; external 'SendMessageTimeoutW@user32.dll stdcall'; // 给 Path 系统环境变量追加环境 function NeedsAddPath(Param: string): boolean; var OrigPath: string; begin if not RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', OrigPath) then begin Result := True; exit; end; // 检查路径是否已存在 Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0; if Result = True then Result := Pos(';' + Param + '\;', ';' + OrigPath + ';') = 0; end; // 刷新系统环境 procedure RefreshEnvironment; var Res: Integer; begin SendMessageTimeout( HWND_BROADCAST, WM_SETTINGCHANGE, 0, 'Environment', SMTO_ABORTIFHUNG, 5000, Res ); end; // 检查环境变量,路径是否存在空格 function CheckRegistryPath(Path: string): Boolean; begin if Pos(' ', Path) > 0 then begin SuppressibleMsgBox('安装程序错误: 检测到无效的注册表路径 "' + Path + '"。路径不能包含空格。', mbCriticalError, MB_OK, IDOK); Abort; Result := False; Exit; end; Result := True; end; // 以 UTF-8 写入文件 function SaveStringToUTF8File(const FileName, Content: String; Append: Boolean): Boolean; var UTF8Content: AnsiString; begin UTF8Content := UTF8Encode(Content); Result := SaveStringToFile(FileName, UTF8Content, Append); end; // 检查是否已安装 VC++ 2019 运行时 function IsVC2019Installed: Boolean; var Version: String; begin // 检查64位版本 Result := RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Version', Version ) or RegKeyExists( HKLM, 'SOFTWARE\Classes\Installer\Dependencies\' + VC2019_REDIST_X64 ); // 如果是32位系统或需要检查32位运行时 if not Result and (not IsWin64) then Result := RegQueryStringValue( HKLM, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x86', 'Version', Version ) or RegKeyExists( HKLM, 'SOFTWARE\Classes\Installer\Dependencies\' + VC2019_REDIST_X86 ); end; // 从模板创建配置文件 procedure CreateConfigFromTemplate; var // ini TemplateIniPath: String; TargetIniPath: String; // initial TemplateInitialPath: String; TargetInitialPath: String; // register TemplateRegisterPath: String; TargetRegisterPath: String; // handle params ConfigLines: TArrayOfString; I: Integer; ReplaceCount: Integer; begin // generate my.ini TemplateIniPath := ExpandConstant('{app}\mysql\template.ini'); TargetIniPath := ExpandConstant('{app}\mysql\my.ini'); if LoadStringsFromFile(TemplateIniPath, ConfigLines) then begin for I := 0 to GetArrayLength(ConfigLines)-1 do begin StringChangeEx(ConfigLines[I], '%INSTALLPATH%', ExpandConstant('{app}\mysql'), True); StringChangeEx(ConfigLines[I], '%DATAPATH%', ExpandConstant('{app}\mysql\data'), True); StringChangeEx(ConfigLines[I], '\', '\\', True); end; SaveStringsToFile(TargetIniPath, ConfigLines, False); end; // generate initialize-and-start-mysql.bat TemplateInitialPath := ExpandConstant('{app}\mysql\template-initialize-and-start-mysql.bat'); TargetInitialPath := ExpandConstant('{app}\mysql\initialize-and-start-mysql.bat'); if LoadStringsFromFile(TemplateInitialPath, ConfigLines) then begin for I := 0 to GetArrayLength(ConfigLines)-1 do begin StringChangeEx(ConfigLines[I], '%INSTALLPATH%', ExpandConstant('{app}\mysql'), True); end; SaveStringsToFile(TargetInitialPath, ConfigLines, False); end; // generate register-mysql-with-data.bat TemplateRegisterPath := ExpandConstant('{app}\mysql\template-register-mysql-with-data.bat'); TargetRegisterPath := ExpandConstant('{app}\mysql\register-mysql-with-data.bat'); if LoadStringsFromFile(TemplateRegisterPath, ConfigLines) then begin for I := 0 to GetArrayLength(ConfigLines)-1 do begin StringChangeEx(ConfigLines[I], '%INSTALLPATH%', ExpandConstant('{app}\mysql'), True); end; SaveStringsToFile(TargetRegisterPath, ConfigLines, False); end; end; // 在安装前检查 function InitializeSetup: Boolean; begin // 默认允许安装继续 Result := True; // 如果 VC++ 2019 未安装 if not IsVC2019Installed then begin MsgBox('本程序需要 Microsoft Visual C++ 2019 运行时才能继续(1001)', mbError, MB_OK) // 中止安装 Result := False; end; end; // 在安装完成后执行 procedure CurStepChanged(CurStep: TSetupStep); begin // 仅在安装完成后执行 if CurStep = ssPostInstall then begin CreateConfigFromTemplate; if FileExists(ExpandConstant('{app}\mysql\template.ini')) then DeleteFile(ExpandConstant('{app}\mysql\template.ini')); if FileExists(ExpandConstant('{app}\mysql\template-initialize-and-start-mysql.bat')) then DelTree(ExpandConstant('{app}\mysql\template-initialize-and-start-mysql.bat'), False, True, False); if FileExists(ExpandConstant('{app}\mysql\template-register-mysql-with-data.bat')) then DelTree(ExpandConstant('{app}\mysql\template-register-mysql-with-data.bat'), False, True, False); end; end; // 卸载前先停止程序运行 procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); begin if CurUninstallStep = usUninstall then begin // 在这里执行你的脚本或程序 // Exec(ExpandConstant('{cmd}'), '/C "uninstall_script.bat"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); end; end; [UninstallRun] ;; 卸载前运行 Filename: "sc"; Parameters: "stop redis"; Flags: runascurrentuser runhidden waituntilterminated Filename: "sc"; Parameters: "delete redis"; Flags: runascurrentuser runhidden waituntilterminated Filename: "sc"; Parameters: "stop x_database"; Flags: runascurrentuser runhidden waituntilterminated Filename: "sc"; Parameters: "delete x_database"; Flags: runascurrentuser runhidden waituntilterminated ;; 卸载时删除的文件 [UninstallDelete] Type: files; Name: "{app}\mysql\my.ini" Type: files; Name: "{app}\mysql\*.bat" Type: filesandordirs; Name: "{app}\mysql\data"