mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 14:50:45 +00:00
手表端 - 修「连上后一片白屏」:initTabs() 从来没被调用过, 导致 tabDefs 为 null、selectTab 直接 return, 标签栏里只剩一个孤立的滑动指示器 - 应用名改成 ykxmz 被控端 - 修「设备已关闭新设备配对」:配对码本身就是密钥, 拿着正确的码就该放行。老逻辑"首台配对后自动关窗"会把 手表重装的人永久挡在外面 - 应用名改成 ykxmz 服务器端 - / 改成官网(公开,简洁介绍 + 下载) - /zhingzhuanzhan 中转站管理(要登录),/admin 自动跳转 - 管理账号 xmz7891,可改 - 官网 / 登录页 / 管理页都加上 favicon
256 lines
9.6 KiB
Plaintext
256 lines
9.6 KiB
Plaintext
; ============================================================================
|
||
; ykxmz 教室电脑手表遥控 · 被控端安装包
|
||
;
|
||
; 用 Inno Setup 6 编译:
|
||
; "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" installer\ykxmz.iss
|
||
;
|
||
; ── 设计说明 ──
|
||
;
|
||
; 尽量用 Inno 自带的机制,不手搓:
|
||
; * 许可协议页 -> LicenseFile(三份文档合并成一份,点"我接受"才能继续)
|
||
; * 开机自启 -> [Tasks](标准勾选框)
|
||
; * 装完启动 -> [Run] postinstall(标准勾选框)
|
||
; * 安装目录 -> DisableDirPage=no(让用户自己选,默认 Program Files)
|
||
; * 卸载 -> [UninstallRun] 调被控端自己的 --uninstall
|
||
; 只有"填 6 位配对码"这一页 Inno 没有现成的,用标准的
|
||
; CreateInputQueryPage —— 那是 Inno 的官方 API,不算自己造轮子。
|
||
;
|
||
; ── 用户确定的设置 ──
|
||
; 安装位置 默认 C:\Program Files\ykxmz,安装时可改
|
||
; 数据目录 能写 exe 旁就写,不能就转 ProgramData(程序内部处理)
|
||
; 三份文档 放安装包里,统一接受才能下一步
|
||
; 配对码 安装向导里填 6 位
|
||
; 快捷方式 不建开始菜单、不建桌面
|
||
; 开机自启 默认勾上
|
||
; 装完启动 立即静默启动
|
||
; 卸载 一并删掉,什么都不留
|
||
; ============================================================================
|
||
|
||
#define MyAppName "ykxmz"
|
||
#define MyAppVersion "1.7.0"
|
||
#define MyAppPublisher "小麦子"
|
||
#define MyAppExeName "ykxmz.exe"
|
||
|
||
[Setup]
|
||
AppId={{8F3C1D42-7A5E-4B91-9C6D-2E8F4A1B7C30}
|
||
AppName={#MyAppName}
|
||
AppVersion={#MyAppVersion}
|
||
AppVerName={#MyAppName} {#MyAppVersion}
|
||
AppPublisher={#MyAppPublisher}
|
||
VersionInfoVersion={#MyAppVersion}
|
||
|
||
; 默认装到 Program Files\ykxmz,但安装向导里有一页让用户自己改
|
||
DefaultDirName={autopf}\ykxmz
|
||
DisableDirPage=no
|
||
|
||
; 三份文档合并成一份许可页:看完点「我接受」才能继续
|
||
LicenseFile=license-combined.txt
|
||
|
||
; 不建开始菜单组(用户明确不要)
|
||
DisableProgramGroupPage=yes
|
||
AllowNoIcons=yes
|
||
|
||
; 装到 Program Files 和改防火墙都要管理员
|
||
PrivilegesRequired=admin
|
||
ArchitecturesAllowed=x64compatible
|
||
ArchitecturesInstallIn64BitMode=x64compatible
|
||
|
||
OutputDir=..\dist
|
||
OutputBaseFilename=ykxmz-setup-{#MyAppVersion}
|
||
SetupIconFile=..\windows\app.ico
|
||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||
UninstallDisplayName={#MyAppName}
|
||
|
||
Compression=lzma2/max
|
||
SolidCompression=yes
|
||
WizardStyle=modern
|
||
ShowLanguageDialog=no
|
||
DisableWelcomePage=no
|
||
|
||
[Languages]
|
||
Name: "chinese"; MessagesFile: "compiler:Default.isl"
|
||
|
||
[Tasks]
|
||
; 用户要的:默认勾上开机自启(取消勾选也允许)
|
||
Name: "autostart"; Description: "开机自动启动(推荐给教室电脑)"; \
|
||
GroupDescription: "启动选项:"; Flags: checkedonce
|
||
|
||
[Files]
|
||
; 被控端整个目录(ykxmz.exe + _internal)
|
||
Source: "..\dist_exe\ykxmz\*"; DestDir: "{app}"; \
|
||
Flags: recursesubdirs createallsubdirs ignoreversion
|
||
|
||
; 三份文档也放一份到安装目录,装完还能翻
|
||
Source: "..\docs\LICENSE.txt"; DestDir: "{app}\docs"; Flags: ignoreversion
|
||
Source: "..\docs\BEFORE-INSTALL.txt"; DestDir: "{app}\docs"; Flags: ignoreversion
|
||
Source: "..\docs\AFTER-INSTALL.txt"; DestDir: "{app}\docs"; Flags: ignoreversion
|
||
|
||
[Dirs]
|
||
; ⚠ 这一节很关键 —— 不配的话装完一运行就报 WinError 5 拒绝访问。
|
||
;
|
||
; 被控端的配置存在 %ProgramData%\ykxmz\data 下(exe 旁边写不进去时的回退)。
|
||
; 安装包是管理员跑的,它建的目录默认只有管理员能改;
|
||
; 之后普通用户(老师的账号)跑被控端时:
|
||
; 能新建文件,但覆盖不了管理员建的那份 settings.json
|
||
; 结果就是"装完打开报权限错误"。
|
||
;
|
||
; Permissions: users-modify 把所有用户的写权限打开,才是真的能用。
|
||
; 注意末尾不带反斜杠,否则 Inno 会给它单独建一层同名子目录。
|
||
Name: "{commonappdata}\ykxmz"; Permissions: users-modify
|
||
Name: "{commonappdata}\ykxmz\data"; Permissions: users-modify
|
||
|
||
[InstallDelete]
|
||
; 早期版本把配置写在程序目录下的 data\ 里。
|
||
; 现在数据统一放 ProgramData(见 config.py 的 data_dir),
|
||
; 这里把旧的那份清掉 —— 留着的话,两份配置并存,
|
||
; 以后排查问题会被误导("我明明改了配对码怎么没变")。
|
||
Type: filesandordirs; Name: "{app}\data"
|
||
|
||
[Run]
|
||
; 装完立即静默启动(标准 postinstall 勾选框,默认勾上)
|
||
Filename: "{app}\{#MyAppExeName}"; Parameters: "--silent"; \
|
||
Description: "立即启动(后台运行,不显示窗口)"; \
|
||
Flags: nowait postinstall skipifsilent
|
||
|
||
; 让用户看一眼装完须知
|
||
Filename: "{app}\docs\AFTER-INSTALL.txt"; \
|
||
Description: "打开「安装后须知」"; \
|
||
Flags: postinstall shellexec unchecked skipifsilent
|
||
|
||
[UninstallRun]
|
||
; 卸载前先让被控端自己收尾:停进程、删开机自启、清数据目录。
|
||
; 这样"卸载后什么都不留"才是真的干净。
|
||
Filename: "{app}\{#MyAppExeName}"; Parameters: "--uninstall"; \
|
||
Flags: runhidden waituntilterminated; RunOnceId: "StopAgent"
|
||
|
||
[UninstallDelete]
|
||
; 被控端会自己删数据目录,这里兜底再清一遍安装目录里的残留
|
||
Type: filesandordirs; Name: "{app}\data"
|
||
Type: filesandordirs; Name: "{app}\docs"
|
||
Type: filesandordirs; Name: "{app}\_internal"
|
||
|
||
[Code]
|
||
var
|
||
CodePage: TInputQueryWizardPage;
|
||
ForcedCode: String;
|
||
|
||
{ 把字符串里的非数字滤掉。 }
|
||
function OnlyDigits(const raw: String): String;
|
||
var
|
||
i: Integer;
|
||
ch: Char;
|
||
begin
|
||
Result := '';
|
||
for i := 1 to Length(raw) do
|
||
begin
|
||
ch := raw[i];
|
||
if (ch >= '0') and (ch <= '9') then
|
||
Result := Result + ch;
|
||
end;
|
||
end;
|
||
|
||
{ 配对码那一页。Inno 没有现成的输入页,用官方的 CreateInputQueryPage。 }
|
||
procedure InitializeWizard();
|
||
begin
|
||
{ 静默部署可以带 /CODE=123456 直接把码传进来(电教批量装用这个)。
|
||
注意:Inno 的 [Code] 段连**注释里的花括号**都会当常量去解析,
|
||
所以这里不能直接写 param 那种写法,否则编译器报语法错误。 }
|
||
ForcedCode := OnlyDigits(ExpandConstant('{param:CODE|}'));
|
||
|
||
CodePage := CreateInputQueryPage(wpSelectTasks,
|
||
'配对码',
|
||
'给这台电脑定一个 6 位配对码',
|
||
'手表上要输这一串数字才能连上这台电脑。自己定个好记的,' +
|
||
'比如 789113。装完之后也可以在被控端窗口里改。');
|
||
CodePage.Add('6 位配对码:', False);
|
||
if ForcedCode <> '' then
|
||
CodePage.Values[0] := ForcedCode;
|
||
end;
|
||
|
||
{ 校验:必须是 6 位数字。
|
||
注意 —— **静默模式下这一页不会显示**,值是空的,
|
||
这时候绝不能拦(否则 /VERYSILENT 安装必然失败,我第一版就栽在这)。 }
|
||
function NextButtonClick(CurPageID: Integer): Boolean;
|
||
var
|
||
raw: String;
|
||
digits: String;
|
||
begin
|
||
Result := True;
|
||
if CurPageID <> CodePage.ID then
|
||
Exit;
|
||
if WizardSilent then
|
||
Exit;
|
||
|
||
raw := CodePage.Values[0];
|
||
digits := OnlyDigits(raw);
|
||
if (Length(digits) <> 6) and (raw <> '') then
|
||
begin
|
||
MsgBox('配对码要 6 位数字。' + #13#10 + #13#10 +
|
||
'你现在填的是:' + raw + #13#10 +
|
||
'(过滤掉非数字后剩 ' + IntToStr(Length(digits)) + ' 位)' + #13#10 + #13#10 +
|
||
'重新填一下,或者直接点「取消」退出安装。',
|
||
mbError, MB_OK);
|
||
Result := False;
|
||
Exit;
|
||
end;
|
||
{ 规范化,后面 [Run] 阶段用得到 }
|
||
CodePage.Values[0] := digits;
|
||
end;
|
||
|
||
{ 最终用的配对码:命令行 /CODE 优先,其次是向导里填的。
|
||
两个都没有就返回空串 —— 那就跳过 --code,让被控端自己生成一个。 }
|
||
function EffectiveCode(): String;
|
||
begin
|
||
Result := ForcedCode;
|
||
if Result = '' then
|
||
Result := OnlyDigits(CodePage.Values[0]);
|
||
if Length(Result) <> 6 then
|
||
Result := '';
|
||
end;
|
||
|
||
{ 把向导里收集的东西交给被控端。
|
||
注意这里**不能带 --silent** —— 那是常驻守护进程,永远不返回,
|
||
用 ewWaitUntilTerminated 等它会把安装程序挂死(第一版就栽在这)。
|
||
写配置和启动服务分成两次调用:
|
||
--code / --autostart 一次性命令,秒退
|
||
--silent 常驻,交给 [Run] 段用 nowait 启动 }
|
||
procedure CurStepChanged(CurStep: TSetupStep);
|
||
var
|
||
ResultCode: Integer;
|
||
Params: String;
|
||
Code: String;
|
||
begin
|
||
if CurStep <> ssPostInstall then
|
||
Exit;
|
||
|
||
Code := EffectiveCode();
|
||
if Code <> '' then
|
||
begin
|
||
Params := '--code ' + Code;
|
||
if not Exec(ExpandConstant('{app}\{#MyAppExeName}'), Params,
|
||
ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated,
|
||
ResultCode) then
|
||
begin
|
||
if not WizardSilent then
|
||
MsgBox('配对码写入失败(错误码 ' + IntToStr(ResultCode) + ')。' + #13#10 +
|
||
'装完之后双击程序图标,重新设一次也行。',
|
||
mbInformation, MB_OK);
|
||
end;
|
||
end;
|
||
|
||
{ 开机自启:单独调一次,也是一次性命令 }
|
||
if WizardIsTaskSelected('autostart') then
|
||
Exec(ExpandConstant('{app}\{#MyAppExeName}'), '--autostart on',
|
||
ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||
end;
|
||
|
||
{ 卸载前先停掉正在跑的进程,不然文件删不干净。 }
|
||
function InitializeUninstall(): Boolean;
|
||
var
|
||
ResultCode: Integer;
|
||
begin
|
||
Result := True;
|
||
Exec(ExpandConstant('{app}\{#MyAppExeName}'), '--uninstall',
|
||
ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||
end;
|