refactor: 迁移 VET 到新语法规范(R1/R2/R3/R8/R13)

R2 短别名 t.CInt->int 等、R3 c.Addr->&x、R8 t.CDefine->define、R13 c.Set->赋值;R1 两处 T | t.CPtr -> T*(VGuard ULONG*、VFs BOOL*)。已用 TransPyV --project project.vpj --clean 编译通过,VET.exe 无参用法与 --search-paths 运行正常。
This commit is contained in:
TermiNexus committed 2026-10-05 16:43:09 +08:00
1 parent 9adaa5cc46
commit 5ba1310524
14 files changed
+449 -449

No files matched your search

+16 -16
View File
@@ -30,12 +30,12 @@ import w32.win32base as wb
# 字段/容量沿用原实现(App/main.vp 拆分前 :140-150、:188-194)。
# ============================================================
CACHE_CAP: t.CDefine = 256 # 缓存条目上限
CACHE_FILE_MAX: t.CDefine = 16777216 # 缓存文件读取上限 16 MiB
DISP_CAP: t.CDefine = 160 # "Viper " + 版本串
CACHE_TEXT_MAX: t.CDefine = 262144 # 解析文本上限 256 KiB(典型缓存约 60 KiB;超出即视为损坏/异常,按空缓存启动)
CACHE_ARENA_PER_BYTE: t.CDefine = 128 # 解析工作池容量 / 输入字节(保守上界,见 ParseCacheText)
CACHE_ARENA_SLACK: t.CDefine = 4096 # 解析工作池固定余量
define CACHE_CAP = 256 # 缓存条目上限
define CACHE_FILE_MAX = 16777216 # 缓存文件读取上限 16 MiB
define DISP_CAP = 160 # "Viper " + 版本串
define CACHE_TEXT_MAX = 262144 # 解析文本上限 256 KiB(典型缓存约 60 KiB;超出即视为损坏/异常,按空缓存启动)
define CACHE_ARENA_PER_BYTE = 128 # 解析工作池容量 / 输入字节(保守上界,见 ParseCacheText)
define CACHE_ARENA_SLACK = 4096 # 解析工作池固定余量
# 由 App/main.vp 注入
_mbuddy: rawptr[memhub.MemBuddy]
@@ -53,7 +53,7 @@ Cache_Count: int = 0
# source 字面量:扫盘侧(VScan)/ check 侧(VCheck)各自传入,缓存本身不解释其含义。
# 旧缓存文件(无 source 字段)按 SOURCE_FALLBACK 载入,保持既有输出不变。
SOURCE_FALLBACK: t.CDefine = "workspace"
define SOURCE_FALLBACK = "workspace"
# ---- 表读取接口(供 VJson 构建 JSON,不暴露数组本身) ----
@@ -143,7 +143,7 @@ def ClearCache():
def CacheAdd(ExeAnsi: str, Id: str, Ver: str, Arch: str, Src: str, Disp: str) -> int:
global Cache_Count
if Cache_Count >= CACHE_CAP:
VLog.LogFmt1(" 警告: 缓存条目已达上限 %d,忽略新条目\n", t.CSizeT(CACHE_CAP))
VLog.LogFmt1(" 警告: 缓存条目已达上限 %d,忽略新条目\n", sizet(CACHE_CAP))
return 0
E: str = VFs.DupStr(ExeAnsi)
if E is None:
@@ -207,7 +207,7 @@ def ValidateCache() -> int:
# ============================================================
# 把 EmitJson 生成的文本(Buf,共 Len 字节)写入缓存文件(失败只告警,不中断进程)
def WriteCacheFile(Path: str, Buf: bytes, Len: t.CSizeT) -> int:
def WriteCacheFile(Path: str, Buf: bytes, Len: sizet) -> int:
if Buf is None:
return 0
rc: int = VFs.WriteFileAll(Path, Buf, Len)
@@ -270,18 +270,18 @@ def AddParsedEntry(Obj: rawptr[json.JsonValue], ConvBuf: bytes) -> int:
def ParseCacheText(Txt: str) -> int:
if Txt is None:
return 0
L: t.CSizeT = string.strlen(Txt)
L: sizet = string.strlen(Txt)
if L == 0:
return 0
if L > t.CSizeT(CACHE_TEXT_MAX):
if L > sizet(CACHE_TEXT_MAX):
VLog.LogFmt2("缓存: 文本 %zu 字节超出解析上限 %zu,按空缓存启动\n",
L, t.CSizeT(CACHE_TEXT_MAX))
L, sizet(CACHE_TEXT_MAX))
return 0
# 工作池容量:解析树最坏 ≈ 每输入字节 1 个 JsonValue 节点(9 字段 × 8 = 72 字节上界,bump 池
# 再按 8 字节对齐)+ 每字符串一次键名副本与值副本 + 数字/键名临时缓冲(最坏 ≈ 80 字节/输入字节);
# 按 CACHE_ARENA_PER_BYTE(128)再放大,覆盖每次分配的 8 字节对齐浪费。
# (MemPool 耗尽会 printf + exit(1),故必须一次给足;这是不选 MemPool 小池的原因。)
Cap: t.CSizeT = L * t.CSizeT(CACHE_ARENA_PER_BYTE) + t.CSizeT(CACHE_ARENA_SLACK)
Cap: sizet = L * sizet(CACHE_ARENA_PER_BYTE) + sizet(CACHE_ARENA_SLACK)
Arena: bytes = stdlib.malloc(Cap)
if Arena is None:
VLog.LogStr("缓存: 解析工作池分配失败,按空缓存启动\n")
@@ -296,7 +296,7 @@ def ParseCacheText(Txt: str) -> int:
# Need 与 Cap 同源(同一 L、同一每字节放大系数与固定余量),且 L*128 与 4096 都是
# MEMHUB_ALIGN(8) 的倍数 ⇒ 新池 offset=0 时 AllocOk(Need) 恒成立;一旦 Cap 的推导被
# 改小到放不下最坏解析开销,此处即失败并按"空缓存启动"降级,不会再走到 exit(1)。
Need: t.CSizeT = L * t.CSizeT(CACHE_ARENA_PER_BYTE) + t.CSizeT(CACHE_ARENA_SLACK)
Need: sizet = L * sizet(CACHE_ARENA_PER_BYTE) + sizet(CACHE_ARENA_SLACK)
if Pool.AllocOk(Need) == 0:
stdlib.free(t.CPtr(Arena))
VLog.LogFmt1("缓存: 解析工作池预检不足(最坏需要 %zu 字节),按空缓存启动\n",
@@ -326,7 +326,7 @@ def ParseCacheText(Txt: str) -> int:
# 启动时读缓存做种子。文件不存在/读失败/解析失败均为非致命:告警后按空缓存启动。
def LoadCache(Path: str) -> int:
buf: bytes = VFs.ReadWholeFile(Path, t.CSizeT(CACHE_FILE_MAX),
buf: bytes = VFs.ReadWholeFile(Path, sizet(CACHE_FILE_MAX),
wf.FILE_SHARE_READ | wf.FILE_SHARE_WRITE)
if buf is None:
rc_err: int = VFs.LastReadErr
@@ -341,7 +341,7 @@ def LoadCache(Path: str) -> int:
return 0
n: int = ParseCacheText(str(buf))
stdlib.free(t.CPtr(buf))
VLog.VerboseFmt1("缓存: 已载入 %u 项种子\n", t.CSizeT(n))
VLog.VerboseFmt1("缓存: 已载入 %u 项种子\n", sizet(n))
return n
+32 -32
View File
@@ -78,34 +78,34 @@ import lib.VCache as VCache
# ============================================================
# 固定失败原因(check_msg 的全部取值;EXE_ERR 是前缀 + 十六进制码,见 FormatExeErr)
CK_NOT_FOUND: t.CDefine = "NOT_FOUND"
CK_NO_PERMISSION: t.CDefine = "NO_PERMISSION"
CK_NOT_EXE: t.CDefine = "NOT_EXE"
CK_NO_PRVERSN: t.CDefine = "NO_PRVERSN"
CK_PRVERSN_ERR: t.CDefine = "PRVERSN_ERR"
CK_PREFIX_ERR: t.CDefine = "PREFIX_ERR" # --version 拉起成功但输出前 5 字符不是 Viper
CK_TIMEOUT: t.CDefine = "TIMEOUT"
define CK_NOT_FOUND = "NOT_FOUND"
define CK_NO_PERMISSION = "NO_PERMISSION"
define CK_NOT_EXE = "NOT_EXE"
define CK_NO_PRVERSN = "NO_PRVERSN"
define CK_PRVERSN_ERR = "PRVERSN_ERR"
define CK_PREFIX_ERR = "PREFIX_ERR" # --version 拉起成功但输出前 5 字符不是 Viper
define CK_TIMEOUT = "TIMEOUT"
# 通道自身不可用(CheckInit 失败):降级回执的唯一原因串,见 EmitReplyUnavailable
CK_CHANNEL_UNAVAILABLE: t.CDefine = "CHANNEL_UNAVAILABLE"
define CK_CHANNEL_UNAVAILABLE = "CHANNEL_UNAVAILABLE"
# check 命中入库时的 source 值(与扫盘侧 "workspace" 区分)
CK_SOURCE: t.CDefine = "check"
define CK_SOURCE = "check"
# EXE_ERR 的原因串缓冲("EXE_ERR,0x" + 最多 8 位十六进制 + NUL)
CK_MSG_CAP: t.CDefine = 32
define CK_MSG_CAP = 32
# 请求行解析上限(与 VGuard.GUARD_LINE_CAP 对齐:看门狗的行缓冲即本上限,
# 超长行在看门狗侧已被截断/丢弃,这里再兜一层,避免对异常长的输入做解析)
CK_LINE_MAX: t.CDefine = 4096
define CK_LINE_MAX = 4096
# 解析工作池容量 / 输入字节与固定余量:与 VCache.ParseCacheText 同一保守口径
# (最坏 ≈ 每输入字节 1 个 JsonValue 节点 + 键名/值副本 + 数字临时缓冲 ≈ 80 字节/输入字节,
# 再按 128 放大覆盖 8 字节对齐浪费)。MemPool 耗尽会 exit(1),故必须先 AllocOk 预检。
CK_ARENA_PER_BYTE: t.CDefine = 128
CK_ARENA_SLACK: t.CDefine = 4096
define CK_ARENA_PER_BYTE = 128
define CK_ARENA_SLACK = 4096
# 回执工作池:72 KiB。单条回执的全部池分配(Need)见 CK_REPLY_NEED_FIXED 的推导。
CK_REPLY_POOL_CAP: t.CDefine = 73728
define CK_REPLY_POOL_CAP = 73728
# 单条回执在回执池上的固定开销上界:
# * JsonValue 节点 9 个(回执对象 + check_result + id + viper 子对象 + 6 个字符串值)
@@ -116,18 +116,18 @@ CK_REPLY_POOL_CAP: t.CDefine = 73728
# * 整数序列化的临时缓冲 32 字节(write 的 JSON_INT 分支 pool.alloc(32));
# * 余量 512:各值副本的结尾 NUL 与按 MEMHUB_ALIGN(8) 向上取整的浪费。
# 合计 648 + 68 + 4096 + 32 + 512 = 5356 ⇒ 取 5632(512 对齐)。
CK_REPLY_NEED_FIXED: t.CDefine = 5632
define CK_REPLY_NEED_FIXED = 5632
# 回执里 executable / displayName 的取值长度上界:二者都经 Check_Conv(VFs.CONV_CAP 字节)
# 转出(WideCharToMultiByte 以 Cap 封顶并写结尾 NUL,失败时 CopyRaw 写空串)⇒ ≤ CONV_CAP-1。
# 写 2047 = VFs.CONV_CAP(2048) - 1 的字面量(与本仓"t.CDefine 不引用跨模块常量"的惯例一致)。
CK_CONV_MAX: t.CDefine = 2047
define CK_CONV_MAX = 2047
# 降级回执(EmitReplyUnavailable)的临时行缓冲字节数:stdlib.malloc,与 _mbuddy 无关,
# 因为该路径的定义就是"mb 上的缓冲拿不到"。长度上界(含换行与结尾 NUL):
# `{"check_result":false,"id":` 27 + id 最长 20(i64 十进制含负号)
# + `,"check_msg":"` 14 + CHANNEL_UNAVAILABLE 19 + `"}` 2 + '\n' 1 + NUL 1 = 84 ⇒ 取 128。
CK_FALLBACK_CAP: t.CDefine = 128
define CK_FALLBACK_CAP = 128
# 由 App/main.vp 注入(与其它模块同一 MemBuddy 实例)
_mbuddy: rawptr[memhub.MemBuddy]
@@ -233,10 +233,10 @@ def ParseCheckLine(Txt: str) -> int:
Req_PathOk = 0
if Txt is None:
return 0
L: t.CSizeT = string.strlen(Txt)
if L == 0 or L > t.CSizeT(CK_LINE_MAX):
L: sizet = string.strlen(Txt)
if L == 0 or L > sizet(CK_LINE_MAX):
return 0
Cap: t.CSizeT = L * t.CSizeT(CK_ARENA_PER_BYTE) + t.CSizeT(CK_ARENA_SLACK)
Cap: sizet = L * sizet(CK_ARENA_PER_BYTE) + sizet(CK_ARENA_SLACK)
Arena: bytes = stdlib.malloc(Cap)
if Arena is None:
return 0
@@ -323,7 +323,7 @@ def MapPeFailure(Rc: int, OpenErr: ULONG) -> str:
# (0 只会出现在"未到达任何 Win32 调用"的失败阶段,如参数/路径非法)。
# 返回值指向模块级缓冲:调用方须立即使用(EmitReplyFail 会把它复制进回执池)。
def FormatExeErr(Err: ULONG) -> str:
viperlib.snprintf(Check_Msg, t.CSizeT(CK_MSG_CAP), "EXE_ERR,0x%X", Err)
viperlib.snprintf(Check_Msg, sizet(CK_MSG_CAP), "EXE_ERR,0x%X", Err)
return str(Check_Msg)
@@ -347,7 +347,7 @@ def CheckExecutable(PathAnsi: str) -> str:
return CK_PRVERSN_ERR # 段在但内容为空(第一个字节即 NUL)
# ② --version 验证(进程正常结束且前 5 字符为 Viper 才算通过)
pc: int = VProc.RunVersionProbe(PathAnsi, Check_Probe,
t.CSizeT(VProc.VERSION_PROBE_DIAG_CAP), 0)
sizet(VProc.VERSION_PROBE_DIAG_CAP), 0)
if pc == 2 or VProc.Probe_TimedOut != 0:
return CK_TIMEOUT
if pc != 0:
@@ -356,7 +356,7 @@ def CheckExecutable(PathAnsi: str) -> str:
if VProc.OutputHasViperPrefix(str(Check_Probe)) == 0:
# 拉起成功但输出前 5 字符不是 Viper ⇒ 独立原因 PREFIX_ERR(与段定位/读取/内容异常区分)
return CK_PREFIX_ERR
viperlib.snprintf(Check_Disp, t.CSizeT(VCache.DISP_CAP), "Viper %s", str(Check_Ver))
viperlib.snprintf(Check_Disp, sizet(VCache.DISP_CAP), "Viper %s", str(Check_Ver))
return None
@@ -401,8 +401,8 @@ def TrackChecked(PathAnsi: str) -> int:
# 换行与新的 NUL。这样做是为了让"一行回执"始终是**单次写**——与别人(看门狗失败行、
# 下一轮数组)写入不会在同一行中间交错。
def WriteReplyLine(Txt: str) -> int:
n: t.CSizeT = string.strlen(Txt)
if n + 2 <= t.CSizeT(4096):
n: sizet = string.strlen(Txt)
if n + 2 <= sizet(4096):
Txt[n] = '\n'
Txt[n + 1] = 0
VLog.StdoutWrite(Txt)
@@ -437,10 +437,10 @@ def EmitReplyOk(ReqIdVal: LONGLONG, PathAnsi: str):
Id = str(Check_Id)
TrackChecked(PathAnsi)
Check_Pool.reset()
ValNeed: t.CSizeT = t.CSizeT(CK_CONV_MAX) + t.CSizeT(CK_CONV_MAX)
ValNeed: sizet = sizet(CK_CONV_MAX) + sizet(CK_CONV_MAX)
ValNeed = ValNeed + string.strlen(Id) + string.strlen(Ver) + string.strlen(Arch)
ValNeed = ValNeed + string.strlen(CK_SOURCE)
if Check_Pool.AllocOk(t.CSizeT(CK_REPLY_NEED_FIXED) + ValNeed) == 0:
if Check_Pool.AllocOk(sizet(CK_REPLY_NEED_FIXED) + ValNeed) == 0:
VLog.LogStr(" 警告: check 回执工作池不足,未回复(不会写穿缓冲)\n")
return
Obj: rawptr[json.JsonValue] = json.object(Check_Pool)
@@ -448,7 +448,7 @@ def EmitReplyOk(ReqIdVal: LONGLONG, PathAnsi: str):
VLog.LogStr(" 警告: check 回执对象分配失败,未回复\n")
return
json.object_set(Check_Pool, Obj, "check_result", json.bool_val(Check_Pool, True))
json.object_set(Check_Pool, Obj, "id", json.int_val(Check_Pool, t.CInt64T(ReqIdVal)))
json.object_set(Check_Pool, Obj, "id", json.int_val(Check_Pool, int64(ReqIdVal)))
Sub: rawptr[json.JsonValue] = json.object(Check_Pool)
if Sub == None:
VLog.LogStr(" 警告: check 回执对象分配失败,未回复\n")
@@ -475,7 +475,7 @@ def EmitReplyOk(ReqIdVal: LONGLONG, PathAnsi: str):
# 失败回执:{"check_result": false, "id": <id>, "check_msg": "<原因>"}
def EmitReplyFail(ReqIdVal: LONGLONG, Msg: str):
Check_Pool.reset()
Need: t.CSizeT = t.CSizeT(CK_REPLY_NEED_FIXED)
Need: sizet = sizet(CK_REPLY_NEED_FIXED)
if Msg != None:
Need = Need + string.strlen(Msg)
if Check_Pool.AllocOk(Need) == 0:
@@ -486,7 +486,7 @@ def EmitReplyFail(ReqIdVal: LONGLONG, Msg: str):
VLog.LogStr(" 警告: check 回执对象分配失败,未回复\n")
return
json.object_set(Check_Pool, Obj, "check_result", json.bool_val(Check_Pool, False))
json.object_set(Check_Pool, Obj, "id", json.int_val(Check_Pool, t.CInt64T(ReqIdVal)))
json.object_set(Check_Pool, Obj, "id", json.int_val(Check_Pool, int64(ReqIdVal)))
json.object_set(Check_Pool, Obj, "check_msg", json.string_val(Check_Pool, Msg))
Txt: str = json.write(Check_Pool, Obj, False)
if Txt == None:
@@ -506,9 +506,9 @@ def EmitReplyUnavailable(ReqIdVal: LONGLONG) -> int:
Fb: bytes = stdlib.malloc(CK_FALLBACK_CAP)
if Fb is None:
return 0
viperlib.snprintf(Fb, t.CSizeT(CK_FALLBACK_CAP),
viperlib.snprintf(Fb, sizet(CK_FALLBACK_CAP),
"{\"check_result\":false,\"id\":%lld,\"check_msg\":\"%s\"}\n",
t.CInt64T(ReqIdVal), CK_CHANNEL_UNAVAILABLE)
int64(ReqIdVal), CK_CHANNEL_UNAVAILABLE)
VLog.StdoutWrite(str(Fb))
stdlib.free(t.CPtr(Fb))
return 1
+15 -15
View File
@@ -38,21 +38,21 @@ import lib.VDirs as VDirs
# 取参方式依据:sys._init_argv/_argc/_argv(includes/sys.vp:60-67)。
# ============================================================
VET_VERSION: t.CDefine = "0.2.0"
SECS_MAX: t.CDefine = 86400 # --need-confirm 秒数上界(1 天;超出视为参数错误)
DEFAULT_SLEEP_MS: t.CDefine = 10000 # --sleep 缺省值(沿用原 SCAN_INTERVAL_MS)
SLEEP_MAX_MS: t.CDefine = 86400000 # --sleep 毫秒上界(1 天;超出视为参数错误)
DEFAULT_CONFIRM_TIMEOUT_MS: t.CDefine = 5000 # --need-confirm-timeout 缺省值
CONFIRM_TIMEOUT_MAX_MS: t.CDefine = 86400000 # --need-confirm-timeout 毫秒上界(1 天)
define VET_VERSION = "0.2.0"
define SECS_MAX = 86400 # --need-confirm 秒数上界(1 天;超出视为参数错误)
define DEFAULT_SLEEP_MS = 10000 # --sleep 缺省值(沿用原 SCAN_INTERVAL_MS)
define SLEEP_MAX_MS = 86400000 # --sleep 毫秒上界(1 天;超出视为参数错误)
define DEFAULT_CONFIRM_TIMEOUT_MS = 5000 # --need-confirm-timeout 缺省值
define CONFIRM_TIMEOUT_MAX_MS = 86400000 # --need-confirm-timeout 毫秒上界(1 天)
# --pace <毫秒>:节流点睡眠时长,取值域 0..60000。0 = 关闭节流(两个节流点都不 Sleep)。
# 上界取 1 分钟:节流只为降低扫盘对机器的持续压力,睡眠远超 1 分钟只会无谓拖慢单轮而无收益,
# 与 --sleep(轮间隔,上界 1 天)的用途不同,故不共用其宽松上界。
DEFAULT_PACE_SLEEP_MS: t.CDefine = 20 # --pace 缺省值(沿用 VScan.PACE_SLEEP_MS,未给出时逐字保持现状)
PACE_SLEEP_MAX_MS: t.CDefine = 60000 # --pace 毫秒上界(1 分钟)
define DEFAULT_PACE_SLEEP_MS = 20 # --pace 缺省值(沿用 VScan.PACE_SLEEP_MS,未给出时逐字保持现状)
define PACE_SLEEP_MAX_MS = 60000 # --pace 毫秒上界(1 分钟)
# --pace-units <N>:文件粒度,取值域 1..1000000(每 N 个文件一个节流点;1 = 每个文件都停)。
# 0 无意义("每 0 个文件"),与非法输入一并报错;上界 1000000 已足够稀疏(等同关闭文件侧节流)。
DEFAULT_PACE_FILE_UNITS: t.CDefine = 64 # --pace-units 缺省值(沿用 VScan.PACE_FILE_UNITS,未给出时逐字保持现状)
PACE_UNITS_MAX: t.CDefine = 1000000 # --pace-units 文件数上界
define DEFAULT_PACE_FILE_UNITS = 64 # --pace-units 缺省值(沿用 VScan.PACE_FILE_UNITS,未给出时逐字保持现状)
define PACE_UNITS_MAX = 1000000 # --pace-units 文件数上界
# --parent-pid 上界(DWORD 全域 4294967295)。
# 注意:这里不能用 `t.CDefine = 4294967295`。CDefine 的物化规则(TPV
# ExprGenCdef._const_int_for_cdefine)把落在 u32 范围内的值一律按 **i32** 常量生成
@@ -348,7 +348,7 @@ def ParseScanArgs() -> int:
M: int = ParseUint(sys._argv[i + 1], SLEEP_MAX_MS)
if M < 0:
VLog.LogFmt1("错误: --sleep 需要一个 0..%u 的整数字面量毫秒数,实际为: ",
t.CSizeT(SLEEP_MAX_MS))
sizet(SLEEP_MAX_MS))
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
PrintUsage()
return 1
@@ -363,7 +363,7 @@ def ParseScanArgs() -> int:
P: int = ParseUint(sys._argv[i + 1], PACE_SLEEP_MAX_MS)
if P < 0:
VLog.LogFmt1("错误: --pace 需要一个 0..%u 的整数字面量毫秒数,实际为: ",
t.CSizeT(PACE_SLEEP_MAX_MS))
sizet(PACE_SLEEP_MAX_MS))
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
PrintUsage()
return 1
@@ -380,7 +380,7 @@ def ParseScanArgs() -> int:
# 0 与非法同为错误(ParseUint 非法返回 -1):0 会让"每 0 个文件一个节流点"无意义
if U <= 0:
VLog.LogFmt1("错误: --pace-units 需要一个 1..%u 的整数字面量文件数,实际为: ",
t.CSizeT(PACE_UNITS_MAX))
sizet(PACE_UNITS_MAX))
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
PrintUsage()
return 1
@@ -401,7 +401,7 @@ def ParseScanArgs() -> int:
if S < 0:
# 两个固定元数调用拼一行(LogFmt1 只带整数,LogFmtS1 只带字符串)
VLog.LogFmt1("错误: --need-confirm 需要一个 0..%u 的整数字面量秒数,实际为: ",
t.CSizeT(SECS_MAX))
sizet(SECS_MAX))
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
PrintUsage()
return 1
@@ -416,7 +416,7 @@ def ParseScanArgs() -> int:
T: int = ParseUint(sys._argv[i + 1], CONFIRM_TIMEOUT_MAX_MS)
if T < 0:
VLog.LogFmt1("错误: --need-confirm-timeout 需要一个 0..%u 的整数字面量毫秒数,实际为: ",
t.CSizeT(CONFIRM_TIMEOUT_MAX_MS))
sizet(CONFIRM_TIMEOUT_MAX_MS))
VLog.LogFmtS1("%s\n", sys._argv[i + 1])
PrintUsage()
return 1
+24 -24
View File
@@ -36,7 +36,7 @@ import w32.win32base as wb
# ============================================================
# Win32: typedef LONG HRESULT(有符号 32 位,负值才是失败)
HRESULT: t.CTypedef = t.CInt32T
HRESULT: t.CTypedef = int32
# Win32: typedef const KNOWNFOLDERID *REFKNOWNFOLDERID(指向 16 字节 GUID)
REFKNOWNFOLDERID: t.CTypedef = t.CPtr
@@ -48,22 +48,22 @@ extern def SHGetKnownFolderPath(rfid: REFKNOWNFOLDERID, dwFlags: DWORD, hToken:
ppszPath: t.CPtr) -> HRESULT
extern def CoTaskMemFree(pv: t.CPtr)
SEM_FAILCRITICALERRORS: t.CDefine = 0x0001 # SetErrorMode 标志(errhandlingapi.h)
KNOWNFOLDERID_SIZE: t.CDefine = 16 # KNOWNFOLDERID / GUID 字节数
define SEM_FAILCRITICALERRORS = 0x0001 # SetErrorMode 标志(errhandlingapi.h)
define KNOWNFOLDERID_SIZE = 16 # KNOWNFOLDERID / GUID 字节数
# winnt.h: FILE_ATTRIBUTE_REPARSE_POINT 在 VScan 使用
# GetDriveTypeA 返回值(winbase.h)
DRIVE_NO_ROOT_DIR: t.CDefine = 1 # 跳过:无根路径
DRIVE_REMOVABLE: t.CDefine = 2 # 探测:可移动介质(U 盘/读卡器)
DRIVE_FIXED: t.CDefine = 3 # 探测:定长磁盘
DRIVE_REMOTE: t.CDefine = 4 # 探测:网络盘
DRIVE_CDROM: t.CDefine = 5 # 跳过:光驱
define DRIVE_NO_ROOT_DIR = 1 # 跳过:无根路径
define DRIVE_REMOVABLE = 2 # 探测:可移动介质(U 盘/读卡器)
define DRIVE_FIXED = 3 # 探测:定长磁盘
define DRIVE_REMOTE = 4 # 探测:网络盘
define DRIVE_CDROM = 5 # 跳过:光驱
# 决策:只跳过 NO_ROOT_DIR(1) 与 CDROM(5),其余(含 REMOVABLE)继续探测 Program Files;
# 空介质/卡读卡器触发的弹窗与阻塞由 AddFixedDriveProgramFiles 里的 SetErrorMode 抑制。
COMMON_DIR_CAPACITY: t.CDefine = 128 # 表容量(内联数组元素个数)
DRIVE_ROOT_CAP: t.CDefine = 8 # "X:\" + NUL 缓冲
DESKTOP_SUFFIX_CAP: t.CDefine = 9 # r"\Desktop"(8) + NUL(1)
define COMMON_DIR_CAPACITY = 128 # 表容量(内联数组元素个数)
define DRIVE_ROOT_CAP = 8 # "X:\" + NUL 缓冲
define DESKTOP_SUFFIX_CAP = 9 # r"\Desktop"(8) + NUL(1)
# 由 App/main.vp 注入
_mbuddy: rawptr[memhub.MemBuddy]
@@ -125,7 +125,7 @@ def AddCommonDirForce(Path: str) -> int:
return 0
i += 1
if Common_Dir_Count >= COMMON_DIR_CAPACITY:
VLog.LogFmt1(" 警告: 常用目录表已满 (%d),已忽略该条\n", t.CSizeT(COMMON_DIR_CAPACITY))
VLog.LogFmt1(" 警告: 常用目录表已满 (%d),已忽略该条\n", sizet(COMMON_DIR_CAPACITY))
return 0
Common_Directory[Common_Dir_Count] = Path
Common_Dir_Count += 1
@@ -155,10 +155,10 @@ def AddFixedDriveProgramFiles() -> int:
Added: int = 0
Letter: int = 0
while Letter < 26:
if (Mask & (t.CUInt32T(1) << t.CUInt32T(Letter))) != 0:
if (Mask & (uint32(1) << uint32(Letter))) != 0:
Root: bytes = _mbuddy.alloc(DRIVE_ROOT_CAP)
if Root is not None:
Root[0] = t.CChar(65 + Letter) # 'A' + Letter
Root[0] = char(65 + Letter) # 'A' + Letter
Root[1] = ':'
Root[2] = '\\'
Root[3] = 0
@@ -169,12 +169,12 @@ def AddFixedDriveProgramFiles() -> int:
Pf: bytes = _mbuddy.alloc(VFs.PATH_BUF_CAP)
if Pf is not None:
string.strcpy(Pf, r"Q:\Program Files") # 'Q' 占位,下句改写为首字母
Pf[0] = t.CChar(65 + Letter)
Pf[0] = char(65 + Letter)
Added += AddCommonDir(str(Pf))
Pfx: bytes = _mbuddy.alloc(VFs.PATH_BUF_CAP)
if Pfx is not None:
string.strcpy(Pfx, r"Q:\Program Files (x86)")
Pfx[0] = t.CChar(65 + Letter)
Pfx[0] = char(65 + Letter)
Added += AddCommonDir(str(Pfx))
Letter += 1
return Added
@@ -185,7 +185,7 @@ def AddFixedDriveProgramFiles() -> int:
def AddDesktopFallback(Buf: bytes) -> int:
n: DWORD = GetEnvironmentVariableA("USERPROFILE", Buf, VFs.PATH_BUF_CAP)
# 还需追加 "\Desktop"(8 字节)+ NUL,n + 9 必须不超过缓冲容量
if n == 0 or n + t.CUInt32T(DESKTOP_SUFFIX_CAP) > t.CUInt32T(VFs.PATH_BUF_CAP):
if n == 0 or n + uint32(DESKTOP_SUFFIX_CAP) > uint32(VFs.PATH_BUF_CAP):
VLog.LogFmt1(" 桌面兜底: 获取 USERPROFILE 失败或过长 (win32 err=%u)\n", wb.GetLastError())
return 0
Buf[n] = 0
@@ -236,13 +236,13 @@ def AddCurrentUserDesktop() -> int:
GuidBuf[15] = 0x41
Pwsz: WCHARPTR = None
Hr: HRESULT = SHGetKnownFolderPath(t.CPtr(GuidBuf), 0, None, t.CPtr(c.Addr(Pwsz)))
Hr: HRESULT = SHGetKnownFolderPath(t.CPtr(GuidBuf), 0, None, t.CPtr(&Pwsz))
stdlib.free(t.CPtr(GuidBuf))
if Hr < 0:
# 规范上失败时 *ppszPath 会被置 NULL;仍防御性释放非空返回值,杜绝泄漏
if Pwsz is not None:
CoTaskMemFree(t.CPtr(Pwsz))
VLog.LogFmt1(" 桌面: SHGetKnownFolderPath 失败 (hr=0x%X),改用兜底路径\n", t.CSizeT(t.CUInt32T(Hr)))
VLog.LogFmt1(" 桌面: SHGetKnownFolderPath 失败 (hr=0x%X),改用兜底路径\n", sizet(uint32(Hr)))
return AddDesktopFallback(Buf)
if Pwsz is None:
VLog.LogStr(" 桌面: SHGetKnownFolderPath 返回空指针,改用兜底路径\n")
@@ -253,7 +253,7 @@ def AddCurrentUserDesktop() -> int:
if Needed <= 0 or Needed > VFs.PATH_BUF_CAP:
CoTaskMemFree(t.CPtr(Pwsz))
VLog.LogFmt2(" 桌面: 路径为空或过长 (%d 字节, 上限 %d),改用兜底路径\n",
t.CSizeT(t.CUInt32T(Needed)), t.CSizeT(VFs.PATH_BUF_CAP))
sizet(uint32(Needed)), sizet(VFs.PATH_BUF_CAP))
return AddDesktopFallback(Buf)
Got: int = VFs.WideToUtf8(Pwsz, Buf, VFs.PATH_BUF_CAP)
CoTaskMemFree(t.CPtr(Pwsz)) # 用后立即释放:成功/失败分支的同一释放点
@@ -274,9 +274,9 @@ def PrintCommonDirs() -> int:
return 1
i: int = 0
while i < Common_Dir_Count:
VLog.VerboseFmtIS(" [%d] %s\n", t.CSizeT(i), Common_Directory[i])
VLog.VerboseFmtIS(" [%d] %s\n", sizet(i), Common_Directory[i])
i += 1
VLog.VerboseFmt1(" 合计 %d 个常用目录\n", t.CSizeT(Common_Dir_Count))
VLog.VerboseFmt1(" 合计 %d 个常用目录\n", sizet(Common_Dir_Count))
return 0
@@ -288,7 +288,7 @@ def BuildTable() -> int:
AddCommonDirForce(r"D:\Viper")
AddFixedDriveProgramFiles() # 各盘 Program Files / Program Files (x86)
AddCurrentUserDesktop() # 当前用户桌面
VLog.VerboseFmt1(" 目录表来源: 自动表 (%u 条)\n", t.CSizeT(Common_Dir_Count))
VLog.VerboseFmt1(" 目录表来源: 自动表 (%u 条)\n", sizet(Common_Dir_Count))
else:
# 强制替换:完全不做自动构建,表 = --path 序列(保序;去重由 AddCommonDirForce)
i: int = 0
@@ -298,5 +298,5 @@ def BuildTable() -> int:
else:
AddCommonDirForce(Override_Dir[i])
i += 1
VLog.VerboseFmt1(" 目录表来源: 覆盖表 (%u 条)\n", t.CSizeT(Common_Dir_Count))
VLog.VerboseFmt1(" 目录表来源: 覆盖表 (%u 条)\n", sizet(Common_Dir_Count))
return PrintCommonDirs()
+38 -38
View File
@@ -36,24 +36,24 @@ import w32.win32base as wb
# 原型出处:Win32 API 文档 / winnls.h。
# ============================================================
PATH_BUF_CAP: t.CDefine = 512 # 单条目录/文件路径缓冲
CONV_CAP: t.CDefine = 2048 # 编码转换临时缓冲
INVALID_FILE_ATTRIBUTES: t.CDefine = 0xFFFFFFFF # GetFileAttributesA 失败返回值
CP_UTF8: t.CDefine = 65001 # Win32 代码页 UTF-8
CP_ACP: t.CDefine = 0 # Win32 代码页 ANSI(A 系列 API 的当前页)
define PATH_BUF_CAP = 512 # 单条目录/文件路径缓冲
define CONV_CAP = 2048 # 编码转换临时缓冲
define INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF # GetFileAttributesA 失败返回值
define CP_UTF8 = 65001 # Win32 代码页 UTF-8
define CP_ACP = 0 # Win32 代码页 ANSI(A 系列 API 的当前页)
extern def WideCharToMultiByte(CodePage: UINT, dwFlags: DWORD, lpWideCharStr: WCHARPTR,
cchWideChar: int, lpMultiByteStr: CHARPTR, cbMultiByte: int,
lpDefaultChar: wb.LPCSTR, lpUsedDefaultChar: BOOL | t.CPtr) -> int
lpDefaultChar: wb.LPCSTR, lpUsedDefaultChar: BOOL*) -> int
# 由 App/main.vp 注入(与其它模块同一 MemBuddy 实例)
_mbuddy: rawptr[memhub.MemBuddy]
# ReadWholeFile / WriteFileAll 的结果输出(模块全局)
LastReadSize: t.CSizeT = 0 # 成功时的实际读取字节数
LastReadSize: sizet = 0 # 成功时的实际读取字节数
LastReadErr: int = 0 # 1=打开失败 2=取大小失败 3=空文件 4=超过上限 5=分配失败
LastReadWinErr: ULONG = 0 # 打开失败时的 GetLastError()
LastWriteDone: t.CSizeT = 0 # WriteFileAll 实际写入字节数
LastWriteDone: sizet = 0 # WriteFileAll 实际写入字节数
# ============================================================
@@ -69,11 +69,11 @@ def CopyRaw(Dst: bytes, Cap: int, Src: str) -> int:
if Src is None:
Dst[0] = 0
return 0
n: t.CSizeT = string.strlen(Src)
if n + 1 > t.CSizeT(Cap):
n: sizet = string.strlen(Src)
if n + 1 > sizet(Cap):
Dst[0] = 0
return 0
i: t.CSizeT = 0
i: sizet = 0
while i < n:
Dst[i] = Src[i]
i += 1
@@ -86,11 +86,11 @@ def CopyRaw(Dst: bytes, Cap: int, Src: str) -> int:
def DupStr(S: str) -> str:
if S is None:
return None
n: t.CSizeT = string.strlen(S)
n: sizet = string.strlen(S)
b: bytes = _mbuddy.alloc(n + 1)
if b is None:
return None
i: t.CSizeT = 0
i: sizet = 0
while i < n:
b[i] = S[i]
i += 1
@@ -125,24 +125,24 @@ def StrEqNoCase(A: str, B: str) -> int:
def JoinPath(Out: bytes, Cap: int, Parent: str, Child: str) -> int:
if Out is None or Parent is None or Child is None:
return 0
pl: t.CSizeT = string.strlen(Parent)
cl: t.CSizeT = string.strlen(Child)
pl: sizet = string.strlen(Parent)
cl: sizet = string.strlen(Child)
need_sep: int = 0
if pl > 0:
last: int = int(Parent[t.CInt(pl) - 1])
last: int = int(Parent[int(pl) - 1])
if last != 47 and last != 92: # '/' or '\'
need_sep = 1
total: t.CSizeT = pl + cl + t.CSizeT(need_sep)
if total + 1 > t.CSizeT(Cap):
total: sizet = pl + cl + sizet(need_sep)
if total + 1 > sizet(Cap):
return 0
i: t.CSizeT = 0
i: sizet = 0
while i < pl:
Out[i] = Parent[i]
i += 1
if need_sep != 0:
Out[i] = '\\'
i += 1
j: t.CSizeT = 0
j: sizet = 0
while j < cl:
Out[i] = Child[j]
i += 1
@@ -184,7 +184,7 @@ def Utf8ToAnsi(Src: str, Dst: bytes, Cap: int) -> int:
if n <= 0:
CopyRaw(Dst, Cap, Src)
return 0
W: WCHARPTR = stdlib.malloc(t.CSizeT(n) * 2)
W: WCHARPTR = stdlib.malloc(sizet(n) * 2)
if W is None:
CopyRaw(Dst, Cap, Src)
return 0
@@ -206,7 +206,7 @@ def AnsiToUtf8(Src: str, Dst: bytes, Cap: int) -> int:
if n <= 0:
CopyRaw(Dst, Cap, Src)
return 0
W: WCHARPTR = stdlib.malloc(t.CSizeT(n) * 2)
W: WCHARPTR = stdlib.malloc(sizet(n) * 2)
if W is None:
CopyRaw(Dst, Cap, Src)
return 0
@@ -240,25 +240,25 @@ def WideToUtf8(Src: WCHARPTR, Dst: bytes, Cap: int) -> int:
# ============================================================
# 从当前文件指针读满 Want 字节(循环),返回实际读取字节数
def ReadFull(h: wb.HANDLE, Buf: bytes, Want: t.CSizeT) -> t.CSizeT:
total: t.CSizeT = 0
def ReadFull(h: wb.HANDLE, Buf: bytes, Want: sizet) -> sizet:
total: sizet = 0
while total < Want:
chunk: t.CSizeT = Want - total
chunk: sizet = Want - total
if chunk > 0x7FFFFFFF:
chunk = t.CSizeT(0x7FFFFFFF)
chunk = sizet(0x7FFFFFFF)
br: ULONG = 0
if wf.ReadFile(h, Buf + total, ULONG(chunk), t.CPtr(c.Addr(br)), None) == 0:
if wf.ReadFile(h, Buf + total, ULONG(chunk), t.CPtr(&br), None) == 0:
break
if br == 0:
break
total += t.CSizeT(br)
total += sizet(br)
return total
# 读取整个文件到 stdlib 分配的缓冲(含 NUL 结尾)。成功返回 bytes,调用方用 stdlib.free 释放;
# 失败返回 None,原因见 LastReadErr(4 仅在 MaxBytes>0 时产生)。
# Share 由调用方给出(各调用点原有的 dwShareMode 语义保持一致)。
def ReadWholeFile(Path: str, MaxBytes: t.CSizeT, Share: ULONG) -> bytes:
def ReadWholeFile(Path: str, MaxBytes: sizet, Share: ULONG) -> bytes:
global LastReadSize, LastReadErr, LastReadWinErr
LastReadSize = 0
LastReadErr = 0
@@ -270,11 +270,11 @@ def ReadWholeFile(Path: str, MaxBytes: t.CSizeT, Share: ULONG) -> bytes:
LastReadErr = 1
return None
li: wb.LARGE_INTEGER = wb.LARGE_INTEGER()
if wf.GetFileSizeEx(H, t.CPtr(c.Addr(li))) == 0:
if wf.GetFileSizeEx(H, t.CPtr(&li)) == 0:
wb.CloseHandle(H)
LastReadErr = 2
return None
Sz: t.CSizeT = t.CSizeT(li.QuadPart)
Sz: sizet = sizet(li.QuadPart)
if Sz <= 0:
wb.CloseHandle(H)
LastReadErr = 3
@@ -288,7 +288,7 @@ def ReadWholeFile(Path: str, MaxBytes: t.CSizeT, Share: ULONG) -> bytes:
wb.CloseHandle(H)
LastReadErr = 5
return None
Got: t.CSizeT = ReadFull(H, Buf, Sz)
Got: sizet = ReadFull(H, Buf, Sz)
wb.CloseHandle(H)
Buf[Got] = 0
LastReadSize = Got
@@ -297,7 +297,7 @@ def ReadWholeFile(Path: str, MaxBytes: t.CSizeT, Share: ULONG) -> bytes:
# 把 Buf[0..Len) 全量写入 Path(CREATE_ALWAYS)。
# 返回 1=完整写入,0=打开/写入失败,-1=写入不完整(实际字节见 LastWriteDone)
def WriteFileAll(Path: str, Buf: bytes, Len: t.CSizeT) -> int:
def WriteFileAll(Path: str, Buf: bytes, Len: sizet) -> int:
global LastWriteDone
LastWriteDone = 0
if Buf is None:
@@ -306,17 +306,17 @@ def WriteFileAll(Path: str, Buf: bytes, Len: t.CSizeT) -> int:
None, wf.CREATE_ALWAYS, wf.FILE_ATTRIBUTE_NORMAL, None)
if H == wb.INVALID_HANDLE_VALUE:
return 0
total: t.CSizeT = 0
total: sizet = 0
while total < Len:
chunk: t.CSizeT = Len - total
chunk: sizet = Len - total
if chunk > 0x7FFFFFFF:
chunk = t.CSizeT(0x7FFFFFFF)
chunk = sizet(0x7FFFFFFF)
bw: ULONG = 0
if wf.WriteFile(H, Buf + total, ULONG(chunk), t.CPtr(c.Addr(bw)), None) == 0:
if wf.WriteFile(H, Buf + total, ULONG(chunk), t.CPtr(&bw), None) == 0:
break
if bw == 0:
break
total += t.CSizeT(bw)
total += sizet(bw)
wb.CloseHandle(H)
LastWriteDone = total
if total < Len:
+87 -87
View File
@@ -216,45 +216,45 @@ import w32.win32console as wc
# 见"加固 1"与 GuardResolveReadEx)。
# ============================================================
SYNCHRONIZE: t.CDefine = 0x00100000 # winnt.h
define SYNCHRONIZE = 0x00100000 # winnt.h
# OpenProcess 需要的访问权限:PROCESS_TERMINATE(0x0001) | SYNCHRONIZE(0x00100000)
WATCH_PARENT_ACCESS: t.CDefine = 0x00100001
CONFIRM_WATCH_CMD: t.CDefine = "confirm-watch" # 内部隐藏子命令名
define WATCH_PARENT_ACCESS = 0x00100001
define CONFIRM_WATCH_CMD = "confirm-watch" # 内部隐藏子命令名
# stdin 行缓冲:既要放 {"ok": 1},也要放 check 请求行({"check":"<exe 绝对路径>","id":<uint64>},
# 路径可达 MAX_PATH 量级)⇒ 取 4096(与 VCheck.CK_LINE_MAX 同值)。行超此上限按"一行结束"
# 处理(与旧实现"缓冲写满即截断成一行"一致;超长的 check 行会因路径被截断而判 NOT_FOUND)。
GUARD_LINE_CAP: t.CDefine = 4096
GUARD_STAMP_CAP: t.CDefine = 64 # 时间戳文本缓冲
SELF_PATH_CAP: t.CDefine = 1024 # GetModuleFileNameA 输出缓冲
define GUARD_LINE_CAP = 4096
define GUARD_STAMP_CAP = 64 # 时间戳文本缓冲
define SELF_PATH_CAP = 1024 # GetModuleFileNameA 输出缓冲
# 命令行额外量(子命令 + interval/timeout/parent-pid/ipc-write + --verbose + NUL;句柄按
# 最多 20 位十进制字符计。留足余量以免 snprintf 截断出"看似正常实则错参"的命令行)
WATCH_CMD_EXTRA_CAP: t.CDefine = 256
WATCH_HANDLE_COUNT: t.CDefine = 2 # WaitForMultipleObjects 句柄个数
define WATCH_CMD_EXTRA_CAP = 256
define WATCH_HANDLE_COUNT = 2 # WaitForMultipleObjects 句柄个数
# 拉起后"存活校验"的等待窗口(毫秒)。代价:每次 scan 启动多等这么一会儿(README 已注明);
# 取 400 是"够看门狗跑完参数解析/OpenProcess/首次等待"与"不显著拖慢启动"之间的折中。
WATCH_ALIVE_PROBE_MS: t.CDefine = 400
define WATCH_ALIVE_PROBE_MS = 400
# ---- 非阻塞确认输入(控制台 / 管道 / 重定向)----
# GetFileType 返回值(fileapi.h;本仓 include 未声明这几个常量)
FILE_TYPE_DISK: t.CDefine = 0x0001 # 磁盘文件(含 `< file` 重定向)
FILE_TYPE_CHAR: t.CDefine = 0x0002 # 字符设备(控制台/LPT 等)
FILE_TYPE_PIPE: t.CDefine = 0x0003 # 管道/套接字(含 `|` 重定向)
define FILE_TYPE_DISK = 0x0001 # 磁盘文件(含 `< file` 重定向)
define FILE_TYPE_CHAR = 0x0002 # 字符设备(控制台/LPT 等)
define FILE_TYPE_PIPE = 0x0003 # 管道/套接字(含 `|` 重定向)
# 输入形态(GuardInputKind 返回值)
GUARD_IN_CONSOLE: t.CDefine = 1 # 控制台(CHAR + GetConsoleMode 成功)
GUARD_IN_PIPE: t.CDefine = 2 # 管道(可用 PeekNamedPipe 预询可读量)
GUARD_IN_STREAM: t.CDefine = 3 # 其它流(磁盘文件等:只能有界读)
define GUARD_IN_CONSOLE = 1 # 控制台(CHAR + GetConsoleMode 成功)
define GUARD_IN_PIPE = 2 # 管道(可用 PeekNamedPipe 预询可读量)
define GUARD_IN_STREAM = 3 # 其它流(磁盘文件等:只能有界读)
# 清位掩码:ENABLE_LINE_INPUT(0x0002)|ENABLE_ECHO_INPUT(0x0004) 的补(wincon.h)
GUARD_RAW_CLEAR_MASK: t.CDefine = 0xFFFFFFF9
define GUARD_RAW_CLEAR_MASK = 0xFFFFFFF9
# INPUT_RECORD 内 KEY_EVENT_RECORD 字段偏移(x64 布局推导见 VET/README.md「控制台记录的读取口径」)
GUARD_KEY_DOWN_OFF: t.CDefine = 4 # KeyEvent.bKeyDown (BOOL)
GUARD_KEY_CHAR_OFF: t.CDefine = 14 # KeyEvent.uChar (WCHAR/CHAR union 头 2 字节)
GUARD_REC_SIZE: t.CDefine = 20 # sizeof(INPUT_RECORD)=EventType(2)+pad(2)+union(16)
GUARD_REC_MAX: t.CDefine = 16 # 单次 ReadConsoleInputA 最多读的记录数
GUARD_REC_CAP: t.CDefine = 320 # 记录缓冲字节数(20*16)
GUARD_CHUNK_CAP: t.CDefine = 64 # 管道/文件单次 ReadFile 上限(字节)
GUARD_POLL_MS: t.CDefine = 20 # 无可读数据时的推进节拍(同 VScan.PACE_SLEEP_MS)
define GUARD_KEY_DOWN_OFF = 4 # KeyEvent.bKeyDown (BOOL)
define GUARD_KEY_CHAR_OFF = 14 # KeyEvent.uChar (WCHAR/CHAR union 头 2 字节)
define GUARD_REC_SIZE = 20 # sizeof(INPUT_RECORD)=EventType(2)+pad(2)+union(16)
define GUARD_REC_MAX = 16 # 单次 ReadConsoleInputA 最多读的记录数
define GUARD_REC_CAP = 320 # 记录缓冲字节数(20*16)
define GUARD_CHUNK_CAP = 64 # 管道/文件单次 ReadFile 上限(字节)
define GUARD_POLL_MS = 20 # 无可读数据时的推进节拍(同 VScan.PACE_SLEEP_MS)
# 取数结果码(两路共用;-1 为"读取失败")
GUARD_PUMP_EOF: t.CDefine = -2 # 不可能再出现输入(ReadFile 读到 0 字节)
define GUARD_PUMP_EOF = -2 # 不可能再出现输入(ReadFile 读到 0 字节)
# ---- 快修:未消费输入 FIFO(两路取数共用;见模块头「快修:批量输入与可疑行」)----
# 容量的推导(常量实际值取自本文件上方声明):
@@ -267,34 +267,34 @@ GUARD_PUMP_EOF: t.CDefine = -2 # 不可能再出现输
# 不变量(改动任一常量时须保持):GUARD_PEND_CAP >= GUARD_CHUNK_CAP,且 >= GUARD_REC_MAX
# (否则单批读入量放不进 FIFO;GuardPumpStream/GuardPumpConsole 里的两处夹取是该不变量的
# 显式执行,GuardPendPut 的边界守卫是最后一道防线)。
GUARD_PEND_CAP: t.CDefine = 128 # 未消费输入 FIFO 容量(字节)
define GUARD_PEND_CAP = 128 # 未消费输入 FIFO 容量(字节)
# 可疑行回显的截断长度(stderr 只回显前缀,避免把超长输入整行打出去)
GUARD_SUSPECT_ECHO_MAX: t.CDefine = 120
define GUARD_SUSPECT_ECHO_MAX = 120
# 行分类结果码(GuardClassifyLine 返回值,见模块头「快修:批量输入与可疑行」)
GUARD_LINE_SUSPECT: t.CDefine = 0 # 形状可疑 ⇒ 忽略该行并继续等(期限不变)
GUARD_LINE_OK: t.CDefine = 1 # 合法 {"ok": 1} ⇒ 本行确认成功
GUARD_LINE_BADOK: t.CDefine = 2 # 形状合法但值不对 ⇒ 致命失败(口径不放宽)
define GUARD_LINE_SUSPECT = 0 # 形状可疑 ⇒ 忽略该行并继续等(期限不变)
define GUARD_LINE_OK = 1 # 合法 {"ok": 1} ⇒ 本行确认成功
define GUARD_LINE_BADOK = 2 # 形状合法但值不对 ⇒ 致命失败(口径不放宽)
# 形状检查的长度上界:合法 {"ok": 1} 正常不超过 16 字节(去掉空格/制表符后更短),
# 取 256 留足余量(可容忍 "{ \"ok\" : 1 }" 之类的空白展开),超过即按"被撕裂的残行/噪声"处理。
GUARD_OK_LINE_MAX: t.CDefine = 256
define GUARD_OK_LINE_MAX = 256
# ---- check 请求通道(看门狗 → 父进程的 IPC 管道;依附 --need-confirm)----
# 行缓冲上限与看门狗侧 GUARD_LINE_CAP 同值(请求行由看门狗逐字节转发,长度不会超过它)
IPC_LINE_CAP: t.CDefine = 4096
IPC_READ_CHUNK: t.CDefine = 1024 # 单次 ReadFile 上限(只读"已确认可用"的部分)
IPC_PIPE_SIZE: t.CDefine = 65536 # CreatePipe 的 nSize 提示(默认过小,给足以免看门狗写阻塞)
define IPC_LINE_CAP = 4096
define IPC_READ_CHUNK = 1024 # 单次 ReadFile 上限(只读"已确认可用"的部分)
define IPC_PIPE_SIZE = 65536 # CreatePipe 的 nSize 提示(默认过小,给足以免看门狗写阻塞)
# ---- 加固 1:ReadConsoleInputExA 动态解析(无导入库,只能 GetProcAddress)----
CONSOLE_READ_NOWAIT: t.CDefine = 0x0002 # 输入缓冲为空也立即返回(文档 wFlags)
define CONSOLE_READ_NOWAIT = 0x0002 # 输入缓冲为空也立即返回(文档 wFlags)
# ReadConsoleInputExA 的解析状态(Guard_ReadExState;0 = 未尝试解析)
GUARD_EX_OK: t.CDefine = 1 # 已解析到函数地址(走主路径)
GUARD_EX_MISSING: t.CDefine = 2 # 该导出不存在(走降级路径,不再重试)
define GUARD_EX_OK = 1 # 已解析到函数地址(走主路径)
define GUARD_EX_MISSING = 2 # 该导出不存在(走降级路径,不再重试)
# 降级路径"等可读"的单次上限(毫秒):把这一步的等待收紧,避免一次等待吃掉整个剩余预算;
# 取 5 是"够等一次输入到达"与"不显著拖慢判定"之间的折中(其后还有 GUARD_POLL_MS 节拍)。
GUARD_LEGACY_WAIT_MS: t.CDefine = 5
define GUARD_LEGACY_WAIT_MS = 5
# SetConsoleCtrlHandler 的 Add 参数(consoleapi.h 的 TRUE/FALSE 同名,此处用字面量以免混淆)
GUARD_CTRL_ADD: t.CDefine = 1
GUARD_CTRL_REMOVE: t.CDefine = 0
define GUARD_CTRL_ADD = 1
define GUARD_CTRL_REMOVE = 0
# Win32 自声明(本仓 includes/w32 无 PeekNamedPipe):
# kernel32!PeekNamedPipe: BOOL PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer,
@@ -303,7 +303,7 @@ GUARD_CTRL_REMOVE: t.CDefine = 0
# 原型出处:namedpipeapi.h / Kernel32.lib / kernel32.dll(project.vpj 已链接 kernel32)。
# 语义:句柄必须是管道读端;不取走数据、立即返回;lpTotalBytesAvail = 当前可读字节数。
extern def PeekNamedPipe(hNamedPipe: wb.HANDLE, lpBuffer: t.CPtr, nBufferSize: ULONG,
lpBytesRead: t.CPtr, lpTotalBytesAvail: ULONG | t.CPtr,
lpBytesRead: t.CPtr, lpTotalBytesAvail: ULONG*,
lpBytesLeftThisMessage: t.CPtr) -> BOOL
# Win32 自声明(本仓 includes/w32 无 SetConsoleCtrlHandler):
@@ -411,15 +411,15 @@ def GuardIpcCreate() -> wb.HANDLE:
if Ipc_Line is None or Ipc_Chunk is None:
return None
Sa: wb.SECURITY_ATTRIBUTES = wb.SECURITY_ATTRIBUTES()
Sa.nLength = t.CUInt32T(wb.SECURITY_ATTRIBUTES.__sizeof__())
Sa.nLength = uint32(wb.SECURITY_ATTRIBUTES.__sizeof__())
Sa.lpSecurityDescriptor = t.CPtr(0)
Sa.bInheritHandle = 1 # 两端先都建为可继承,随后单方面清读端
hRead: wb.HANDLE = None
hWrite: wb.HANDLE = None
if wf.CreatePipe(t.CPtr(c.Addr(hRead)), t.CPtr(c.Addr(hWrite)),
t.CPtr(c.Addr(Sa)), ULONG(IPC_PIPE_SIZE)) == 0:
if wf.CreatePipe(t.CPtr(&hRead), t.CPtr(&hWrite),
t.CPtr(&Sa), ULONG(IPC_PIPE_SIZE)) == 0:
VLog.LogFmt1("[confirm] 警告: check 通道 CreatePipe 失败 (GetLastError=%u)\n",
t.CSizeT(wb.GetLastError()))
sizet(wb.GetLastError()))
return None
# 读端只在父进程用:清掉继承位,避免被任何子进程(看门狗、--version 探测)继承
wf.SetHandleInformation(hRead, wf.HANDLE_FLAG_INHERIT, 0)
@@ -448,7 +448,7 @@ def PollIpc() -> int:
Done: int = 0
while True:
Avail: ULONG = 0
if PeekNamedPipe(Ipc_Read, t.CPtr(0), 0, t.CPtr(0), t.CPtr(c.Addr(Avail)), t.CPtr(0)) == 0:
if PeekNamedPipe(Ipc_Read, t.CPtr(0), 0, t.CPtr(0), t.CPtr(&Avail), t.CPtr(0)) == 0:
return Done # 管道异常:本轮不再读(不阻塞、不重试)
if Avail == 0:
return Done # 无数据 ⇒ 立即返回(轮询点的高频路径)
@@ -456,13 +456,13 @@ def PollIpc() -> int:
if Avail < N:
N = Avail # 只读"确已有数据"的部分 ⇒ ReadFile 不会阻塞
Br: ULONG = 0
if wf.ReadFile(Ipc_Read, Ipc_Chunk, N, t.CPtr(c.Addr(Br)), None) == 0:
if wf.ReadFile(Ipc_Read, Ipc_Chunk, N, t.CPtr(&Br), None) == 0:
return Done
if Br == 0:
return Done
ub: BYTEPTR = BYTEPTR(Ipc_Chunk)
i: int = 0
while i < t.CInt(Br):
while i < int(Br):
Ch: int = int(ub[i])
i += 1
if Ch == 10: # '\n':一行结束
@@ -475,7 +475,7 @@ def PollIpc() -> int:
if Ipc_Fill >= IPC_LINE_CAP - 1:
Ipc_Fill = 0 # 超长行:丢弃该行(不越界、不与下一行粘连)
else:
Ipc_Line[Ipc_Fill] = t.CChar(Ch)
Ipc_Line[Ipc_Fill] = char(Ch)
Ipc_Fill += 1
@@ -490,9 +490,9 @@ def SelfExePath(Out: bytes, Cap: int) -> int:
n: ULONG = wf.GetModuleFileNameA(None, Out, ULONG(Cap))
if n == 0:
return 0
if t.CSizeT(n) >= t.CSizeT(Cap):
if sizet(n) >= sizet(Cap):
return 0 # 缓冲不足(路径被截断)⇒ 视为失败
Out[t.CInt(n)] = 0
Out[int(n)] = 0
return 1
@@ -552,11 +552,11 @@ def SpawnWatchdog(IntervalSec: int, TimeoutMs: int) -> int:
Extra: str = ""
if VCli.Verbose() != 0:
Extra = " --verbose"
viperlib.snprintf(Cmd, t.CSizeT(SELF_PATH_CAP + WATCH_CMD_EXTRA_CAP),
viperlib.snprintf(Cmd, sizet(SELF_PATH_CAP + WATCH_CMD_EXTRA_CAP),
"\"%s\" %s --interval %u --timeout %u --parent-pid %u --ipc-write %llu%s",
str(Self), CONFIRM_WATCH_CMD,
t.CSizeT(IntervalSec), t.CSizeT(TimeoutMs), t.CSizeT(Pid),
t.CUInt64T(IpcW), Extra)
sizet(IntervalSec), sizet(TimeoutMs), sizet(Pid),
uint64(IpcW), Extra)
# 标准句柄:与父进程同一(继承控制台/重定向);stdin 也一并交给看门狗
hIn: wb.HANDLE = wf.GetStdHandle(wf.STD_INPUT_HANDLE)
@@ -577,7 +577,7 @@ def SpawnWatchdog(IntervalSec: int, TimeoutMs: int) -> int:
wf.SetHandleInformation(hErr, wf.HANDLE_FLAG_INHERIT, wf.HANDLE_FLAG_INHERIT)
Si: wp.STARTUPINFOA = wp.STARTUPINFOA()
Si.cb = t.CUInt32T(wp.STARTUPINFOA.__sizeof__())
Si.cb = uint32(wp.STARTUPINFOA.__sizeof__())
Si.dwFlags = wp.STARTF_USESTDHANDLES
Si.hStdInput = hIn
Si.hStdOutput = hOut
@@ -585,7 +585,7 @@ def SpawnWatchdog(IntervalSec: int, TimeoutMs: int) -> int:
Pi: wp.PROCESS_INFORMATION = wp.PROCESS_INFORMATION()
Ok: BOOL = wp.CreateProcessA(t.CPtr(0), Cmd, t.CPtr(0), t.CPtr(0), 1, 0,
t.CPtr(0), t.CPtr(0), t.CPtr(c.Addr(Si)), t.CPtr(c.Addr(Pi)))
t.CPtr(0), t.CPtr(0), t.CPtr(&Si), t.CPtr(&Pi))
CreateErr: ULONG = wb.GetLastError() # 紧跟调用读取(free 会覆盖 GetLastError)
_mbuddy.free(t.CPtr(Self))
_mbuddy.free(t.CPtr(Cmd))
@@ -593,7 +593,7 @@ def SpawnWatchdog(IntervalSec: int, TimeoutMs: int) -> int:
wb.CloseHandle(IpcW)
GuardIpcClose()
VLog.LogFmt1("[confirm] 警告: 看门狗 CreateProcessA 失败 (GetLastError=%u),未启动\n",
t.CSizeT(CreateErr))
sizet(CreateErr))
return 0
# 写端已由看门狗继承 ⇒ 父进程立即关掉自己的副本:这样后续任何 CreateProcessA
@@ -607,9 +607,9 @@ def SpawnWatchdog(IntervalSec: int, TimeoutMs: int) -> int:
Wp: ULONG = wsync.WaitForSingleObject(Watch_Process, WATCH_ALIVE_PROBE_MS)
if Wp == wb.WAIT_OBJECT_0:
Ec: ULONG = 0
wp.GetExitCodeProcess(Watch_Process, t.CPtr(c.Addr(Ec)))
wp.GetExitCodeProcess(Watch_Process, t.CPtr(&Ec))
VLog.LogFmt1("[confirm] 看门狗拉起后立即退出 (exit=%u),心跳确认无法生效\n",
t.CSizeT(Ec))
sizet(Ec))
# 致命路径与 StopWatchdog 一样各自 CloseHandle 一次,并清空模块级句柄
# (避免随后 StopWatchdog/再次 Spawn 造成双重关闭)。
wb.CloseHandle(Watch_Process)
@@ -623,8 +623,8 @@ def SpawnWatchdog(IntervalSec: int, TimeoutMs: int) -> int:
VLog.StdoutWrite("{\"exit\":1, \"msg\": \"timeout/ok!=1\"}\n")
return 0
VLog.LogFmt2("[confirm] 心跳看门狗已启动 (pid=%u, 每 %u 秒一次",
t.CSizeT(Pi.dwProcessId), t.CSizeT(IntervalSec))
VLog.LogFmt1(", 确认超时 %u ms)\n", t.CSizeT(TimeoutMs))
sizet(Pi.dwProcessId), sizet(IntervalSec))
VLog.LogFmt1(", 确认超时 %u ms)\n", sizet(TimeoutMs))
return 1
@@ -740,10 +740,10 @@ def GuardForwardLine():
bw: ULONG = 0
if Guard_Fill > 0:
if wf.WriteFile(Guard_IpcWrite, Guard_Line, ULONG(Guard_Fill),
t.CPtr(c.Addr(bw)), None) == 0:
t.CPtr(&bw), None) == 0:
VLog.LogStr("[confirm] 警告: check 请求转发失败(写管道失败),已丢弃该行\n")
return
if wf.WriteFile(Guard_IpcWrite, "\n", 1, t.CPtr(c.Addr(bw)), None) == 0:
if wf.WriteFile(Guard_IpcWrite, "\n", 1, t.CPtr(&bw), None) == 0:
VLog.LogStr("[confirm] 警告: check 请求转发失败(写行分隔符失败),该行可能不完整\n")
@@ -778,7 +778,7 @@ def GuardFeedByte(Ch: int) -> int:
if Guard_Fill >= GUARD_LINE_CAP - 1:
Guard_Line[Guard_Fill] = 0
return 1
Guard_Line[Guard_Fill] = t.CChar(Ch)
Guard_Line[Guard_Fill] = char(Ch)
Guard_Fill += 1
Guard_Line[Guard_Fill] = 0
return 0
@@ -816,9 +816,9 @@ def GuardPendPut(Ch: int):
global Guard_PendLen
if Guard_PendLen >= GUARD_PEND_CAP:
VLog.LogFmt1("[confirm] 警告: 未消费输入 FIFO 已满(容量 %u),丢弃该批字节\n",
t.CSizeT(GUARD_PEND_CAP))
sizet(GUARD_PEND_CAP))
return
Guard_Pend[Guard_PendLen] = t.CChar(Ch)
Guard_Pend[Guard_PendLen] = char(Ch)
Guard_PendLen += 1
@@ -829,7 +829,7 @@ def GuardInputKind(hIn: wb.HANDLE) -> int:
T: ULONG = wf.GetFileType(hIn)
if T == ULONG(FILE_TYPE_CHAR):
Mode: ULONG = 0
if wc.GetConsoleMode(hIn, t.CPtr(c.Addr(Mode))) != 0:
if wc.GetConsoleMode(hIn, t.CPtr(&Mode)) != 0:
return GUARD_IN_CONSOLE
return GUARD_IN_STREAM
if T == ULONG(FILE_TYPE_PIPE):
@@ -908,7 +908,7 @@ def GuardRemoveCtrlHandler():
def GuardEnterRaw(hIn: wb.HANDLE) -> int:
global Guard_SavedMode, Guard_RawActive, Guard_ConHandle, Guard_CtrlHandlerOn
Mode: ULONG = 0
if wc.GetConsoleMode(hIn, t.CPtr(c.Addr(Mode))) == 0:
if wc.GetConsoleMode(hIn, t.CPtr(&Mode)) == 0:
return 0
Guard_SavedMode = Mode
Guard_RawActive = 0
@@ -970,7 +970,7 @@ def GuardPumpConsole(hIn: wb.HANDLE, Deadline: ULONGLONG) -> int:
# Guard_ReadEx 为模块级 t.Callable,其返回类型签名已随模块级 AnnAssign 登记
# (TranslatorGenMixin.handle_module_level_var),故可直接间接调用。
if Guard_ReadEx(hIn, t.CPtr(Guard_Recs), ULONG(GUARD_REC_MAX),
t.CPtr(c.Addr(Read)), USHORT(CONSOLE_READ_NOWAIT)) == 0:
t.CPtr(&Read), USHORT(CONSOLE_READ_NOWAIT)) == 0:
return -1
else:
Rem: ULONG = GuardRemainMs(Deadline)
@@ -981,25 +981,25 @@ def GuardPumpConsole(hIn: wb.HANDLE, Deadline: ULONGLONG) -> int:
if wsync.WaitForSingleObject(hIn, Rem) == ULONG(wb.WAIT_FAILED):
return -1
Cnt: ULONG = 0
if wc.GetNumberOfConsoleInputEvents(hIn, t.CPtr(c.Addr(Cnt))) == 0:
if wc.GetNumberOfConsoleInputEvents(hIn, t.CPtr(&Cnt)) == 0:
return -1
if Cnt == 0:
return 0
N: ULONG = Cnt
if N > ULONG(GUARD_REC_MAX):
N = ULONG(GUARD_REC_MAX)
if wc.ReadConsoleInputA(hIn, t.CPtr(Guard_Recs), N, t.CPtr(c.Addr(Read))) == 0:
if wc.ReadConsoleInputA(hIn, t.CPtr(Guard_Recs), N, t.CPtr(&Read)) == 0:
return -1
ub: BYTEPTR = BYTEPTR(Guard_Recs)
i: int = 0
while i < t.CInt(Read):
while i < int(Read):
Base: int = i * GUARD_REC_SIZE
i += 1
if VPe.ReadU16(ub, Base) != t.CUInt32T(wc.KEY_EVENT):
if VPe.ReadU16(ub, Base) != uint32(wc.KEY_EVENT):
continue
if VPe.ReadU32(ub, Base + GUARD_KEY_DOWN_OFF) == 0:
continue
Ch: int = t.CInt(VPe.ReadU16(ub, Base + GUARD_KEY_CHAR_OFF))
Ch: int = int(VPe.ReadU16(ub, Base + GUARD_KEY_CHAR_OFF))
if Ch == 13:
Ch = 10 # Enter → 行结束(与 '\n' 同一口径)
elif Ch != 9 and (Ch < 32 or Ch > 126):
@@ -1029,7 +1029,7 @@ def GuardPumpStream(hIn: wb.HANDLE, Kind: int) -> int:
N: ULONG = ULONG(GUARD_CHUNK_CAP)
if Kind == GUARD_IN_PIPE:
Avail: ULONG = 0
if PeekNamedPipe(hIn, t.CPtr(0), 0, t.CPtr(0), t.CPtr(c.Addr(Avail)), t.CPtr(0)) == 0:
if PeekNamedPipe(hIn, t.CPtr(0), 0, t.CPtr(0), t.CPtr(&Avail), t.CPtr(0)) == 0:
return -1
if Avail == 0:
return 0
@@ -1044,13 +1044,13 @@ def GuardPumpStream(hIn: wb.HANDLE, Kind: int) -> int:
if N > ULONG(GUARD_PEND_CAP):
N = ULONG(GUARD_PEND_CAP)
Br: ULONG = 0
if wf.ReadFile(hIn, Guard_Chunk, N, t.CPtr(c.Addr(Br)), None) == 0:
if wf.ReadFile(hIn, Guard_Chunk, N, t.CPtr(&Br), None) == 0:
return -1
if Br == 0:
return GUARD_PUMP_EOF
ub: BYTEPTR = BYTEPTR(Guard_Chunk)
i: int = 0
while i < t.CInt(Br):
while i < int(Br):
GuardPendPut(int(ub[i])) # ③ 先整批入队(Br <= N <= GUARD_PEND_CAP)
i += 1
if GuardFeedPending() != 0: # ③ 再喂 FIFO;剩余字节留给下次调用
@@ -1127,7 +1127,7 @@ def GuardWaitConfirm(hIn: wb.HANDLE, hParent: wb.HANDLE, Kind: int, TimeoutMs: i
if GuardEnterRaw(hIn) == 0:
GuardWatchFail(hParent, "控制台模式读取/切换失败")
return 0
Deadline: ULONGLONG = wb.GetTickCount64() + ULONGLONG(t.CUInt32T(TimeoutMs))
Deadline: ULONGLONG = wb.GetTickCount64() + ULONGLONG(uint32(TimeoutMs))
while True:
# ② 先取数(GuardPump* 内部先喂 FIFO):FIFO 里已有整行时不会白等一次 20ms
R: int = 0
@@ -1183,7 +1183,7 @@ def GuardWaitConfirm(hIn: wb.HANDLE, hParent: wb.HANDLE, Kind: int, TimeoutMs: i
Hs.H0 = hIn
Hs.H1 = hParent
Wm: ULONG = wsync.WaitForMultipleObjects(ULONG(WATCH_HANDLE_COUNT),
t.CPtr(c.Addr(Hs)), 0, Sl)
t.CPtr(&Hs), 0, Sl)
if Wm == ULONG(wb.WAIT_OBJECT_0) + ULONG(1): # 父进程句柄(索引 1)置信号
GuardLeaveRaw(hIn)
return 0
@@ -1213,14 +1213,14 @@ def RunConfirmWatch() -> int:
return 1
VLog.LogFmt3("[confirm] 看门狗启动: 每 %u 秒要求一次 {\"ok\": 1},超时 %u ms,父进程 pid=%u\n",
t.CSizeT(IntervalSec), t.CSizeT(TimeoutMs), t.CSizeT(ParentPid))
sizet(IntervalSec), sizet(TimeoutMs), sizet(ParentPid))
# 父进程句柄:同时用于"父退出检测(SYNCHRONIZE)"与"失败时终止父进程(PROCESS_TERMINATE)"
hParent: wb.HANDLE = wp.OpenProcess(ULONG(WATCH_PARENT_ACCESS), 0, ULONG(ParentPid))
if hParent is None or hParent == wb.INVALID_HANDLE_VALUE:
# 拿不到父进程句柄 ⇒ 既无法监控也无法终止,无法履行契约:只记 stderr 并以 1 退出
VLog.LogFmt1("[confirm] 看门狗: OpenProcess 父进程失败 (GetLastError=%u),自身退出\n",
t.CSizeT(wb.GetLastError()))
sizet(wb.GetLastError()))
return 1
hIn: wb.HANDLE = wf.GetStdHandle(wf.STD_INPUT_HANDLE)
@@ -1237,19 +1237,19 @@ def RunConfirmWatch() -> int:
Ch0: int = 0
if Guard_IpcWrite is not None:
Ch0 = 1
VLog.VerboseFmt1("[confirm] 看门狗: check 通道=%u (1=已配置 0=无)\n", t.CSizeT(Ch0))
VLog.VerboseFmt1("[confirm] 看门狗: check 通道=%u (1=已配置 0=无)\n", sizet(Ch0))
# 输入形态只判一次(句柄在进程生命周期内不变);控制台与管道/重定向的取数路径不同。
Kind: int = GuardInputKind(hIn)
VLog.VerboseFmt1("[confirm] 看门狗: stdin 形态=%u (1=控制台 2=管道 3=其它流)\n",
t.CSizeT(Kind))
sizet(Kind))
# 控制台取数路径(加固 1):主路径=ReadConsoleInputExA+CONSOLE_READ_NOWAIT,
# 降级=GetNumberOfConsoleInputEvents+ReadConsoleInputA(该导出不存在的老系统)。
# 这里提前解析一次(后续判定复用缓存的状态),顺带把实际走哪一路写进 --verbose 日志。
if Kind == GUARD_IN_CONSOLE:
GuardResolveReadEx()
VLog.VerboseFmt1("[confirm] 看门狗: 控制台取数路径=%u (1=ReadConsoleInputExA+NOWAIT 2=降级查询+ReadConsoleInputA)\n",
t.CSizeT(Guard_ReadExState))
sizet(Guard_ReadExState))
while True:
# 阶段 1:等 interval;父进程先退出则立即自退(无需父侧配合,杜绝孤儿)
@@ -1259,15 +1259,15 @@ def RunConfirmWatch() -> int:
wb.CloseHandle(hParent)
return 0
if Ws != wb.WAIT_TIMEOUT:
VLog.LogFmt1("[confirm] 看门狗: 等待父进程失败 (wait=%u)\n", t.CSizeT(Ws))
VLog.LogFmt1("[confirm] 看门狗: 等待父进程失败 (wait=%u)\n", sizet(Ws))
GuardWatchFail(hParent, "等待父进程失败")
return 1
# 阶段 2:stderr 提示(期限自本行起算)+ 在 timeout 内非阻塞等"完整一行且分类通过"
VLog.LogFmt2("[confirm] 请在 %u ms 内输入 {\"ok\": 1}(每 %u 秒一次)\n",
t.CSizeT(TimeoutMs), t.CSizeT(IntervalSec))
sizet(TimeoutMs), sizet(IntervalSec))
VLog.TimeStampNow(Guard_Stamp, GUARD_STAMP_CAP)
VLog.TimeStampAfter(Guard_Dl, GUARD_STAMP_CAP, t.CSizeT(TimeoutMs))
VLog.TimeStampAfter(Guard_Dl, GUARD_STAMP_CAP, sizet(TimeoutMs))
VLog.LogFmtS2("[confirm] 当前 %s, 本轮期限 %s\n", str(Guard_Stamp), str(Guard_Dl))
# 失败路径(期限到点/形状合法但值不对/读失败/EOF)已在 GuardWaitConfirm 内收尾并退出进程;
@@ -1276,4 +1276,4 @@ def RunConfirmWatch() -> int:
VLog.LogStr("[confirm] 看门狗: 父进程已退出,自身退出\n")
wb.CloseHandle(hParent)
return 0
VLog.LogFmt1("[confirm] 确认成功 (ok=1),下一次约 %u 秒后\n", t.CSizeT(IntervalSec))
VLog.LogFmt1("[confirm] 确认成功 (ok=1),下一次约 %u 秒后\n", sizet(IntervalSec))
+11 -11
View File
@@ -17,10 +17,10 @@ import lib.VPe as VPe
# DLL 归属:ole32(project.vpj 已含 -lole32)。
# ============================================================
GUID_SIZE: t.CDefine = 16 # GUID:Data1(4)+Data2(2)+Data3(2)+Data4(8)
GUID_STR_CAP: t.CDefine = 40 # "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + NUL
define GUID_SIZE = 16 # GUID:Data1(4)+Data2(2)+Data3(2)+Data4(8)
define GUID_STR_CAP = 40 # "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + NUL
HRESULT: t.CTypedef = t.CInt32T # Win32: typedef LONG HRESULT(负值才是失败)
HRESULT: t.CTypedef = int32 # Win32: typedef LONG HRESULT(负值才是失败)
extern def CoCreateGuid(pguid: t.CPtr) -> HRESULT
@@ -40,15 +40,15 @@ def NewGuidStr(Out: bytes, Cap: int) -> int:
_mbuddy.free(t.CPtr(GB))
return 0
ub: BYTEPTR = BYTEPTR(GB)
D1: t.CUInt32T = VPe.ReadU32(ub, 0)
D2: t.CUInt32T = VPe.ReadU16(ub, 4)
D3: t.CUInt32T = VPe.ReadU16(ub, 6)
viperlib.snprintf(Out, t.CSizeT(Cap),
D1: uint32 = VPe.ReadU32(ub, 0)
D2: uint32 = VPe.ReadU16(ub, 4)
D3: uint32 = VPe.ReadU16(ub, 6)
viperlib.snprintf(Out, sizet(Cap),
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
D1, D2, D3,
t.CUInt32T(ub[8]), t.CUInt32T(ub[9]),
t.CUInt32T(ub[10]), t.CUInt32T(ub[11]),
t.CUInt32T(ub[12]), t.CUInt32T(ub[13]),
t.CUInt32T(ub[14]), t.CUInt32T(ub[15]))
uint32(ub[8]), uint32(ub[9]),
uint32(ub[10]), uint32(ub[11]),
uint32(ub[12]), uint32(ub[13]),
uint32(ub[14]), uint32(ub[15]))
_mbuddy.free(t.CPtr(GB))
return 1
+21 -21
View File
@@ -51,10 +51,10 @@ import lib.VCache as VCache
# NewMemPool 注释与 Debugger/memhub-lifetime.md)。
# ============================================================
JSON_BUF_CAP: t.CDefine = 1048576 # 一轮 JSON 数组缓冲 1 MiB
JSON_POOL_CAP: t.CDefine = 65536 # 逐条序列化工作池 64 KiB(每条目 reset 复用)
JSON_ENTRY_MAX: t.CDefine = 4096 # 单条上限 = json 库 write 的缺省 cap(4096)
JSON_ENTRY_SLACK: t.CDefine = 512 # 单条固定语法开销上界(实测 77:键/值引号 24 + 键名 40 + 冒号 6 + 逗号 5 + 大括号 2)
define JSON_BUF_CAP = 1048576 # 一轮 JSON 数组缓冲 1 MiB
define JSON_POOL_CAP = 65536 # 逐条序列化工作池 64 KiB(每条目 reset 复用)
define JSON_ENTRY_MAX = 4096 # 单条上限 = json 库 write 的缺省 cap(4096)
define JSON_ENTRY_SLACK = 512 # 单条固定语法开销上界(实测 77:键/值引号 24 + 键名 40 + 冒号 6 + 逗号 5 + 大括号 2)
# 单条序列化在 Json_Pool 上的固定开销上界(BuildEntry 的 AllocOk 预检的一部分)。
# 组成(一次 BuildEntry 的全部池分配;条目级 reset 之后 offset=0):
@@ -67,13 +67,13 @@ JSON_ENTRY_SLACK: t.CDefine = 512 # 单条固定语法开销上界(
# * 余量 512 字节:6 个值副本各带一个结尾 NUL,加条内 20 次池分配按
# MEMHUB_ALIGN=8 向上取整的浪费(每次 ≤7,合计 ≤140)。
# 合计 504 + 46 + 4096 + 512 = 5158。
JSON_ENTRY_POOL_FIXED: t.CDefine = 5158
define JSON_ENTRY_POOL_FIXED = 5158
# 单条里 executable / displayName 的取值长度上界:二者都由 Conv_Buf(VFs.CONV_CAP 字节)
# 拷出(WideCharToMultiByte 以 Cap 封顶并写结尾 NUL,失败时 VFs.CopyRaw 写空串),
# 故每个值 ≤ CONV_CAP-1 字符。写 2047 = VFs.CONV_CAP(2048) - 1 的字面量:模块级
# t.CDefine 不引用跨模块常量,VFs.CONV_CAP 只在函数体内参与算术。
JSON_ENTRY_CONV_MAX: t.CDefine = 2047
define JSON_ENTRY_CONV_MAX = 2047
# 由 App/main.vp 注入
_mbuddy: rawptr[memhub.MemBuddy]
@@ -125,14 +125,14 @@ def JWrite(S: str) -> int:
global Json_Len
if S is None or Json_Buf is None:
return 0
n: t.CSizeT = string.strlen(S)
if t.CSizeT(Json_Len) + n + 1 > t.CSizeT(JSON_BUF_CAP):
n: sizet = string.strlen(S)
if sizet(Json_Len) + n + 1 > sizet(JSON_BUF_CAP):
return 0
i: t.CSizeT = 0
i: sizet = 0
while i < n:
Json_Buf[Json_Len + t.CInt(i)] = S[i]
Json_Buf[Json_Len + int(i)] = S[i]
i += 1
Json_Len += t.CInt(n)
Json_Len += int(n)
Json_Buf[Json_Len] = 0
return 1
@@ -147,12 +147,12 @@ def BuildEntry(Idx: int) -> str:
# 预检在 EmitJson 的条目级 reset 之后执行(池 offset=0),故单次 AllocOk 覆盖整条;
# Need 由固定开销 + 全部值的长度上界构成,是条内池占用(含 20 次分配的 8 字节对齐
# 浪费)的上界,推导见 JSON_ENTRY_POOL_FIXED 与 JSON_ENTRY_CONV_MAX 的注释。
ValNeed: t.CSizeT = t.CSizeT(JSON_ENTRY_CONV_MAX) + t.CSizeT(JSON_ENTRY_CONV_MAX)
ValNeed: sizet = sizet(JSON_ENTRY_CONV_MAX) + sizet(JSON_ENTRY_CONV_MAX)
ValNeed = ValNeed + string.strlen(VCache.CacheId(Idx))
ValNeed = ValNeed + string.strlen(VCache.CacheVer(Idx))
ValNeed = ValNeed + string.strlen(VCache.CacheArch(Idx))
ValNeed = ValNeed + string.strlen(VCache.CacheSrc(Idx)) # source("workspace"/"check")
Need: t.CSizeT = t.CSizeT(JSON_ENTRY_POOL_FIXED) + ValNeed
Need: sizet = sizet(JSON_ENTRY_POOL_FIXED) + ValNeed
if Json_Pool.AllocOk(Need) == 0:
VLog.VerboseFmtIS(" 警告: JSON 工作池不足(本条需 %zu 字节),已跳过: %s\n",
Need, VCache.CacheExe(Idx))
@@ -181,7 +181,7 @@ def BuildEntry(Idx: int) -> str:
# 顺带累计各值长度,供容量守卫使用。
Ok: int = 1
N: int = 0
Sum: t.CSizeT = 0
Sum: sizet = 0
cu: rawptr[json.JsonValue] = Obj.child
while cu != None:
if cu.key == None or cu.vtype != json.JSON_STRING or cu.str_val == None:
@@ -200,9 +200,9 @@ def BuildEntry(Idx: int) -> str:
# 实际输出 = 各值转义长度和 + 固定语法 77(键/值引号 24 + 键名 40 + 冒号 6 + 逗号 5 + 大括号 2)
# ≤ 2*Sum + 77 ≤ 2*Sum + JSON_ENTRY_SLACK(512)
# 故 2*Sum + JSON_ENTRY_SLACK ≤ JSON_ENTRY_MAX ⇒ 实际输出 ≤ 3661 < 4095,含结尾 NUL 也放得下。
if 2 * Sum + t.CSizeT(JSON_ENTRY_SLACK) > t.CSizeT(JSON_ENTRY_MAX):
if 2 * Sum + sizet(JSON_ENTRY_SLACK) > sizet(JSON_ENTRY_MAX):
VLog.VerboseFmtIS(" 警告: JSON 条目过长(超过 %zu 字节),已跳过: %s\n",
t.CSizeT(JSON_ENTRY_MAX), VCache.CacheExe(Idx))
sizet(JSON_ENTRY_MAX), VCache.CacheExe(Idx))
return None
Txt: str = json.write(Json_Pool, Obj, False)
@@ -232,11 +232,11 @@ def EmitJson() -> int:
if Txt is None:
Skipped += 1
else:
n: t.CSizeT = string.strlen(Txt)
Need: t.CSizeT = n + 3 # 自身 + 结尾 ']' + '\n' + NUL
n: sizet = string.strlen(Txt)
Need: sizet = n + 3 # 自身 + 结尾 ']' + '\n' + NUL
if Count > 0:
Need = Need + 1 # 分隔逗号
if t.CSizeT(Json_Len) + Need > t.CSizeT(JSON_BUF_CAP):
if sizet(Json_Len) + Need > sizet(JSON_BUF_CAP):
# 缓冲不足:跳过该条(不再像旧实现那样写出半条),末尾仍闭合 ']'
Truncated = 1
else:
@@ -251,8 +251,8 @@ def EmitJson() -> int:
Truncated = 1
if Truncated != 0:
VLog.LogFmt2(" 警告: JSON 输出缓冲不足,本轮已截断(已写 %zu 条 / 缓存 %zu 条)\n",
t.CSizeT(Count), t.CSizeT(VCache.CacheCount()))
sizet(Count), sizet(VCache.CacheCount()))
if Skipped != 0:
VLog.LogFmt1(" 警告: 本轮有 %zu 条 JSON 条目因过长/工作池不足被跳过\n",
t.CSizeT(Skipped))
sizet(Skipped))
return 0
+33 -33
View File
@@ -31,7 +31,7 @@ import w32.win32base as wb
# viperlib.snprintf(includes/viperlib.vp:25)、string.strlen(includes/string.vp:69)。
# ============================================================
LOG_CAP: t.CDefine = 4096 # 日志格式化缓冲
define LOG_CAP = 4096 # 日志格式化缓冲
Log_Buffer: bytes = None # 日志格式化缓冲(stdlib.malloc,进程生命周期)
@@ -43,11 +43,11 @@ def LogStr(S: str):
H: wb.HANDLE = wf.GetStdHandle(wf.STD_ERROR_HANDLE)
if H is None or H == wb.INVALID_HANDLE_VALUE:
return
n: t.CSizeT = string.strlen(S)
n: sizet = string.strlen(S)
if n == 0:
return
bw: ULONG = 0
wf.WriteFile(H, S, ULONG(n), t.CPtr(c.Addr(bw)), None)
wf.WriteFile(H, S, ULONG(n), t.CPtr(&bw), None)
def LogInit() -> int:
@@ -61,35 +61,35 @@ def LogInit() -> int:
# 固定元数的格式化转发(非 varargs 函数 ⇒ 不引入自定义可变参数风险):
# 内部调用既有的 viperlib.snprintf,把格式化结果写 stderr。
def LogFmt1(Fmt: str, A: t.CSizeT) -> int:
def LogFmt1(Fmt: str, A: sizet) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, t.CSizeT(LOG_CAP), Fmt, A)
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A)
LogStr(str(Log_Buffer))
return 1
def LogFmt2(Fmt: str, A: t.CSizeT, B: t.CSizeT) -> int:
def LogFmt2(Fmt: str, A: sizet, B: sizet) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, t.CSizeT(LOG_CAP), Fmt, A, B)
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B)
LogStr(str(Log_Buffer))
return 1
def LogFmt3(Fmt: str, A: t.CSizeT, B: t.CSizeT, C: t.CSizeT) -> int:
def LogFmt3(Fmt: str, A: sizet, B: sizet, C: sizet) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, t.CSizeT(LOG_CAP), Fmt, A, B, C)
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B, C)
LogStr(str(Log_Buffer))
return 1
# 需求 3:扫盘进度行需要 4 个整数(r/dir/file/cand)
def LogFmt4(Fmt: str, A: t.CSizeT, B: t.CSizeT, C: t.CSizeT, D: t.CSizeT) -> int:
def LogFmt4(Fmt: str, A: sizet, B: sizet, C: sizet, D: sizet) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, t.CSizeT(LOG_CAP), Fmt, A, B, C, D)
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B, C, D)
LogStr(str(Log_Buffer))
return 1
@@ -97,7 +97,7 @@ def LogFmt4(Fmt: str, A: t.CSizeT, B: t.CSizeT, C: t.CSizeT, D: t.CSizeT) -> int
def LogFmtS1(Fmt: str, A: str) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, t.CSizeT(LOG_CAP), Fmt, A)
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A)
LogStr(str(Log_Buffer))
return 1
@@ -105,15 +105,15 @@ def LogFmtS1(Fmt: str, A: str) -> int:
def LogFmtS2(Fmt: str, A: str, B: str) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, t.CSizeT(LOG_CAP), Fmt, A, B)
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B)
LogStr(str(Log_Buffer))
return 1
def LogFmtIS(Fmt: str, A: t.CSizeT, B: str) -> int:
def LogFmtIS(Fmt: str, A: sizet, B: str) -> int:
if Log_Buffer is None:
return 0
viperlib.snprintf(Log_Buffer, t.CSizeT(LOG_CAP), Fmt, A, B)
viperlib.snprintf(Log_Buffer, sizet(LOG_CAP), Fmt, A, B)
LogStr(str(Log_Buffer))
return 1
@@ -144,28 +144,28 @@ def VerboseStr(S: str) -> int:
return 1
def VerboseFmt1(Fmt: str, A: t.CSizeT) -> int:
def VerboseFmt1(Fmt: str, A: sizet) -> int:
if Verbose_Enabled == 0:
return 0
LogFmt1(Fmt, A)
return 1
def VerboseFmt2(Fmt: str, A: t.CSizeT, B: t.CSizeT) -> int:
def VerboseFmt2(Fmt: str, A: sizet, B: sizet) -> int:
if Verbose_Enabled == 0:
return 0
LogFmt2(Fmt, A, B)
return 1
def VerboseFmt3(Fmt: str, A: t.CSizeT, B: t.CSizeT, C: t.CSizeT) -> int:
def VerboseFmt3(Fmt: str, A: sizet, B: sizet, C: sizet) -> int:
if Verbose_Enabled == 0:
return 0
LogFmt3(Fmt, A, B, C)
return 1
def VerboseFmt4(Fmt: str, A: t.CSizeT, B: t.CSizeT, C: t.CSizeT, D: t.CSizeT) -> int:
def VerboseFmt4(Fmt: str, A: sizet, B: sizet, C: sizet, D: sizet) -> int:
if Verbose_Enabled == 0:
return 0
LogFmt4(Fmt, A, B, C, D)
@@ -186,7 +186,7 @@ def VerboseFmtS2(Fmt: str, A: str, B: str) -> int:
return 1
def VerboseFmtIS(Fmt: str, A: t.CSizeT, B: str) -> int:
def VerboseFmtIS(Fmt: str, A: sizet, B: str) -> int:
if Verbose_Enabled == 0:
return 0
LogFmtIS(Fmt, A, B)
@@ -200,11 +200,11 @@ def StdoutWrite(S: str):
H: wb.HANDLE = wf.GetStdHandle(wf.STD_OUTPUT_HANDLE)
if H is None or H == wb.INVALID_HANDLE_VALUE:
return
n: t.CSizeT = string.strlen(S)
n: sizet = string.strlen(S)
if n == 0:
return
bw: ULONG = 0
wf.WriteFile(H, S, ULONG(n), t.CPtr(c.Addr(bw)), None)
wf.WriteFile(H, S, ULONG(n), t.CPtr(&bw), None)
# ============================================================
@@ -216,27 +216,27 @@ def StdoutWrite(S: str):
def TimeStampNow(Out: bytes, Cap: int) -> int:
St: wb.SYSTEMTIME = wb.SYSTEMTIME()
wb.GetLocalTime(t.CPtr(c.Addr(St)))
viperlib.snprintf(Out, t.CSizeT(Cap), "%04u-%02u-%02u %02u:%02u:%02u",
t.CUInt32T(St.wYear), t.CUInt32T(St.wMonth), t.CUInt32T(St.wDay),
t.CUInt32T(St.wHour), t.CUInt32T(St.wMinute), t.CUInt32T(St.wSecond))
wb.GetLocalTime(t.CPtr(&St))
viperlib.snprintf(Out, sizet(Cap), "%04u-%02u-%02u %02u:%02u:%02u",
uint32(St.wYear), uint32(St.wMonth), uint32(St.wDay),
uint32(St.wHour), uint32(St.wMinute), uint32(St.wSecond))
return 1
def TimeStampAfter(Out: bytes, Cap: int, AddMs: t.CSizeT) -> int:
def TimeStampAfter(Out: bytes, Cap: int, AddMs: sizet) -> int:
St: wb.SYSTEMTIME = wb.SYSTEMTIME()
wb.GetLocalTime(t.CPtr(c.Addr(St)))
wb.GetLocalTime(t.CPtr(&St))
Ft: wb.FILETIME = wb.FILETIME()
if wb.SystemTimeToFileTime(t.CPtr(c.Addr(St)), t.CPtr(c.Addr(Ft))) == 0:
if wb.SystemTimeToFileTime(t.CPtr(&St), t.CPtr(&Ft)) == 0:
return TimeStampNow(Out, Cap)
Ticks: ULONGLONG = (ULONGLONG(Ft.dwHighDateTime) << 32) | ULONGLONG(Ft.dwLowDateTime)
Ticks = Ticks + (ULONGLONG(AddMs) * ULONGLONG(10000))
Ft.dwLowDateTime = DWORD(Ticks & ULONGLONG(0xFFFFFFFF))
Ft.dwHighDateTime = DWORD(Ticks >> 32)
St2: wb.SYSTEMTIME = wb.SYSTEMTIME()
if wb.FileTimeToSystemTime(t.CPtr(c.Addr(Ft)), t.CPtr(c.Addr(St2))) == 0:
if wb.FileTimeToSystemTime(t.CPtr(&Ft), t.CPtr(&St2)) == 0:
return TimeStampNow(Out, Cap)
viperlib.snprintf(Out, t.CSizeT(Cap), "%04u-%02u-%02u %02u:%02u:%02u",
t.CUInt32T(St2.wYear), t.CUInt32T(St2.wMonth), t.CUInt32T(St2.wDay),
t.CUInt32T(St2.wHour), t.CUInt32T(St2.wMinute), t.CUInt32T(St2.wSecond))
viperlib.snprintf(Out, sizet(Cap), "%04u-%02u-%02u %02u:%02u:%02u",
uint32(St2.wYear), uint32(St2.wMonth), uint32(St2.wDay),
uint32(St2.wHour), uint32(St2.wMinute), uint32(St2.wSecond))
return 1
+101 -101
View File
@@ -28,40 +28,40 @@ import w32.win32base as wb
# ============================================================
# PE/COFF 常量
DOS_SIGNATURE: t.CDefine = 0x5A4D # "MZ" 小端
NT_SIGNATURE: t.CDefine = 0x00004550 # "PE\0\0" 小端
OPT_MAGIC_PE32: t.CDefine = 0x10B
OPT_MAGIC_PE32P: t.CDefine = 0x20B
SEC_ENTRY_SIZE: t.CDefine = 40
define DOS_SIGNATURE = 0x5A4D # "MZ" 小端
define NT_SIGNATURE = 0x00004550 # "PE\0\0" 小端
define OPT_MAGIC_PE32 = 0x10B
define OPT_MAGIC_PE32P = 0x20B
define SEC_ENTRY_SIZE = 40
E_LFANEW_OFF: t.CDefine = 0x3C # DOS 头 e_lfanew
COFF_MACHINE_OFF: t.CDefine = 4 # PE签名(4)+COFF: Machine @ COFF+0
COFF_NUMSEC_OFF: t.CDefine = 6 # PE签名(4)+COFF: NumberOfSections @ COFF+2
COFF_OPTSIZE_OFF: t.CDefine = 20 # PE签名(4)+COFF: SizeOfOptionalHeader @ COFF+16
OPT_MAGIC_OFF: t.CDefine = 24 # PE签名(4)+COFF(20) 之后为可选头 Magic
define E_LFANEW_OFF = 0x3C # DOS 头 e_lfanew
define COFF_MACHINE_OFF = 4 # PE签名(4)+COFF: Machine @ COFF+0
define COFF_NUMSEC_OFF = 6 # PE签名(4)+COFF: NumberOfSections @ COFF+2
define COFF_OPTSIZE_OFF = 20 # PE签名(4)+COFF: SizeOfOptionalHeader @ COFF+16
define OPT_MAGIC_OFF = 24 # PE签名(4)+COFF(20) 之后为可选头 Magic
SEC_NAME_OFF: t.CDefine = 0
SEC_RAWSIZE_OFF: t.CDefine = 16
SEC_RAWPTR_OFF: t.CDefine = 20
define SEC_NAME_OFF = 0
define SEC_RAWSIZE_OFF = 16
define SEC_RAWPTR_OFF = 20
VERSION_SECTION: t.CDefine = ".PRVERSN"
define VERSION_SECTION = ".PRVERSN"
# COFF Machine 值(winnt.h)→ arch 字符串
MACHINE_X86_64: t.CDefine = 0x8664
MACHINE_X86: t.CDefine = 0x14C
MACHINE_ARM64: t.CDefine = 0xAA64
MACHINE_ARM: t.CDefine = 0x1C0
MACHINE_ARMNT: t.CDefine = 0x1C4
define MACHINE_X86_64 = 0x8664
define MACHINE_X86 = 0x14C
define MACHINE_ARM64 = 0xAA64
define MACHINE_ARM = 0x1C0
define MACHINE_ARMNT = 0x1C4
# 扫盘用 PE 读取上限
PE_HEAD_READ: t.CDefine = 8192 # 单次读取的"头部窗口"(DOS+COFF+可选头+节表)
define PE_HEAD_READ = 8192 # 单次读取的"头部窗口"(DOS+COFF+可选头+节表)
VER_CAP: t.CDefine = 128 # .PRVERSN 版本串缓冲
ARCH_CAP: t.CDefine = 32 # arch 字符串缓冲("unknown(0xFFFF)" 14 字节)
define VER_CAP = 128 # .PRVERSN 版本串缓冲
define ARCH_CAP = 32 # arch 字符串缓冲("unknown(0xFFFF)" 14 字节)
# ParsePeHead 的输出(用模块全局而非数组出参)
Pe_RawPtr: t.CUInt32T = 0
Pe_RawSize: t.CUInt32T = 0
Pe_RawPtr: uint32 = 0
Pe_RawSize: uint32 = 0
# TryReadPeInfo 打开失败(rc=1)时的 GetLastError(check 通道据此区分 NOT_FOUND / NO_PERMISSION)。
# 生命周期:TryReadPeInfo 每次进入复位为 0;仅在 CreateFileA 返回 INVALID_HANDLE_VALUE 时写入,
@@ -74,11 +74,11 @@ Pe_OpenErr: ULONG = 0
# Pe_DbgLastRc :最近一次解析失败码(0=成功;scan 路径为 ParsePeHead 的 prc,
# ver 路径为 Phase1VersionSection 按同一条件编码的等价码)
# 生命周期:TryReadPeInfo 每次进入复位;Phase1VersionSection 每次进入复位。
Pe_DbgLfanew: t.CUInt32T = 0
Pe_DbgNsec: t.CUInt32T = 0
Pe_DbgOptSize: t.CUInt32T = 0
Pe_DbgSecOff: t.CUInt32T = 0
Pe_DbgGot: t.CSizeT = 0
Pe_DbgLfanew: uint32 = 0
Pe_DbgNsec: uint32 = 0
Pe_DbgOptSize: uint32 = 0
Pe_DbgSecOff: uint32 = 0
Pe_DbgGot: sizet = 0
Pe_DbgLastRc: int = 0
@@ -86,17 +86,17 @@ Pe_DbgLastRc: int = 0
# 小端读取(Buf 为无符号字节基址,Buf[i] 即 u8)
# ============================================================
def ReadU16(Buf: BYTEPTR, Off: int) -> t.CUInt32T:
Val: t.CUInt32T = t.CUInt32T(Buf[Off])
Val = Val | (t.CUInt32T(Buf[Off + 1]) << 8)
def ReadU16(Buf: BYTEPTR, Off: int) -> uint32:
Val: uint32 = uint32(Buf[Off])
Val = Val | (uint32(Buf[Off + 1]) << 8)
return Val
def ReadU32(Buf: BYTEPTR, Off: int) -> t.CUInt32T:
Val: t.CUInt32T = t.CUInt32T(Buf[Off])
Val = Val | (t.CUInt32T(Buf[Off + 1]) << 8)
Val = Val | (t.CUInt32T(Buf[Off + 2]) << 16)
Val = Val | (t.CUInt32T(Buf[Off + 3]) << 24)
def ReadU32(Buf: BYTEPTR, Off: int) -> uint32:
Val: uint32 = uint32(Buf[Off])
Val = Val | (uint32(Buf[Off + 1]) << 8)
Val = Val | (uint32(Buf[Off + 2]) << 16)
Val = Val | (uint32(Buf[Off + 3]) << 24)
return Val
@@ -126,7 +126,7 @@ def FillArch(OutArch: bytes, Cap: int, Machine: int) -> int:
elif Machine == MACHINE_ARM or Machine == MACHINE_ARMNT:
VFs.CopyRaw(OutArch, Cap, "arm")
else:
viperlib.snprintf(OutArch, t.CSizeT(Cap), "unknown(0x%X)", t.CUInt32T(Machine))
viperlib.snprintf(OutArch, sizet(Cap), "unknown(0x%X)", uint32(Machine))
return 1
@@ -134,15 +134,15 @@ def FillArch(OutArch: bytes, Cap: int, Machine: int) -> int:
# Rc:失败码;Fsz:文件大小(该点尚不可知时传 0);RawPtr/RawSize:.PRVERSN 段位置(尚不可知时传 0)。
# sectab_end = sec_off + nsec * 40,由打印时当场推算(尚未解析出的量保持 0)。
# 属"内部路径细节"(逐次解析中间量),默认安静,仅 --verbose 时输出。
def PeDbgPrint(Rc: int, Fsz: t.CSizeT, RawPtr: t.CUInt32T, RawSize: t.CUInt32T):
def PeDbgPrint(Rc: int, Fsz: sizet, RawPtr: uint32, RawSize: uint32):
global Pe_DbgLastRc
Pe_DbgLastRc = Rc
SecTabEnd: t.CSizeT = t.CSizeT(Pe_DbgSecOff) + t.CSizeT(Pe_DbgNsec) * t.CSizeT(SEC_ENTRY_SIZE)
SecTabEnd: sizet = sizet(Pe_DbgSecOff) + sizet(Pe_DbgNsec) * sizet(SEC_ENTRY_SIZE)
VLog.VerboseFmt4(" [pe] rc=%u lfanew=%u nsec=%u optsize=%u",
t.CSizeT(Rc), t.CSizeT(Pe_DbgLfanew), t.CSizeT(Pe_DbgNsec), t.CSizeT(Pe_DbgOptSize))
sizet(Rc), sizet(Pe_DbgLfanew), sizet(Pe_DbgNsec), sizet(Pe_DbgOptSize))
VLog.VerboseFmt4(" sec_off=%u sectab_end=%u got=%u fsz=%u",
t.CSizeT(Pe_DbgSecOff), SecTabEnd, Pe_DbgGot, Fsz)
VLog.VerboseFmt2(" rawptr=%u rawsize=%u\n", t.CSizeT(RawPtr), t.CSizeT(RawSize))
sizet(Pe_DbgSecOff), SecTabEnd, Pe_DbgGot, Fsz)
VLog.VerboseFmt2(" rawptr=%u rawsize=%u\n", sizet(RawPtr), sizet(RawSize))
# 取"最近一次 TryReadPeInfo 的 CreateFileA 失败错误码"(rc=1 时才有意义;其余情况为 0)。
@@ -159,7 +159,7 @@ def PeOpenErr() -> ULONG:
# 7=未找到 .PRVERSN
# 每个 return 前把失败码写入 Pe_DbgLastRc,关键中间量写入 Pe_DbgLfanew/Pe_DbgNsec/
# Pe_DbgOptSize/Pe_DbgSecOff/Pe_DbgGot,供调用点在失败时打 [pe] 行。
def ParsePeHead(Buf: bytes, Got: t.CSizeT, OutArch: bytes, ArchCap: int) -> int:
def ParsePeHead(Buf: bytes, Got: sizet, OutArch: bytes, ArchCap: int) -> int:
global Pe_RawPtr, Pe_RawSize
global Pe_DbgLfanew, Pe_DbgNsec, Pe_DbgOptSize, Pe_DbgSecOff, Pe_DbgGot, Pe_DbgLastRc
Pe_RawPtr = 0
@@ -177,32 +177,32 @@ def ParsePeHead(Buf: bytes, Got: t.CSizeT, OutArch: bytes, ArchCap: int) -> int:
if ReadU16(ub, 0) != DOS_SIGNATURE:
Pe_DbgLastRc = 2
return 2
lfanew: t.CUInt32T = ReadU32(ub, E_LFANEW_OFF)
lfanew: uint32 = ReadU32(ub, E_LFANEW_OFF)
Pe_DbgLfanew = lfanew
if t.CSizeT(lfanew) + 24 > Got:
if sizet(lfanew) + 24 > Got:
Pe_DbgLastRc = 3
return 3
if ReadU32(ub, t.CInt(lfanew)) != NT_SIGNATURE:
if ReadU32(ub, int(lfanew)) != NT_SIGNATURE:
Pe_DbgLastRc = 4
return 4
nsec: t.CUInt32T = ReadU16(ub, t.CInt(lfanew) + COFF_NUMSEC_OFF)
optsize: t.CUInt32T = ReadU16(ub, t.CInt(lfanew) + COFF_OPTSIZE_OFF)
nsec: uint32 = ReadU16(ub, int(lfanew) + COFF_NUMSEC_OFF)
optsize: uint32 = ReadU16(ub, int(lfanew) + COFF_OPTSIZE_OFF)
Pe_DbgNsec = nsec
Pe_DbgOptSize = optsize
magic: t.CUInt32T = ReadU16(ub, t.CInt(lfanew) + OPT_MAGIC_OFF)
magic: uint32 = ReadU16(ub, int(lfanew) + OPT_MAGIC_OFF)
if magic != OPT_MAGIC_PE32 and magic != OPT_MAGIC_PE32P:
Pe_DbgLastRc = 5
return 5
mach: t.CUInt32T = ReadU16(ub, t.CInt(lfanew) + COFF_MACHINE_OFF)
FillArch(OutArch, ArchCap, t.CInt(mach))
sec_off: t.CInt = t.CInt(lfanew) + 24 + t.CInt(optsize)
Pe_DbgSecOff = t.CUInt32T(sec_off)
if t.CSizeT(sec_off) + t.CSizeT(nsec) * t.CSizeT(SEC_ENTRY_SIZE) > Got:
mach: uint32 = ReadU16(ub, int(lfanew) + COFF_MACHINE_OFF)
FillArch(OutArch, ArchCap, int(mach))
sec_off: int = int(lfanew) + 24 + int(optsize)
Pe_DbgSecOff = uint32(sec_off)
if sizet(sec_off) + sizet(nsec) * sizet(SEC_ENTRY_SIZE) > Got:
Pe_DbgLastRc = 6
return 6 # 节表超出头部窗口(极端多节 PE,跳过)
i: int = 0
while i < t.CInt(nsec):
entry: t.CInt = sec_off + i * SEC_ENTRY_SIZE
while i < int(nsec):
entry: int = sec_off + i * SEC_ENTRY_SIZE
if string.strncmp(str(Buf + entry + SEC_NAME_OFF), VERSION_SECTION, 8) == 0:
Pe_RawPtr = ReadU32(ub, entry + SEC_RAWPTR_OFF)
Pe_RawSize = ReadU32(ub, entry + SEC_RAWSIZE_OFF)
@@ -213,8 +213,8 @@ def ParsePeHead(Buf: bytes, Got: t.CSizeT, OutArch: bytes, ArchCap: int) -> int:
# 把 Src[Off .. Off+Len) 中的版本串(到第一个 NUL 或 VerCap-1)拷入 OutVer
def CopyVersion(Src: bytes, Off: t.CInt, Len: t.CInt, OutVer: bytes, VerCap: int) -> int:
n: t.CInt = 0
def CopyVersion(Src: bytes, Off: int, Len: int, OutVer: bytes, VerCap: int) -> int:
n: int = 0
while n < Len and n < VerCap - 1:
if Src[Off + n] == 0:
break
@@ -251,33 +251,33 @@ def TryReadPeInfo(Target: str, OutVer: bytes, VerCap: int, OutArch: bytes, ArchC
None, wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, None)
if h == wb.INVALID_HANDLE_VALUE:
Pe_OpenErr = wb.GetLastError() # 紧跟 CreateFileA 读取(PeDbgPrint 的写日志会覆盖)
PeDbgPrint(1, t.CSizeT(0), t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(1, sizet(0), uint32(0), uint32(0))
return 1
li: wb.LARGE_INTEGER = wb.LARGE_INTEGER()
if wf.GetFileSizeEx(h, t.CPtr(c.Addr(li))) == 0:
if wf.GetFileSizeEx(h, t.CPtr(&li)) == 0:
wb.CloseHandle(h)
PeDbgPrint(2, t.CSizeT(0), t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(2, sizet(0), uint32(0), uint32(0))
return 2
fsz: t.CSizeT = t.CSizeT(li.QuadPart)
fsz: sizet = sizet(li.QuadPart)
if fsz < 0x40:
wb.CloseHandle(h)
PeDbgPrint(3, fsz, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(3, fsz, uint32(0), uint32(0))
return 3
head_cap: t.CSizeT = t.CSizeT(PE_HEAD_READ)
head_cap: sizet = sizet(PE_HEAD_READ)
if fsz < head_cap:
head_cap = fsz
head: bytes = stdlib.malloc(head_cap + 1)
if head is None:
wb.CloseHandle(h)
PeDbgPrint(4, fsz, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(4, fsz, uint32(0), uint32(0))
return 4
got: t.CSizeT = VFs.ReadFull(h, head, head_cap)
got: sizet = VFs.ReadFull(h, head, head_cap)
head[got] = 0
Pe_DbgGot = got
if got < 0x40:
stdlib.free(t.CPtr(head))
wb.CloseHandle(h)
PeDbgPrint(5, fsz, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(5, fsz, uint32(0), uint32(0))
return 5
prc: int = ParsePeHead(head, got, OutArch, ArchCap)
@@ -286,17 +286,17 @@ def TryReadPeInfo(Target: str, OutVer: bytes, VerCap: int, OutArch: bytes, ArchC
wb.CloseHandle(h)
PeDbgPrint(100 + prc, fsz, Pe_RawPtr, Pe_RawSize)
return 100 + prc
raw_ptr: t.CUInt32T = Pe_RawPtr
raw_size: t.CUInt32T = Pe_RawSize
if raw_size == 0 or t.CSizeT(raw_ptr) + t.CSizeT(raw_size) > fsz:
raw_ptr: uint32 = Pe_RawPtr
raw_size: uint32 = Pe_RawSize
if raw_size == 0 or sizet(raw_ptr) + sizet(raw_size) > fsz:
stdlib.free(t.CPtr(head))
wb.CloseHandle(h)
PeDbgPrint(6, fsz, raw_ptr, raw_size)
return 6
if t.CSizeT(raw_ptr) + t.CSizeT(raw_size) <= got:
if sizet(raw_ptr) + sizet(raw_size) <= got:
# 段内容已在头部窗口内
CopyVersion(head, t.CInt(raw_ptr), t.CInt(raw_size), OutVer, VerCap)
CopyVersion(head, int(raw_ptr), int(raw_size), OutVer, VerCap)
else:
# 段内容不在头部窗口内:改走"整文件读"路径(与 ver [1/3] 同一套 API,已验证可用)。
# 这里不用 wf.SetFilePointerEx 定位:其 liDistanceToMove 在真实 Win32 上按值传 8 字节,
@@ -310,7 +310,7 @@ def TryReadPeInfo(Target: str, OutVer: bytes, VerCap: int, OutArch: bytes, ArchC
if whole is None:
PeDbgPrint(7, fsz, raw_ptr, raw_size)
return 7
CopyVersion(whole, t.CInt(raw_ptr), t.CInt(raw_size), OutVer, VerCap)
CopyVersion(whole, int(raw_ptr), int(raw_size), OutVer, VerCap)
stdlib.free(t.CPtr(whole))
return 0
@@ -340,7 +340,7 @@ def Phase1VersionSection(Target: str) -> int:
if buf is None:
rc_err: int = VFs.LastReadErr
if rc_err == 1:
VLog.LogFmt1(" 打开文件失败 (win32 err=%u)\n", t.CSizeT(VFs.LastReadWinErr))
VLog.LogFmt1(" 打开文件失败 (win32 err=%u)\n", sizet(VFs.LastReadWinErr))
elif rc_err == 2:
VLog.LogStr(" 读取文件大小失败\n")
elif rc_err == 3:
@@ -348,12 +348,12 @@ def Phase1VersionSection(Target: str) -> int:
else:
VLog.LogStr(" 分配缓冲区失败\n")
return 1
total: t.CSizeT = VFs.LastReadSize
total: sizet = VFs.LastReadSize
Pe_DbgGot = total
if total < 0x40:
VLog.LogStr(" 不是有效的 PE 文件(文件过短)\n")
PeDbgPrint(1, total, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(1, total, uint32(0), uint32(0))
stdlib.free(t.CPtr(buf))
return 1
@@ -362,50 +362,50 @@ def Phase1VersionSection(Target: str) -> int:
# DOS 头
if ReadU16(ub, 0) != DOS_SIGNATURE:
VLog.LogStr(" 不是有效的 PE 文件(缺少 MZ 头)\n")
PeDbgPrint(2, total, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(2, total, uint32(0), uint32(0))
stdlib.free(t.CPtr(buf))
return 1
# NT 头
lfanew: t.CUInt32T = ReadU32(ub, E_LFANEW_OFF)
lfanew: uint32 = ReadU32(ub, E_LFANEW_OFF)
Pe_DbgLfanew = lfanew
if t.CSizeT(lfanew) + 24 > total:
if sizet(lfanew) + 24 > total:
VLog.LogStr(" 不是有效的 PE 文件(e_lfanew 越界)\n")
PeDbgPrint(3, total, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(3, total, uint32(0), uint32(0))
stdlib.free(t.CPtr(buf))
return 1
if ReadU32(ub, t.CInt(lfanew)) != NT_SIGNATURE:
if ReadU32(ub, int(lfanew)) != NT_SIGNATURE:
VLog.LogStr(" 不是有效的 PE 文件(缺少 PE 签名)\n")
PeDbgPrint(4, total, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(4, total, uint32(0), uint32(0))
stdlib.free(t.CPtr(buf))
return 1
nsec: t.CUInt32T = ReadU16(ub, t.CInt(lfanew) + COFF_NUMSEC_OFF)
optsize: t.CUInt32T = ReadU16(ub, t.CInt(lfanew) + COFF_OPTSIZE_OFF)
nsec: uint32 = ReadU16(ub, int(lfanew) + COFF_NUMSEC_OFF)
optsize: uint32 = ReadU16(ub, int(lfanew) + COFF_OPTSIZE_OFF)
Pe_DbgNsec = nsec
Pe_DbgOptSize = optsize
magic: t.CUInt32T = ReadU16(ub, t.CInt(lfanew) + OPT_MAGIC_OFF)
magic: uint32 = ReadU16(ub, int(lfanew) + OPT_MAGIC_OFF)
if magic != OPT_MAGIC_PE32 and magic != OPT_MAGIC_PE32P:
VLog.LogFmt1(" 可选头 Magic 未知: 0x%X\n", magic)
PeDbgPrint(5, total, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(5, total, uint32(0), uint32(0))
stdlib.free(t.CPtr(buf))
return 1
sec_off: t.CInt = t.CInt(lfanew) + 24 + t.CInt(optsize)
Pe_DbgSecOff = t.CUInt32T(sec_off)
if t.CSizeT(sec_off) + t.CSizeT(nsec) * SEC_ENTRY_SIZE > total:
sec_off: int = int(lfanew) + 24 + int(optsize)
Pe_DbgSecOff = uint32(sec_off)
if sizet(sec_off) + sizet(nsec) * SEC_ENTRY_SIZE > total:
VLog.LogFmt1(" 节表越界(节数=%u)\n", nsec)
PeDbgPrint(6, total, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(6, total, uint32(0), uint32(0))
stdlib.free(t.CPtr(buf))
return 1
# 查找 .PRVERSN
found: int = 0
raw_ptr: t.CUInt32T = 0
raw_size: t.CUInt32T = 0
raw_ptr: uint32 = 0
raw_size: uint32 = 0
i: int = 0
while i < t.CInt(nsec):
entry: t.CInt = sec_off + i * SEC_ENTRY_SIZE
while i < int(nsec):
entry: int = sec_off + i * SEC_ENTRY_SIZE
if string.strncmp(str(buf + entry + SEC_NAME_OFF), VERSION_SECTION, 8) == 0:
found = 1
raw_size = ReadU32(ub, entry + SEC_RAWSIZE_OFF)
@@ -415,12 +415,12 @@ def Phase1VersionSection(Target: str) -> int:
if found == 0:
VLog.LogFmtS1(" 未找到 %s 节\n", VERSION_SECTION)
PeDbgPrint(7, total, t.CUInt32T(0), t.CUInt32T(0))
PeDbgPrint(7, total, uint32(0), uint32(0))
VLog.LogStr(" 实际节名:\n")
name8: bytes = stdlib.malloc(9)
if name8 is not None:
j: int = 0
while j < t.CInt(nsec):
while j < int(nsec):
CopySectionName(ub, sec_off + j * SEC_ENTRY_SIZE, name8)
VLog.LogFmtS1(" %s\n", str(name8))
j += 1
@@ -429,17 +429,17 @@ def Phase1VersionSection(Target: str) -> int:
return 1
# 取到第一个 NUL 为止
if t.CSizeT(raw_ptr) + t.CSizeT(raw_size) > total:
if sizet(raw_ptr) + sizet(raw_size) > total:
VLog.LogFmt2(" 段内容越界 (offset=%u size=%u)\n", raw_ptr, raw_size)
PeDbgPrint(6, total, raw_ptr, raw_size)
stdlib.free(t.CPtr(buf))
return 1
vlen: t.CUInt32T = 0
while vlen < raw_size and ub[t.CInt(raw_ptr) + t.CInt(vlen)] != 0:
vlen: uint32 = 0
while vlen < raw_size and ub[int(raw_ptr) + int(vlen)] != 0:
vlen += 1
VLog.LogFmt1(" 原始长度: %u 字节\n", vlen)
VLog.LogFmtS1(" 版本串: %s\n", str(buf + t.CInt(raw_ptr)))
VLog.LogFmtS1(" 版本串: %s\n", str(buf + int(raw_ptr)))
stdlib.free(t.CPtr(buf))
return 0
+26 -26
View File
@@ -38,12 +38,12 @@ import lib.VLog as VLog
# wb.CloseHandle(:64)、wb.WAIT_OBJECT_0(:15)、wb.WAIT_TIMEOUT(:16)、wb.INFINITE(:13)。
# ============================================================
VERSION_PROBE_TIMEOUT_MS: t.CDefine = 5000 # --version 探测超时(毫秒)
VERSION_PROBE_DIAG_CAP: t.CDefine = 4096 # ver [2/3] 诊断输出缓冲
PROBE_CMD_EXTRA_CAP: t.CDefine = 32 # 命令缓冲额外量(引号 + " --version" + NUL)
PROBE_TARGET_MAX: t.CDefine = 4096 # 目标路径上界(超限直接判启动失败,不构造命令)
PROBE_LINE_CAP: t.CDefine = 208 # 探测输出"首行"诊断缓冲(200 字符 + NUL + 对齐)
VERSION_PREFIX: t.CDefine = "Viper"
define VERSION_PROBE_TIMEOUT_MS = 5000 # --version 探测超时(毫秒)
define VERSION_PROBE_DIAG_CAP = 4096 # ver [2/3] 诊断输出缓冲
define PROBE_CMD_EXTRA_CAP = 32 # 命令缓冲额外量(引号 + " --version" + NUL)
define PROBE_TARGET_MAX = 4096 # 目标路径上界(超限直接判启动失败,不构造命令)
define PROBE_LINE_CAP = 208 # 探测输出"首行"诊断缓冲(200 字符 + NUL + 对齐)
define VERSION_PREFIX = "Viper"
# 结果输出(模块全局);RunVersionProbe 每次进入先复位
Probe_ExitCode: ULONG = 0
@@ -111,7 +111,7 @@ def CopyFirstLine(Src: bytes, Out: bytes, Cap: int) -> int:
# 失败诊断(含 GetLastError / 等待结果 / 等待时长)始终输出,且不内嵌被探测路径——路径与
# 已捕获内容/首行属逐次探测细节,仅 --verbose 时经 Verbose* 给出(默认不逐路径刷屏)。
# ============================================================
def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> int:
def RunVersionProbe(Target: str, Out: bytes, OutCap: sizet, Trace: int) -> int:
global Probe_ExitCode, Probe_TimedOut, Probe_LaunchStage, Probe_LaunchErr
Probe_ExitCode = 0
Probe_TimedOut = 0
@@ -122,7 +122,7 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
if Trace != 0:
VLog.VerboseStr(" [--version] 启动失败 (rc=1): 参数非法\n")
return 1
if string.strlen(Target) > t.CSizeT(PROBE_TARGET_MAX):
if string.strlen(Target) > sizet(PROBE_TARGET_MAX):
Probe_LaunchStage = 1
VLog.LogStr(" 警告: --version 目标路径过长 (rc=1),已跳过该候选\n")
return 1
@@ -132,7 +132,7 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
VLog.VerboseFmtS1(" [--version] 开始: %s\n", Target)
# --- 命令缓冲:"<exe>" --version ---
n: t.CSizeT = string.strlen(Target) + t.CSizeT(PROBE_CMD_EXTRA_CAP)
n: sizet = string.strlen(Target) + sizet(PROBE_CMD_EXTRA_CAP)
CmdLine: bytes = stdlib.malloc(n)
if CmdLine is None:
Probe_LaunchStage = 2
@@ -144,15 +144,15 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
# nLength 必须等于 sizeof(SECURITY_ATTRIBUTES)=24,否则 CreatePipe 直接失败
# (ERROR_INVALID_PARAMETER);lpSecurityDescriptor=NULL 表示默认安全描述符。
Sa: wb.SECURITY_ATTRIBUTES = wb.SECURITY_ATTRIBUTES()
Sa.nLength = t.CUInt32T(wb.SECURITY_ATTRIBUTES.__sizeof__())
Sa.nLength = uint32(wb.SECURITY_ATTRIBUTES.__sizeof__())
Sa.lpSecurityDescriptor = t.CPtr(0)
Sa.bInheritHandle = 1
# --- 管道(可继承;父进程随后关写端) ---
hRead: wb.HANDLE = None
hWrite: wb.HANDLE = None
if wf.CreatePipe(t.CPtr(c.Addr(hRead)), t.CPtr(c.Addr(hWrite)),
t.CPtr(c.Addr(Sa)), 0) == 0:
if wf.CreatePipe(t.CPtr(&hRead), t.CPtr(&hWrite),
t.CPtr(&Sa), 0) == 0:
PipeErr: ULONG = wb.GetLastError() # 紧跟 CreatePipe 读取
Probe_LaunchStage = 3
Probe_LaunchErr = PipeErr
@@ -164,7 +164,7 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
# --- STARTUPINFOA:cb=sizeof, dwFlags=STARTF_USESTDHANDLES, hStdOutput=hStdError=写端 ---
Si: wp.STARTUPINFOA = wp.STARTUPINFOA()
Si.cb = t.CUInt32T(wp.STARTUPINFOA.__sizeof__())
Si.cb = uint32(wp.STARTUPINFOA.__sizeof__())
Si.dwFlags = wp.STARTF_USESTDHANDLES
Si.hStdOutput = hWrite
Si.hStdError = hWrite
@@ -174,7 +174,7 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
# --- CreateProcessA(bInheritHandles=TRUE;CREATE_NO_WINDOW:输出走管道,不开控制台窗口) ---
Ok: BOOL = wp.CreateProcessA(t.CPtr(0), CmdLine, t.CPtr(0), t.CPtr(0), 1,
wp.CREATE_NO_WINDOW, t.CPtr(0), t.CPtr(0),
t.CPtr(c.Addr(Si)), t.CPtr(c.Addr(Pi)))
t.CPtr(&Si), t.CPtr(&Pi))
# GetLastError 紧跟调用读取:其后 stdlib.free 等都会覆盖它
CreateErr: ULONG = wb.GetLastError()
stdlib.free(t.CPtr(CmdLine))
@@ -184,7 +184,7 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
wb.CloseHandle(hRead)
wb.CloseHandle(hWrite)
VLog.LogFmt1(" 警告: --version 探测启动失败 (rc=1, CreateProcessA, GetLastError=%u)\n",
t.CSizeT(CreateErr))
sizet(CreateErr))
return 1
# 父进程关闭写端(子进程已持有其副本);此后管道只由子进程持有写端
@@ -192,29 +192,29 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
# --- 超时等待:先等进程退出,再读干管道 ---
TimedOut: int = 0
KillMs: t.CSizeT = 0
KillMs: sizet = 0
Wr: ULONG = wsync.WaitForSingleObject(Pi.hProcess, VERSION_PROBE_TIMEOUT_MS)
if Wr != wb.WAIT_OBJECT_0:
# WAIT_TIMEOUT(或 WAIT_FAILED 等):杀进程并回收,避免悬挂
TimedOut = 1
KillMs = t.CSizeT(wb.GetTickCount64() - T0)
KillMs = sizet(wb.GetTickCount64() - T0)
wp.TerminateProcess(Pi.hProcess, 1)
wsync.WaitForSingleObject(Pi.hProcess, wb.INFINITE)
# 读干管道:子进程已退出/被杀 ⇒ 写端全关,ReadFile 到 EOF 返回
total: t.CSizeT = 0
total: sizet = 0
while total + 1 < OutCap:
br: ULONG = 0
room: ULONG = ULONG(OutCap - 1 - total)
if wf.ReadFile(hRead, Out + total, room, t.CPtr(c.Addr(br)), None) == 0:
if wf.ReadFile(hRead, Out + total, room, t.CPtr(&br), None) == 0:
break
if br == 0:
break
total += t.CSizeT(br)
total += sizet(br)
Out[total] = 0
ExitCode: ULONG = 0
wp.GetExitCodeProcess(Pi.hProcess, t.CPtr(c.Addr(ExitCode)))
wp.GetExitCodeProcess(Pi.hProcess, t.CPtr(&ExitCode))
Probe_ExitCode = ExitCode
Probe_TimedOut = TimedOut
@@ -222,12 +222,12 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
wb.CloseHandle(Pi.hThread)
wb.CloseHandle(hRead)
Elapsed: t.CSizeT = t.CSizeT(wb.GetTickCount64() - T0)
Elapsed: sizet = sizet(wb.GetTickCount64() - T0)
if TimedOut != 0:
# 超时:写明等待结果(WAIT_TIMEOUT=258 / WAIT_FAILED=4294967295 等)与等待时长。
# 该候选路径与"超时前已捕获内容"属逐次探测细节,仅 --verbose 时给出(默认不逐路径刷屏)
VLog.LogFmt3(" 警告: --version 探测超时 (rc=2, wait=%u, 上限 %u ms),已等待 %u ms 后终止子进程\n",
t.CSizeT(Wr), t.CSizeT(VERSION_PROBE_TIMEOUT_MS), KillMs)
sizet(Wr), sizet(VERSION_PROBE_TIMEOUT_MS), KillMs)
if total == 0:
VLog.VerboseStr(" 警告: 超时前已捕获 0 字节(空)\n")
else:
@@ -236,7 +236,7 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
VLog.VerboseStr("\n")
if Trace != 0:
VLog.VerboseFmt4(" [--version] 结束: 耗时 %u ms, 退出码 %u, 超时 %u, 输出 %u 字节\n",
Elapsed, t.CSizeT(ExitCode), t.CSizeT(TimedOut), total)
Elapsed, sizet(ExitCode), sizet(TimedOut), total)
if TimedOut == 0:
# 正常结束(rc=0):捕获字节数与首行属逐次探测细节,仅 --verbose 时输出
# (ver 路径的 [2/3] 结果仍由 RunVersionPrefixCheck 照常给出)。
@@ -261,7 +261,7 @@ def RunVersionProbe(Target: str, Out: bytes, OutCap: t.CSizeT, Trace: int) -> in
# 行为增强:改用带超时探测(原实现无超时),其余日志与判定口径不变。
# ============================================================
def RunVersionPrefixCheck(Target: str) -> int:
OutCap: t.CSizeT = t.CSizeT(VERSION_PROBE_DIAG_CAP)
OutCap: sizet = sizet(VERSION_PROBE_DIAG_CAP)
Out: bytes = stdlib.malloc(OutCap)
if Out is None:
VLog.LogStr(" 分配缓冲区失败\n")
@@ -276,7 +276,7 @@ def RunVersionPrefixCheck(Target: str) -> int:
stdlib.free(t.CPtr(Out))
return 1
VLog.LogFmt1(" 退出码: %d\n", t.CSizeT(t.CUInt32T(Probe_ExitCode)))
VLog.LogFmt1(" 退出码: %d\n", sizet(uint32(Probe_ExitCode)))
out: str = str(Out)
if out is None:
VLog.LogStr(" 未捕获到 stdout\n")
+42 -42
View File
@@ -57,20 +57,20 @@ import w32.win32base as wb
# wf.FindClose(:119)、wf.FILE_ATTRIBUTE_DIRECTORY(:23)、wb.Sleep(:77)、wb.GetTickCount64(:80)。
# ============================================================
MAX_SCAN_DEPTH: t.CDefine = 6 # 递归深度上限
PACE_DIR_UNITS: t.CDefine = 16 # 每处理 16 个目录项
PACE_FILE_UNITS: t.CDefine = 64 # 每处理 64 个文件
PACE_SLEEP_MS: t.CDefine = 20 # 上述节流点 Sleep(20)
PROG_INTERVAL_MS: t.CDefine = 1000 # 进度行最短间隔(需求 3)
PROG_STAMP_CAP: t.CDefine = 64 # 时间戳文本缓冲
PROG_ELAPSED_CAP: t.CDefine = 32 # "3.2s" 文本缓冲
REJECT_LOG_MAX: t.CDefine = 20 # [reject] 逐条打印上限(超出后只在轮末打一次汇总)
define MAX_SCAN_DEPTH = 6 # 递归深度上限
define PACE_DIR_UNITS = 16 # 每处理 16 个目录项
define PACE_FILE_UNITS = 64 # 每处理 64 个文件
define PACE_SLEEP_MS = 20 # 上述节流点 Sleep(20)
define PROG_INTERVAL_MS = 1000 # 进度行最短间隔(需求 3)
define PROG_STAMP_CAP = 64 # 时间戳文本缓冲
define PROG_ELAPSED_CAP = 32 # "3.2s" 文本缓冲
define REJECT_LOG_MAX = 20 # [reject] 逐条打印上限(超出后只在轮末打一次汇总)
# WIN32_FIND_DATAA:4+8+8+8+4+4+4+4 = 44 之后为 cFileName(见"API 证据表"推导)
FD_ATTRIBUTES_OFF: t.CDefine = 0
FD_FILENAME_OFF: t.CDefine = 44
FIND_DATA_SIZE: t.CDefine = 320 # sizeof(WIN32_FIND_DATAA):318 对齐到 4 = 320
define FD_ATTRIBUTES_OFF = 0
define FD_FILENAME_OFF = 44
define FIND_DATA_SIZE = 320 # sizeof(WIN32_FIND_DATAA):318 对齐到 4 = 320
# winnt.h: FILE_ATTRIBUTE_REPARSE_POINT(符号链接/挂载点等重解析点,递归时跳过以防成环)
FILE_ATTRIBUTE_REPARSE_POINT: t.CDefine = 0x400
define FILE_ATTRIBUTE_REPARSE_POINT = 0x400
# 由 App/main.vp 注入
_mbuddy: rawptr[memhub.MemBuddy]
@@ -138,13 +138,13 @@ def IsSkippedDir(Name: str) -> int:
# 文件名是否以 .exe 结尾(大小写无关)
def HasExeSuffix(Name: str) -> int:
n: t.CSizeT = string.strlen(Name)
n: sizet = string.strlen(Name)
if n < 4:
return 0
a: int = int(Name[t.CInt(n) - 4])
b: int = int(Name[t.CInt(n) - 3])
d0: int = int(Name[t.CInt(n) - 2])
e0: int = int(Name[t.CInt(n) - 1])
a: int = int(Name[int(n) - 4])
b: int = int(Name[int(n) - 3])
d0: int = int(Name[int(n) - 2])
e0: int = int(Name[int(n) - 1])
if a >= 65 and a <= 90:
a += 32
if b >= 65 and b <= 90:
@@ -168,16 +168,16 @@ def ProgPrint():
global Prog_LastTick
Now: ULONGLONG = wb.GetTickCount64()
Prog_LastTick = Now
ElapsedMs: t.CSizeT = t.CSizeT(Now - Prog_T0)
Secs: t.CSizeT = ElapsedMs // 1000
Tenths: t.CSizeT = (ElapsedMs % 1000) // 100
viperlib.snprintf(Prog_Elapsed, t.CSizeT(PROG_ELAPSED_CAP), "%u.%us",
t.CUInt32T(Secs), t.CUInt32T(Tenths))
ElapsedMs: sizet = sizet(Now - Prog_T0)
Secs: sizet = ElapsedMs // 1000
Tenths: sizet = (ElapsedMs % 1000) // 100
viperlib.snprintf(Prog_Elapsed, sizet(PROG_ELAPSED_CAP), "%u.%us",
uint32(Secs), uint32(Tenths))
Cur: str = Prog_Cur
if Cur is None:
Cur = "?"
VLog.VerboseFmt4("[scan] r=%u dir=%u file=%u cand=%u",
t.CSizeT(Prog_Round), t.CSizeT(Prog_Dirs), t.CSizeT(Prog_Files), t.CSizeT(Prog_Cands))
sizet(Prog_Round), sizet(Prog_Dirs), sizet(Prog_Files), sizet(Prog_Cands))
VLog.VerboseFmtS2(" cur=%s elapsed=%s\n", Cur, str(Prog_Elapsed))
@@ -201,7 +201,7 @@ def ProgReset(RoundIdx: int, Roots: int):
Prog_Cands = 0
Prog_Cur = None
VLog.TimeStampNow(Prog_Stamp, PROG_STAMP_CAP)
VLog.VerboseFmt2("==== 扫盘轮次 %u 开始 (roots=%u", t.CSizeT(RoundIdx), t.CSizeT(Roots))
VLog.VerboseFmt2("==== 扫盘轮次 %u 开始 (roots=%u", sizet(RoundIdx), sizet(Roots))
VLog.VerboseFmtS1(", t=%s) ====\n", str(Prog_Stamp))
@@ -209,7 +209,7 @@ def ProgRootEnter(Dir: str):
VLog.VerboseFmtS1(" [root] 进入: %s\n", Dir)
def ProgRootLeave(Dir: str, ElapsedMs: t.CSizeT):
def ProgRootLeave(Dir: str, ElapsedMs: sizet):
global Prog_Cur
VLog.VerboseFmtIS(" [root] 离开 (耗时 %u ms): %s\n", ElapsedMs, Dir)
Prog_Cur = None # 该根结束后其路径缓冲即将释放,避免遗留悬空指针
@@ -277,7 +277,7 @@ def ScanInit() -> int:
# ----version 前缀探测(带 5s 超时;超时/启动失败 ⇒ 判不通过,不入缓存)
def ProbeVersion(Target: str) -> int:
rc: int = VProc.RunVersionProbe(Target, Probe_Buf, t.CSizeT(VProc.VERSION_PROBE_DIAG_CAP), 1)
rc: int = VProc.RunVersionProbe(Target, Probe_Buf, sizet(VProc.VERSION_PROBE_DIAG_CAP), 1)
if rc == 0:
if VProc.OutputHasViperPrefix(str(Probe_Buf)) != 0:
return 1
@@ -310,7 +310,7 @@ def HandleCandidate(ExeAnsi: str) -> int:
Reject_Total += 1
if Reject_Shown < REJECT_LOG_MAX:
Reject_Shown += 1
VLog.VerboseFmtIS(" [reject] rc=%u %s\n", t.CSizeT(rc), ExeAnsi)
VLog.VerboseFmtIS(" [reject] rc=%u %s\n", sizet(rc), ExeAnsi)
return 0
Prog_Cands += 1
# 仅对已过 PE + .PRVERSN 过滤的 exe 调用 --version(这类很少)。
@@ -323,7 +323,7 @@ def HandleCandidate(ExeAnsi: str) -> int:
if VGuid.NewGuidStr(Scr_Id, VGuid.GUID_STR_CAP) == 0:
VLog.LogStr(" 警告: CoCreateGuid 失败,跳过该条目\n")
return 0
viperlib.snprintf(Scr_Disp, t.CSizeT(VCache.DISP_CAP), "Viper %s", Scr_Ver)
viperlib.snprintf(Scr_Disp, sizet(VCache.DISP_CAP), "Viper %s", Scr_Ver)
if VCache.CacheAdd(ExeAnsi, str(Scr_Id), str(Scr_Ver), str(Scr_Arch),
"workspace", str(Scr_Disp)) == 0:
return 0
@@ -366,7 +366,7 @@ def ScanTree(DirAnsi: str, Depth: int) -> int:
if hDir != wb.INVALID_HANDLE_VALUE:
while True:
PaceDir()
Attrs: t.CUInt32T = VPe.ReadU32(BYTEPTR(Fd), FD_ATTRIBUTES_OFF)
Attrs: uint32 = VPe.ReadU32(BYTEPTR(Fd), FD_ATTRIBUTES_OFF)
Name: str = str(Fd + FD_FILENAME_OFF)
if string.strcmp(Name, ".") != 0 and string.strcmp(Name, "..") != 0:
if (Attrs & wf.FILE_ATTRIBUTE_DIRECTORY) != 0:
@@ -388,7 +388,7 @@ def ScanTree(DirAnsi: str, Depth: int) -> int:
hExe: wb.HANDLE = wf.FindFirstFileA(Pat, t.CPtr(Fd))
if hExe != wb.INVALID_HANDLE_VALUE:
while True:
Attrs2: t.CUInt32T = VPe.ReadU32(BYTEPTR(Fd), FD_ATTRIBUTES_OFF)
Attrs2: uint32 = VPe.ReadU32(BYTEPTR(Fd), FD_ATTRIBUTES_OFF)
Name2: str = str(Fd + FD_FILENAME_OFF)
if (Attrs2 & wf.FILE_ATTRIBUTE_DIRECTORY) == 0:
PaceFile()
@@ -469,7 +469,7 @@ def RunScanRound(CachePath: str, RoundIdx: int) -> int:
R0: ULONGLONG = wb.GetTickCount64()
ProgRootEnter(str(AnsiDir))
ScanTree(str(AnsiDir), 1)
ProgRootLeave(str(AnsiDir), t.CSizeT(wb.GetTickCount64() - R0))
ProgRootLeave(str(AnsiDir), sizet(wb.GetTickCount64() - R0))
else:
# 自动表里的静态两条(C:\Viper / D:\Viper)与盘符枚举结果可能本就不存在,
# 逐条回显属过程细节,仅在 --verbose 时给出(--path 的显式告警在 VDirs.BuildTable)
@@ -481,26 +481,26 @@ def RunScanRound(CachePath: str, RoundIdx: int) -> int:
# 这里只补一行汇总(不逐条重打)——逐条与汇总均属过程细节,仅 --verbose 时给出
if Reject_Total > REJECT_LOG_MAX:
VLog.VerboseFmt2(" [reject] 其余省略…(本轮累计 %u 条,已逐条打印前 %u 条)\n",
t.CSizeT(Reject_Total), t.CSizeT(REJECT_LOG_MAX))
sizet(Reject_Total), sizet(REJECT_LOG_MAX))
# 轮结束前的最后一次轮询:此后到写盘之间不再有等待点(check 命中项因此能进入本轮数组)
VGuard.PollIpc()
removed: int = VCache.ValidateCache()
VJson.EmitJson()
VLog.StdoutWrite(str(VJson.JsonBuf()))
VCache.WriteCacheFile(CachePath, VJson.JsonBuf(), t.CSizeT(VJson.JsonLen()))
VCache.WriteCacheFile(CachePath, VJson.JsonBuf(), sizet(VJson.JsonLen()))
T1: ULONGLONG = wb.GetTickCount64()
# 轮末一行(默认保留,不含路径):常驻模式下它是"本轮已跑完"的存活信号(与启动摘要同构),
# --once 时即结束行;"下轮开始"时间戳属下轮过程细节,仅 --verbose。
# "移除"含两部分:完整重校剔除(Revalidated)+ 存在性校验移除(removed)。
VLog.LogFmt3("轮次 %u 结束: 扫描目录 %u 个, 已知条目 %u 项",
t.CSizeT(RoundIdx), t.CSizeT(Dirs), t.CSizeT(VCache.CacheCount()))
sizet(RoundIdx), sizet(Dirs), sizet(VCache.CacheCount()))
VLog.LogFmt3(", 新增 %u 项, 移除 %u 项, 耗时 %u ms",
t.CSizeT(Round_Added), t.CSizeT(Revalidated + removed), t.CSizeT(T1 - T0))
sizet(Round_Added), sizet(Revalidated + removed), sizet(T1 - T0))
# "下轮开始"时间戳属过程细节(仅 --verbose)
if VLog.VerboseOn() != 0:
VLog.TimeStampAfter(Prog_Stamp, PROG_STAMP_CAP, t.CSizeT(VCli.SleepMs()))
VLog.TimeStampAfter(Prog_Stamp, PROG_STAMP_CAP, sizet(VCli.SleepMs()))
VLog.LogFmtS1(", 下轮开始: %s", str(Prog_Stamp))
VLog.LogStr("\n")
return 0
@@ -519,8 +519,8 @@ def RunScan() -> int:
# 不含缓存路径与其他被处理路径(路径属过程细节,仅 --verbose 时经目录表/进度行给出):
# 该行只承担"进程已启动"这一可观测信号,与轮末一行共同构成本模式的存活指示。
VLog.LogFmtS1("VET - Viper Environment Tool %s (scan)", VCli.Version())
VLog.LogFmt2(" sleep=%u once=%u", t.CSizeT(VCli.SleepMs()), t.CSizeT(Once))
VLog.LogFmt1(" need-confirm=%u\n", t.CSizeT(VCli.NeedConfirm()))
VLog.LogFmt2(" sleep=%u once=%u", sizet(VCli.SleepMs()), sizet(Once))
VLog.LogFmt1(" need-confirm=%u\n", sizet(VCli.NeedConfirm()))
if VCli.PaceSet() != 0:
# 显式给出 --pace 或 --pace-units ⇒ 一律启用节流(--once 下也生效),时长/粒度取命令行值;
@@ -531,7 +531,7 @@ def RunScan() -> int:
Pace_Sleep_Ms = VCli.PaceSleepMs()
Pace_File_Units = VCli.PaceFileUnits()
VLog.VerboseFmt2("--pace: 已启用扫描节流 sleep=%u units=%u\n",
t.CSizeT(Pace_Sleep_Ms), t.CSizeT(Pace_File_Units))
sizet(Pace_Sleep_Ms), sizet(Pace_File_Units))
elif Once != 0:
# 未显式给出节流参数时的原有行为:--once 只跑一轮,关闭节流 sleep(PaceDir/PaceFile 均不再 Sleep)
Pace_Enabled = 0
@@ -608,11 +608,11 @@ def RunSearchPathsQuery() -> int:
if VFs.PathIsDir(str(Ansi)) == 0:
Skip = 1
if Skip != 0:
VLog.LogFmtIS(" [search] [%u] %s (不存在或非目录,扫盘时会跳过)\n", t.CSizeT(i), Path)
VLog.LogFmtIS(" [search] [%u] %s (不存在或非目录,扫盘时会跳过)\n", sizet(i), Path)
else:
VLog.LogFmtIS(" [search] [%u] %s\n", t.CSizeT(i), Path)
VLog.LogFmtIS(" [search] [%u] %s\n", sizet(i), Path)
i += 1
if Ansi is not None:
_mbuddy.free(t.CPtr(Ansi))
VLog.LogFmt1(" [search] 合计 %u 条;未扫盘、未读写缓存,查询到此结束\n", t.CSizeT(N))
VLog.LogFmt1(" [search] 合计 %u 条;未扫盘、未读写缓存,查询到此结束\n", sizet(N))
return 0
+1 -1
View File
@@ -12,4 +12,4 @@ import sys
# 其它模块只经各模块公开函数访问,不交叉读写本文件的全局。
# ============================================================
LIB_PACKAGE_MARKER: t.CDefine = 1
define LIB_PACKAGE_MARKER = 1
+2 -2
View File
@@ -32,8 +32,8 @@ import lib.VCli as VCli
# → 初始化 argv → 分派子命令。所有实现见 lib/ 下各模块。
# ============================================================
CODE_PAGE: t.CDefine = 65001
POOL_SIZE: t.CDefine = 16777216 # MemBuddy 主内存池 16 MiB
define CODE_PAGE = 65001
define POOL_SIZE = 16777216 # MemBuddy 主内存池 16 MiB
@t.CExport