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:
1 parent
9adaa5cc46
commit
5ba1310524
14 files changed
+449
-449
No files matched your search
+16
-16
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -12,4 +12,4 @@ import sys
|
||||
# 其它模块只经各模块公开函数访问,不交叉读写本文件的全局。
|
||||
# ============================================================
|
||||
|
||||
LIB_PACKAGE_MARKER: t.CDefine = 1
|
||||
define LIB_PACKAGE_MARKER = 1
|
||||
+2
-2
@@ -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
|
||||
|
||||
Reference in new issue
Block a user