quickwin 3.0.1 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +3 -0
- package/README.md +2 -0
- package/package.json +1 -3
- package/qwin-nowasm-x86.exe +0 -0
- package/qwin-nowasm.exe +0 -0
- package/qwin-x86.exe +0 -0
- package/qwin.exe +0 -0
- package/examples/PdfCanvas.js +0 -38
- package/examples/PdfCanvas.tsx +0 -54
- package/examples/execd/main.js +0 -23379
- package/examples/execd/tray.js +0 -192
- package/examples/execd/worker.js +0 -122
- package/examples/pdf_preview2.js +0 -398
- package/examples/pdf_preview2.ts +0 -353
- package/examples/pdf_viewer.js +0 -22515
- package/examples/pdf_viewer.tsx +0 -228
- package/examples/test_gallery.js +0 -18692
- package/examples/test_gallery.tsx +0 -291
- package/examples/test_react_lazy.js +0 -18560
- package/examples/test_react_lazy.tsx +0 -65
- package/examples/test_react_listbox.js +0 -18562
- package/examples/test_react_listbox.tsx +0 -73
- package/examples/test_root_window.js +0 -18532
- package/examples/test_root_window.tsx +0 -41
- package/examples/test_tab.js +0 -18563
- package/examples/test_tab.tsx +0 -77
- package/examples/tray_demo.js +0 -86
- package/examples/tray_demo.tsx +0 -89
- package/test/add.wasm +0 -0
- package/test/bench_ffi_callraw.js +0 -235
- package/test/bench_ffi_callraw.ts +0 -235
- package/test/bench_ffi_overhead.js +0 -139
- package/test/bench_ffi_overhead.ts +0 -141
- package/test/bench_sendmessage.js +0 -104
- package/test/bench_sendmessage.ts +0 -100
- package/test/bench_sendmessage_nonnull.js +0 -187
- package/test/bench_sendmessage_nonnull.ts +0 -188
- package/test/complex.wasm +0 -0
- package/test/complex_imports.wasm +0 -0
- package/test/debug/http_server_e2e.js +0 -31
- package/test/debug/minwasm.js +0 -52
- package/test/debug/mupdf_debug.js +0 -86
- package/test/debug/wasm.js +0 -122
- package/test/debug/wasm_export_debug.js +0 -54
- package/test/debug/wasm_names.js +0 -36
- package/test/example.pdf +0 -0
- package/test/global_imports.wasm +0 -0
- package/test/import_func.wasm +0 -0
- package/test/imports.wasm +0 -0
- package/test/mupdf-cdn.js +0 -40
- package/test/mupdf-cdn.ts +0 -43
- package/test/run.js +0 -111
- package/test/run.ts +0 -116
- package/test/sjlj.wasm +0 -0
- package/test/test_basic.js +0 -7
- package/test/test_basic.ts +0 -9
- package/test/test_basic_fetch.js +0 -63
- package/test/test_basic_fetch.ts +0 -59
- package/test/test_brotli.js +0 -48
- package/test/test_brotli.ts +0 -52
- package/test/test_fetch_cache.js +0 -169
- package/test/test_fetch_cache.ts +0 -187
- package/test/test_ffi.js +0 -157
- package/test/test_ffi.ts +0 -170
- package/test/test_ffi_abi.js +0 -290
- package/test/test_ffi_abi.ts +0 -274
- package/test/test_ffi_array.js +0 -116
- package/test/test_ffi_array.ts +0 -141
- package/test/test_ffi_bind.js +0 -656
- package/test/test_ffi_bind.ts +0 -670
- package/test/test_ffi_struct.js +0 -779
- package/test/test_ffi_struct.ts +0 -828
- package/test/test_ffi_struct_layout.js +0 -495
- package/test/test_ffi_struct_layout.ts +0 -543
- package/test/test_ffi_user32.js +0 -103
- package/test/test_ffi_user32.ts +0 -113
- package/test/test_ffi_windows.js +0 -172
- package/test/test_ffi_windows.ts +0 -187
- package/test/test_files.js +0 -102
- package/test/test_files.ts +0 -121
- package/test/test_frame_encoding.js +0 -128
- package/test/test_frame_encoding.ts +0 -132
- package/test/test_gdi_draw.js +0 -681
- package/test/test_gdi_draw.ts +0 -725
- package/test/test_helper.js +0 -91
- package/test/test_helper.ts +0 -88
- package/test/test_http_import.js +0 -96
- package/test/test_http_import.ts +0 -93
- package/test/test_http_server.js +0 -214
- package/test/test_http_server.ts +0 -211
- package/test/test_import_dir.js +0 -4
- package/test/test_import_dir.ts +0 -5
- package/test/test_ipv6.js +0 -134
- package/test/test_ipv6.ts +0 -130
- package/test/test_jsx.js +0 -1314
- package/test/test_meta_url.js +0 -3
- package/test/test_meta_url.ts +0 -4
- package/test/test_mupdf_render.js +0 -51
- package/test/test_mupdf_render.ts +0 -55
- package/test/test_mupdf_twice.js +0 -62
- package/test/test_mupdf_twice.ts +0 -65
- package/test/test_mupdf_wasm.js +0 -39
- package/test/test_mupdf_wasm.ts +0 -32
- package/test/test_net_event.js +0 -57
- package/test/test_net_event.ts +0 -56
- package/test/test_net_fetch.js +0 -384
- package/test/test_net_fetch.ts +0 -373
- package/test/test_net_websocket.js +0 -180
- package/test/test_net_websocket.ts +0 -164
- package/test/test_polyfill.js +0 -144
- package/test/test_polyfill.ts +0 -159
- package/test/test_printer.js +0 -137
- package/test/test_printer.ts +0 -148
- package/test/test_proc.js +0 -141
- package/test/test_proc.ts +0 -163
- package/test/test_react_complex.js +0 -18678
- package/test/test_react_counter.js +0 -18555
- package/test/test_react_flex.js +0 -18871
- package/test/test_react_hidden.js +0 -18560
- package/test/test_react_listview_link.js +0 -18590
- package/test/test_react_notify.js +0 -18797
- package/test/test_react_render.js +0 -18550
- package/test/test_registry.js +0 -74
- package/test/test_registry.ts +0 -95
- package/test/test_sysinfo.js +0 -122
- package/test/test_sysinfo.ts +0 -141
- package/test/test_url.js +0 -173
- package/test/test_url.ts +0 -183
- package/test/test_wasm_basic.js +0 -85
- package/test/test_wasm_basic.ts +0 -73
- package/test/test_wasm_import_global.js +0 -41
- package/test/test_wasm_import_global.ts +0 -45
- package/test/test_wasm_sjlj.js +0 -145
- package/test/test_wasm_sjlj.ts +0 -125
- package/test/test_wasm_types.js +0 -97
- package/test/test_wasm_types.ts +0 -124
- package/test/test_wolfssl_read.js +0 -203
- package/test/test_wolfssl_read.ts +0 -184
- package/test/test_worker.js +0 -21
- package/test/test_worker.ts +0 -24
- package/test/test_worker_concurrent.js +0 -45
- package/test/test_worker_concurrent.ts +0 -56
- package/test/test_worker_data_url.js +0 -46
- package/test/test_worker_data_url.ts +0 -51
- package/test/test_worker_fetch.js +0 -26
- package/test/test_worker_fetch.ts +0 -27
- package/test/test_worker_net.js +0 -69
- package/test/test_worker_net.ts +0 -66
- package/test/test_worker_wasm_concurrent.js +0 -44
- package/test/test_worker_wasm_concurrent.ts +0 -45
- package/test/types.wasm +0 -0
- package/test/worker_fetch.js +0 -13
- package/test/worker_fetch.ts +0 -13
- package/test/worker_helper.js +0 -1
- package/test/worker_helper.ts +0 -1
- package/test/worker_http.js +0 -18
- package/test/worker_http.ts +0 -20
- package/test/worker_import_lib.js +0 -12
- package/test/worker_import_lib.ts +0 -13
- package/test/worker_module.js +0 -16
- package/test/worker_module.ts +0 -17
- package/test/worker_wasm_concurrent.js +0 -22
- package/test/worker_wasm_concurrent.ts +0 -22
package/test/test_printer.ts
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import * as ffi from 'ffi'
|
|
2
|
-
import * as os from 'os'
|
|
3
|
-
import { WCHAR } from '../lib/ffi/bind.js'
|
|
4
|
-
import { structArray } from '../lib/ffi/struct.js'
|
|
5
|
-
import { NULL, PtrArrayBuffer } from '../lib/ffi/ctype.js'
|
|
6
|
-
import { CloseHandle, CreateFile, DeleteFile, GetLastError, GetTempPath, GetFileSize, ReadFile } from '../lib/windows/kernel32.js'
|
|
7
|
-
import {
|
|
8
|
-
ClosePrinter, DeviceCapabilities, EnumPrinters, EndDocPrinter, EndPagePrinter,
|
|
9
|
-
GetDefaultPrinter, OpenPrinter, StartDocPrinter, StartPagePrinter, WritePrinter,
|
|
10
|
-
} from '../lib/windows/winspool.js'
|
|
11
|
-
import { PRINTER_INFO_2W } from '../lib/windows/structs.js'
|
|
12
|
-
import { Tester } from './test_helper.js'
|
|
13
|
-
import { DeviceCap, FileCreation } from '../constants'
|
|
14
|
-
|
|
15
|
-
// PrinterEnum.LOCAL(0x2) | CONNECTIONS(0x4):可组合 flags 组合 OR 裸传
|
|
16
|
-
const LOCAL_OR_CONNECTIONS = 0x6
|
|
17
|
-
const MAX_WCHARS = 4096
|
|
18
|
-
|
|
19
|
-
// 指针字段跟进读宽字符串 / 跨架构读指针(test_ffi.ts 同型)
|
|
20
|
-
function decodeWideAtPtr(ptr: number): string {
|
|
21
|
-
if (!ptr) return ''
|
|
22
|
-
const chars: number[] = []
|
|
23
|
-
let pos = ptr
|
|
24
|
-
while (chars.length < MAX_WCHARS) {
|
|
25
|
-
const ch = ffi.readByte(pos) + ffi.readByte(pos + 1) * 256
|
|
26
|
-
if (ch === 0) break
|
|
27
|
-
chars.push(ch)
|
|
28
|
-
pos += 2
|
|
29
|
-
}
|
|
30
|
-
return String.fromCharCode(...chars)
|
|
31
|
-
}
|
|
32
|
-
function readPtr(dv: DataView, offset: number): number {
|
|
33
|
-
if (os.arch === 'ia32') return dv.getUint32(offset, true)
|
|
34
|
-
return dv.getUint32(offset, true) + dv.getUint32(offset + 4, true) * 4294967296
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const suite = {
|
|
38
|
-
name: 'printer',
|
|
39
|
-
run: (t: Tester) => {
|
|
40
|
-
t.section('EnumPrinters: two-pass LOCAL|CONNECTIONS')
|
|
41
|
-
const neededB = new PtrArrayBuffer(4)
|
|
42
|
-
const returnedB = new PtrArrayBuffer(4)
|
|
43
|
-
const neededU = new Uint32Array(neededB)
|
|
44
|
-
const returnedU = new Uint32Array(returnedB)
|
|
45
|
-
EnumPrinters(LOCAL_OR_CONNECTIONS, NULL, 2, NULL, 0, neededB, returnedB)
|
|
46
|
-
if (neededU[0]! === 0) {
|
|
47
|
-
// 无打印机环境(XP VM):整段优雅跳过——属环境差异非缺陷(test_ffi 先例)
|
|
48
|
-
t.skipCase('printer suite (no printers on this VM)')
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
t.checkTrue(`pcbNeeded > 0 (${neededU[0]!})`, neededU[0]! > 0)
|
|
52
|
-
const buf = new PtrArrayBuffer(neededU[0]!)
|
|
53
|
-
t.checkTrue('EnumPrinters full buffer',
|
|
54
|
-
EnumPrinters(LOCAL_OR_CONNECTIONS, NULL, 2, buf, neededU[0]!, neededB, returnedB) !== 0)
|
|
55
|
-
const count = returnedU[0]!
|
|
56
|
-
t.checkTrue(`found printers (${count})`, count > 0)
|
|
57
|
-
|
|
58
|
-
// 出参数组读回:结构数组区 = 前 count*size 字节,尾部是字符串数据
|
|
59
|
-
// (byteLength 非 size 整数倍)→ 必须显式 count;指针字段跟进读字符串
|
|
60
|
-
const infos = structArray(PRINTER_INFO_2W).decode(buf, count)
|
|
61
|
-
t.check('decode count == pcReturned', count, infos.length)
|
|
62
|
-
// 单值 decode 与数组 decode 首元素一致(布局单一来源互证,无硬编码 offset)
|
|
63
|
-
t.checkTrue('array/single decode agree',
|
|
64
|
-
PRINTER_INFO_2W.decode(buf).pPrinterName === infos[0]!.pPrinterName)
|
|
65
|
-
const names: string[] = []
|
|
66
|
-
const ports: Record<string, string> = {}
|
|
67
|
-
for (const inf of infos) {
|
|
68
|
-
const name = decodeWideAtPtr(inf.pPrinterName)
|
|
69
|
-
names.push(name)
|
|
70
|
-
ports[name] = decodeWideAtPtr(inf.pPortName)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
t.section('GetDefaultPrinter')
|
|
74
|
-
const defW = WCHAR.alloc(260)
|
|
75
|
-
const lenB = new PtrArrayBuffer(4)
|
|
76
|
-
new Uint32Array(lenB)[0] = 260
|
|
77
|
-
t.checkTrue('GetDefaultPrinter ok', GetDefaultPrinter(defW, lenB) !== 0)
|
|
78
|
-
const defName = WCHAR.decode(defW)
|
|
79
|
-
t.checkTrue(`default in enum list (${defName})`, names.includes(defName))
|
|
80
|
-
|
|
81
|
-
t.section('DeviceCapabilities: numeric caps on default printer')
|
|
82
|
-
const defPort = ports[defName] ?? NULL
|
|
83
|
-
const horz = DeviceCapabilities(defName, defPort, DeviceCap.HORZRES, NULL, NULL)
|
|
84
|
-
t.checkTrue(`HORZRES sane (${horz})`, horz > 0)
|
|
85
|
-
const vert = DeviceCapabilities(defName, defPort, DeviceCap.VERTRES, NULL, NULL)
|
|
86
|
-
t.checkTrue(`VERTRES sane (${vert})`, vert > 0)
|
|
87
|
-
|
|
88
|
-
t.section('raw job chain on default printer (FILE: port -> .prn file, no UI)')
|
|
89
|
-
// 输出文件放系统 temp(GetTempPath 尾带反斜杠)——FILE: 端口把作业写到 pOutputFile
|
|
90
|
-
const tmpW = WCHAR.alloc(260)
|
|
91
|
-
GetTempPath(260, tmpW)
|
|
92
|
-
const outPrn = `${WCHAR.decode(tmpW)}qw_job.prn`
|
|
93
|
-
const slot = new PtrArrayBuffer(8)
|
|
94
|
-
t.checkTrue('OpenPrinter ok', OpenPrinter(defName, slot, NULL) !== 0)
|
|
95
|
-
const hPrinter = readPtr(new DataView(slot), 0)
|
|
96
|
-
t.checkTrue(`hPrinter valid (${hPrinter})`, hPrinter !== 0)
|
|
97
|
-
// pDatatype 省略(NULL)→ spooler 用打印机默认 datatype;显式传 RAW/XPS_PASS 反而
|
|
98
|
-
// 可能被驱动拒——1804 = ERROR_INVALID_DATATYPE(官方错误码表;注意 StartDocPrinter
|
|
99
|
-
// 不设置 last error,GetLastError 读到的常是残留值)
|
|
100
|
-
const jobId = StartDocPrinter(hPrinter, 1, {
|
|
101
|
-
pDocName: WCHAR.encode('QuickWin Raw Job').ptr, // <>ptr 字段收裸地址(number 子类型)
|
|
102
|
-
pOutputFile: WCHAR.encode(outPrn).ptr,
|
|
103
|
-
})
|
|
104
|
-
if (jobId === 0) {
|
|
105
|
-
// 失败即暴露(1804 = ERROR_INVALID_DATATYPE——datatype 显式 RAW/XPS_PASS 会被
|
|
106
|
-
// XPS 驱动拒,NULL 让 spooler 用默认即通;StartDocPrinter 不设置 last error,
|
|
107
|
-
// 下方诊断值常是残留)
|
|
108
|
-
t.checkTrue(`StartDocPrinter job id > 0 (lastError=${GetLastError()})`, false)
|
|
109
|
-
ClosePrinter(hPrinter)
|
|
110
|
-
return
|
|
111
|
-
}
|
|
112
|
-
t.checkTrue(`StartDocPrinter job id > 0`, jobId > 0)
|
|
113
|
-
const sp = StartPagePrinter(hPrinter)
|
|
114
|
-
t.checkTrue(`StartPagePrinter (lastError=${GetLastError()})`, sp !== 0)
|
|
115
|
-
const job = 'hello printer'
|
|
116
|
-
const jBuf = new PtrArrayBuffer(job.length)
|
|
117
|
-
const jDv = new DataView(jBuf)
|
|
118
|
-
for (let i = 0; i < job.length; i++) jDv.setUint8(i, job.charCodeAt(i))
|
|
119
|
-
const pWr = new PtrArrayBuffer(4)
|
|
120
|
-
const wr = WritePrinter(hPrinter, jBuf, job.length, pWr)
|
|
121
|
-
t.checkTrue(`WritePrinter ok (lastError=${GetLastError()})`, wr !== 0)
|
|
122
|
-
t.check('bytes written', job.length, new Uint32Array(pWr)[0])
|
|
123
|
-
const ep = EndPagePrinter(hPrinter)
|
|
124
|
-
t.checkTrue(`EndPagePrinter (lastError=${GetLastError()})`, ep !== 0)
|
|
125
|
-
const ed = EndDocPrinter(hPrinter)
|
|
126
|
-
t.checkTrue(`EndDocPrinter (lastError=${GetLastError()})`, ed !== 0)
|
|
127
|
-
t.checkTrue('ClosePrinter', ClosePrinter(hPrinter) !== 0)
|
|
128
|
-
|
|
129
|
-
// 作业落盘断言:FILE: 端口把 RAW 数据写到 pOutputFile
|
|
130
|
-
const hOut = CreateFile(outPrn, 0x80000000 /* GENERIC_READ */, 0, NULL,
|
|
131
|
-
FileCreation.OPEN_EXISTING, 0, NULL)
|
|
132
|
-
t.checkTrue(`job file exists (${outPrn})`, hOut !== -1)
|
|
133
|
-
if (hOut !== -1) {
|
|
134
|
-
const fSize = GetFileSize(hOut, NULL)
|
|
135
|
-
t.checkTrue(`job file size > 0 (${fSize})`, fSize > 0)
|
|
136
|
-
const fBuf = new PtrArrayBuffer(Math.max(fSize, 1))
|
|
137
|
-
const pRd = new PtrArrayBuffer(4)
|
|
138
|
-
ReadFile(hOut, fBuf, fSize, pRd, NULL)
|
|
139
|
-
// RAW 通道(winprint + RAW)应原样落盘——若 XPS 驱动重渲染则 FAIL 揭示实际内容头
|
|
140
|
-
let back = ''
|
|
141
|
-
const fDv = new DataView(fBuf)
|
|
142
|
-
for (let i = 0; i < Math.min(fSize, job.length); i++) back += String.fromCharCode(fDv.getUint8(i))
|
|
143
|
-
t.check('job data roundtrip (RAW passthrough)', job, back)
|
|
144
|
-
CloseHandle(hOut)
|
|
145
|
-
}
|
|
146
|
-
DeleteFile(outPrn)
|
|
147
|
-
},
|
|
148
|
-
}
|
package/test/test_proc.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { WCHAR } from '../lib/ffi/bind.js';
|
|
2
|
-
import { NULL, PtrArrayBuffer } from '../lib/ffi/ctype.js';
|
|
3
|
-
import { CloseHandle, CreateProcess, CreateToolhelp32Snapshot, GetCurrentProcessId, GetExitCodeProcess, GetLastError, GetModuleFileName, GetSystemDirectory, Module32First, Module32Next, OpenProcess, Process32First, Process32Next, Sleep, TerminateProcess, WaitForSingleObject, } from '../lib/windows/kernel32.js';
|
|
4
|
-
import { EnumProcesses, GetModuleFileNameEx, GetProcessMemoryInfo } from '../lib/windows/psapi.js';
|
|
5
|
-
import { MODULEENTRY32W, PROCESSENTRY32W, PROCESS_INFORMATION, PROCESS_MEMORY_COUNTERS, STARTUPINFOW, } from '../lib/windows/structs.js';
|
|
6
|
-
// STILL_ACTIVE:GetExitCodeProcess 对存活进程的保留返回值(杀进程退出码避开 259)
|
|
7
|
-
const STILL_ACTIVE = 259;
|
|
8
|
-
export const suite = {
|
|
9
|
-
name: 'proc',
|
|
10
|
-
run: (t) => {
|
|
11
|
-
const selfPid = GetCurrentProcessId();
|
|
12
|
-
t.checkTrue(`GetCurrentProcessId > 0 (${selfPid})`, selfPid > 0);
|
|
13
|
-
// PID 数组扫描(每次刷新:EnumProcesses 出参 needed 字节 /4 = PID 数)
|
|
14
|
-
const pidBuf = new PtrArrayBuffer(4096); // 1024 个 PID 槽
|
|
15
|
-
const needed = new PtrArrayBuffer(4);
|
|
16
|
-
const listPids = () => {
|
|
17
|
-
const out = new Set();
|
|
18
|
-
if (!EnumProcesses(pidBuf, 4096, needed))
|
|
19
|
-
return out;
|
|
20
|
-
const n = new Uint32Array(needed)[0] >>> 2; // 字节 /4 = 条数
|
|
21
|
-
const arr = new Uint32Array(pidBuf);
|
|
22
|
-
for (let i = 0; i < n; i++)
|
|
23
|
-
out.add(arr[i]);
|
|
24
|
-
return out;
|
|
25
|
-
};
|
|
26
|
-
t.section('EnumProcesses: pid array contains self');
|
|
27
|
-
t.checkTrue('EnumProcesses', EnumProcesses(pidBuf, 4096, needed) !== 0);
|
|
28
|
-
const nPid = new Uint32Array(needed)[0] >>> 2;
|
|
29
|
-
t.checkTrue(`pid count sane (${nPid})`, nPid > 0);
|
|
30
|
-
t.checkTrue('self pid present', listPids().has(selfPid));
|
|
31
|
-
t.section('toolhelp Process32 walk: self entry + exe name');
|
|
32
|
-
const snapP = CreateToolhelp32Snapshot(0x2 /* TH32CS_SNAPPROCESS */, 0);
|
|
33
|
-
t.checkTrue(`snapshot handle (${snapP})`, snapP > 0);
|
|
34
|
-
const pe = PROCESSENTRY32W.alloc();
|
|
35
|
-
new Uint32Array(pe)[0] = PROCESSENTRY32W.size; // 预写 dwSize(MSDN 要求)
|
|
36
|
-
let nProc = 0;
|
|
37
|
-
let selfName = '';
|
|
38
|
-
let parentOfSelf = -1;
|
|
39
|
-
let selfThreads = -1;
|
|
40
|
-
if (Process32First(snapP, pe.ptr)) {
|
|
41
|
-
do {
|
|
42
|
-
const e = PROCESSENTRY32W.decode(pe);
|
|
43
|
-
nProc++;
|
|
44
|
-
if (e.th32ProcessID === selfPid) {
|
|
45
|
-
selfName = e.szExeFile;
|
|
46
|
-
parentOfSelf = e.th32ParentProcessID;
|
|
47
|
-
selfThreads = e.cntThreads;
|
|
48
|
-
}
|
|
49
|
-
} while (Process32Next(snapP, pe.ptr));
|
|
50
|
-
}
|
|
51
|
-
const firstRd = PROCESSENTRY32W.decode(pe);
|
|
52
|
-
CloseHandle(snapP);
|
|
53
|
-
t.checkTrue(`process entries (${nProc}) >= 3`, nProc >= 3);
|
|
54
|
-
t.checkTrue('self found in walk', selfName.length > 0);
|
|
55
|
-
t.check('dwSize readback stays size', PROCESSENTRY32W.size, firstRd.dwSize);
|
|
56
|
-
t.checkTrue(`parent pid sane (${parentOfSelf})`, parentOfSelf > 0);
|
|
57
|
-
t.checkTrue(`cntThreads sane (${selfThreads})`, selfThreads >= 1);
|
|
58
|
-
// exe 名与 GetModuleFileName(NULL) 的 basename 互证(批 8 同源锚点)
|
|
59
|
-
const exeW = WCHAR.alloc(520);
|
|
60
|
-
GetModuleFileName(NULL, exeW, 520);
|
|
61
|
-
const exePath = WCHAR.decode(exeW);
|
|
62
|
-
const exeBase = exePath.slice(exePath.lastIndexOf('\\') + 1).toLowerCase();
|
|
63
|
-
t.check('szExeFile === basename(GetModuleFileName)', exeBase, selfName.toLowerCase());
|
|
64
|
-
t.section('OpenProcess + GetProcessMemoryInfo');
|
|
65
|
-
const hSelf = OpenProcess(0x400 /* QUERY_INFORMATION */ | 0x10 /* VM_READ */, 0, selfPid);
|
|
66
|
-
t.checkTrue(`OpenProcess(self) (${hSelf})`, hSelf !== NULL);
|
|
67
|
-
// 上行已判定非 NULL;断言进 ! 消费位(真 NULL → bind 运行时 throw 兜底)
|
|
68
|
-
const hQ = hSelf;
|
|
69
|
-
const pmc = PROCESS_MEMORY_COUNTERS.alloc();
|
|
70
|
-
new Uint32Array(pmc)[0] = PROCESS_MEMORY_COUNTERS.size; // 预写 cb
|
|
71
|
-
t.checkTrue('GetProcessMemoryInfo', GetProcessMemoryInfo(hQ, pmc.ptr, PROCESS_MEMORY_COUNTERS.size) !== 0);
|
|
72
|
-
const mem = PROCESS_MEMORY_COUNTERS.decode(pmc);
|
|
73
|
-
t.check('cb readback stays size', PROCESS_MEMORY_COUNTERS.size, mem.cb);
|
|
74
|
-
t.checkTrue(`WorkingSetSize > 0 (${mem.WorkingSetSize})`, mem.WorkingSetSize > 0);
|
|
75
|
-
t.checkTrue(`PeakWorkingSet >= WorkingSet (${mem.PeakWorkingSetSize})`, mem.PeakWorkingSetSize >= mem.WorkingSetSize);
|
|
76
|
-
t.checkTrue(`PagefileUsage > 0 (${mem.PagefileUsage})`, mem.PagefileUsage > 0);
|
|
77
|
-
t.checkTrue(`PageFaultCount >= 0 (${mem.PageFaultCount})`, mem.PageFaultCount >= 0);
|
|
78
|
-
const codeB = new PtrArrayBuffer(4);
|
|
79
|
-
t.checkTrue('GetExitCodeProcess(self)', GetExitCodeProcess(hQ, codeB) !== 0);
|
|
80
|
-
t.check('self is STILL_ACTIVE', STILL_ACTIVE, new Int32Array(codeB)[0]);
|
|
81
|
-
CloseHandle(hSelf);
|
|
82
|
-
t.checkTrue('OpenProcess(nonexistent pid) -> NULL', OpenProcess(0x400, 0, 0xfffffffe) === NULL);
|
|
83
|
-
t.section('toolhelp Module32 walk + GetModuleFileNameEx');
|
|
84
|
-
const snapM = CreateToolhelp32Snapshot(0x8 /* TH32CS_SNAPMODULE */, selfPid);
|
|
85
|
-
t.checkTrue(`module snapshot (${snapM})`, snapM > 0);
|
|
86
|
-
const me = MODULEENTRY32W.alloc();
|
|
87
|
-
new Uint32Array(me)[0] = MODULEENTRY32W.size; // 预写 dwSize
|
|
88
|
-
let modCount = 0;
|
|
89
|
-
let k32Base = 0;
|
|
90
|
-
let hasK32 = false;
|
|
91
|
-
if (Module32First(snapM, me.ptr)) {
|
|
92
|
-
do {
|
|
93
|
-
const m = MODULEENTRY32W.decode(me);
|
|
94
|
-
modCount++;
|
|
95
|
-
if (m.szModule.toLowerCase() === 'kernel32.dll') {
|
|
96
|
-
hasK32 = true;
|
|
97
|
-
k32Base = m.modBaseAddr;
|
|
98
|
-
}
|
|
99
|
-
} while (Module32Next(snapM, me.ptr));
|
|
100
|
-
}
|
|
101
|
-
CloseHandle(snapM);
|
|
102
|
-
t.checkTrue(`module count (${modCount}) > 0`, modCount > 0);
|
|
103
|
-
t.checkTrue('kernel32.dll in module walk + base sane', hasK32 && k32Base > 0x10000);
|
|
104
|
-
// psapi 主模块路径 === kernel32 GetModuleFileName(NULL)(同进程同源互证)
|
|
105
|
-
const hSelf2 = OpenProcess(0x400 | 0x10, 0, selfPid);
|
|
106
|
-
t.checkTrue(`OpenProcess(self) second (${hSelf2})`, hSelf2 !== NULL);
|
|
107
|
-
const exW = WCHAR.alloc(520);
|
|
108
|
-
const exLen = GetModuleFileNameEx(hSelf2, NULL, exW, 520);
|
|
109
|
-
CloseHandle(hSelf2);
|
|
110
|
-
t.checkTrue(`GetModuleFileNameEx len (${exLen})`, exLen > 0);
|
|
111
|
-
t.check('exe path psapi === kernel32', exePath.toLowerCase(), WCHAR.decode(exW).toLowerCase());
|
|
112
|
-
t.section('CreateProcess -> pid visible -> TerminateProcess');
|
|
113
|
-
const sysW = WCHAR.alloc(300);
|
|
114
|
-
GetSystemDirectory(sysW, 300); // 尾不带反斜杠
|
|
115
|
-
// 首 token 全路径(worker 探针:裸名 PATH 解析在本环境失败 err 267/123)
|
|
116
|
-
const cmdline = `${WCHAR.decode(sysW)}\\ping.exe -n 60 127.0.0.1`;
|
|
117
|
-
const pi = PROCESS_INFORMATION.alloc();
|
|
118
|
-
const created = CreateProcess(NULL, cmdline, NULL, NULL, 0, 0x08000000 /* CREATE_NO_WINDOW */, NULL, NULL, { cb: STARTUPINFOW.size }, pi.ptr);
|
|
119
|
-
t.checkTrue(`CreateProcess(ping) ${created ? '' : `err=${GetLastError()}`}`, created !== 0);
|
|
120
|
-
if (created) {
|
|
121
|
-
const { hProcess, hThread, dwProcessId, dwThreadId } = PROCESS_INFORMATION.decode(pi);
|
|
122
|
-
t.checkTrue(`child pid (${dwProcessId}) valid`, dwProcessId > 0 && dwProcessId !== selfPid);
|
|
123
|
-
t.checkTrue(`child tid sane (${dwThreadId})`, dwThreadId > 0);
|
|
124
|
-
t.checkTrue('child pid in EnumProcesses', listPids().has(dwProcessId));
|
|
125
|
-
const codeB2 = new PtrArrayBuffer(4);
|
|
126
|
-
GetExitCodeProcess(hProcess, codeB2);
|
|
127
|
-
t.check('child alive (STILL_ACTIVE)', STILL_ACTIVE, new Int32Array(codeB2)[0]);
|
|
128
|
-
// XP 怪癖:进程初始化未完成时 TerminateProcess,exit code 不保留读回 0
|
|
129
|
-
// (win7 立即杀读 42 正常;探针实测 XP 立即杀=0、等 3s 后杀=42)——先等初始化
|
|
130
|
-
Sleep(1000);
|
|
131
|
-
t.checkTrue('TerminateProcess', TerminateProcess(hProcess, 42) !== 0);
|
|
132
|
-
const waited = WaitForSingleObject(hProcess, 5000);
|
|
133
|
-
t.checkTrue(`child exited (wait=${waited})`, waited === 0 /* WAIT_OBJECT_0 */);
|
|
134
|
-
GetExitCodeProcess(hProcess, codeB2);
|
|
135
|
-
t.check('child exit code 42', 42, new Int32Array(codeB2)[0]);
|
|
136
|
-
t.checkTrue('child pid gone', !listPids().has(dwProcessId));
|
|
137
|
-
CloseHandle(hProcess);
|
|
138
|
-
CloseHandle(hThread);
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
};
|
package/test/test_proc.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { WCHAR } from '../lib/ffi/bind.js'
|
|
2
|
-
import { NULL, PtrArrayBuffer, type Ptr } from '../lib/ffi/ctype.js'
|
|
3
|
-
import {
|
|
4
|
-
CloseHandle, CreateProcess, CreateToolhelp32Snapshot, GetCurrentProcessId,
|
|
5
|
-
GetExitCodeProcess, GetLastError, GetModuleFileName, GetSystemDirectory,
|
|
6
|
-
Module32First, Module32Next, OpenProcess, Process32First, Process32Next, Sleep,
|
|
7
|
-
TerminateProcess, WaitForSingleObject,
|
|
8
|
-
} from '../lib/windows/kernel32.js'
|
|
9
|
-
import { EnumProcesses, GetModuleFileNameEx, GetProcessMemoryInfo } from '../lib/windows/psapi.js'
|
|
10
|
-
import {
|
|
11
|
-
MODULEENTRY32W, PROCESSENTRY32W, PROCESS_INFORMATION,
|
|
12
|
-
PROCESS_MEMORY_COUNTERS, STARTUPINFOW,
|
|
13
|
-
} from '../lib/windows/structs.js'
|
|
14
|
-
import { Tester } from './test_helper.js'
|
|
15
|
-
|
|
16
|
-
// STILL_ACTIVE:GetExitCodeProcess 对存活进程的保留返回值(杀进程退出码避开 259)
|
|
17
|
-
const STILL_ACTIVE = 259
|
|
18
|
-
|
|
19
|
-
export const suite = {
|
|
20
|
-
name: 'proc',
|
|
21
|
-
run: (t: Tester) => {
|
|
22
|
-
const selfPid = GetCurrentProcessId()
|
|
23
|
-
t.checkTrue(`GetCurrentProcessId > 0 (${selfPid})`, selfPid > 0)
|
|
24
|
-
|
|
25
|
-
// PID 数组扫描(每次刷新:EnumProcesses 出参 needed 字节 /4 = PID 数)
|
|
26
|
-
const pidBuf = new PtrArrayBuffer(4096) // 1024 个 PID 槽
|
|
27
|
-
const needed = new PtrArrayBuffer(4)
|
|
28
|
-
const listPids = (): Set<number> => {
|
|
29
|
-
const out = new Set<number>()
|
|
30
|
-
if (!EnumProcesses(pidBuf, 4096, needed)) return out
|
|
31
|
-
const n = new Uint32Array(needed)[0]! >>> 2 // 字节 /4 = 条数
|
|
32
|
-
const arr = new Uint32Array(pidBuf)
|
|
33
|
-
for (let i = 0; i < n; i++) out.add(arr[i]!)
|
|
34
|
-
return out
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
t.section('EnumProcesses: pid array contains self')
|
|
38
|
-
t.checkTrue('EnumProcesses', EnumProcesses(pidBuf, 4096, needed) !== 0)
|
|
39
|
-
const nPid = new Uint32Array(needed)[0]! >>> 2
|
|
40
|
-
t.checkTrue(`pid count sane (${nPid})`, nPid > 0)
|
|
41
|
-
t.checkTrue('self pid present', listPids().has(selfPid))
|
|
42
|
-
|
|
43
|
-
t.section('toolhelp Process32 walk: self entry + exe name')
|
|
44
|
-
const snapP = CreateToolhelp32Snapshot(0x2 /* TH32CS_SNAPPROCESS */, 0)
|
|
45
|
-
t.checkTrue(`snapshot handle (${snapP})`, snapP > 0)
|
|
46
|
-
const pe = PROCESSENTRY32W.alloc()
|
|
47
|
-
new Uint32Array(pe)[0] = PROCESSENTRY32W.size // 预写 dwSize(MSDN 要求)
|
|
48
|
-
let nProc = 0
|
|
49
|
-
let selfName = ''
|
|
50
|
-
let parentOfSelf = -1
|
|
51
|
-
let selfThreads = -1
|
|
52
|
-
if (Process32First(snapP, pe.ptr)) {
|
|
53
|
-
do {
|
|
54
|
-
const e = PROCESSENTRY32W.decode(pe)
|
|
55
|
-
nProc++
|
|
56
|
-
if (e.th32ProcessID === selfPid) {
|
|
57
|
-
selfName = e.szExeFile
|
|
58
|
-
parentOfSelf = e.th32ParentProcessID
|
|
59
|
-
selfThreads = e.cntThreads
|
|
60
|
-
}
|
|
61
|
-
} while (Process32Next(snapP, pe.ptr))
|
|
62
|
-
}
|
|
63
|
-
const firstRd = PROCESSENTRY32W.decode(pe)
|
|
64
|
-
CloseHandle(snapP)
|
|
65
|
-
t.checkTrue(`process entries (${nProc}) >= 3`, nProc >= 3)
|
|
66
|
-
t.checkTrue('self found in walk', selfName.length > 0)
|
|
67
|
-
t.check('dwSize readback stays size', PROCESSENTRY32W.size, firstRd.dwSize)
|
|
68
|
-
t.checkTrue(`parent pid sane (${parentOfSelf})`, parentOfSelf > 0)
|
|
69
|
-
t.checkTrue(`cntThreads sane (${selfThreads})`, selfThreads >= 1)
|
|
70
|
-
// exe 名与 GetModuleFileName(NULL) 的 basename 互证(批 8 同源锚点)
|
|
71
|
-
const exeW = WCHAR.alloc(520)
|
|
72
|
-
GetModuleFileName(NULL, exeW, 520)
|
|
73
|
-
const exePath = WCHAR.decode(exeW)
|
|
74
|
-
const exeBase = exePath.slice(exePath.lastIndexOf('\\') + 1).toLowerCase()
|
|
75
|
-
t.check('szExeFile === basename(GetModuleFileName)', exeBase, selfName.toLowerCase())
|
|
76
|
-
|
|
77
|
-
t.section('OpenProcess + GetProcessMemoryInfo')
|
|
78
|
-
const hSelf = OpenProcess(0x400 /* QUERY_INFORMATION */ | 0x10 /* VM_READ */, 0, selfPid)
|
|
79
|
-
t.checkTrue(`OpenProcess(self) (${hSelf})`, hSelf !== NULL)
|
|
80
|
-
// 上行已判定非 NULL;断言进 ! 消费位(真 NULL → bind 运行时 throw 兜底)
|
|
81
|
-
const hQ = hSelf as Ptr<'HPROCESS'>
|
|
82
|
-
const pmc = PROCESS_MEMORY_COUNTERS.alloc()
|
|
83
|
-
new Uint32Array(pmc)[0] = PROCESS_MEMORY_COUNTERS.size // 预写 cb
|
|
84
|
-
t.checkTrue('GetProcessMemoryInfo',
|
|
85
|
-
GetProcessMemoryInfo(hQ, pmc.ptr, PROCESS_MEMORY_COUNTERS.size) !== 0)
|
|
86
|
-
const mem = PROCESS_MEMORY_COUNTERS.decode(pmc)
|
|
87
|
-
t.check('cb readback stays size', PROCESS_MEMORY_COUNTERS.size, mem.cb)
|
|
88
|
-
t.checkTrue(`WorkingSetSize > 0 (${mem.WorkingSetSize})`, mem.WorkingSetSize > 0)
|
|
89
|
-
t.checkTrue(`PeakWorkingSet >= WorkingSet (${mem.PeakWorkingSetSize})`,
|
|
90
|
-
mem.PeakWorkingSetSize >= mem.WorkingSetSize)
|
|
91
|
-
t.checkTrue(`PagefileUsage > 0 (${mem.PagefileUsage})`, mem.PagefileUsage > 0)
|
|
92
|
-
t.checkTrue(`PageFaultCount >= 0 (${mem.PageFaultCount})`, mem.PageFaultCount >= 0)
|
|
93
|
-
const codeB = new PtrArrayBuffer(4)
|
|
94
|
-
t.checkTrue('GetExitCodeProcess(self)', GetExitCodeProcess(hQ, codeB) !== 0)
|
|
95
|
-
t.check('self is STILL_ACTIVE', STILL_ACTIVE, new Int32Array(codeB)[0])
|
|
96
|
-
CloseHandle(hSelf)
|
|
97
|
-
t.checkTrue('OpenProcess(nonexistent pid) -> NULL',
|
|
98
|
-
OpenProcess(0x400, 0, 0xfffffffe) === NULL)
|
|
99
|
-
|
|
100
|
-
t.section('toolhelp Module32 walk + GetModuleFileNameEx')
|
|
101
|
-
const snapM = CreateToolhelp32Snapshot(0x8 /* TH32CS_SNAPMODULE */, selfPid)
|
|
102
|
-
t.checkTrue(`module snapshot (${snapM})`, snapM > 0)
|
|
103
|
-
const me = MODULEENTRY32W.alloc()
|
|
104
|
-
new Uint32Array(me)[0] = MODULEENTRY32W.size // 预写 dwSize
|
|
105
|
-
let modCount = 0
|
|
106
|
-
let k32Base = 0
|
|
107
|
-
let hasK32 = false
|
|
108
|
-
if (Module32First(snapM, me.ptr)) {
|
|
109
|
-
do {
|
|
110
|
-
const m = MODULEENTRY32W.decode(me)
|
|
111
|
-
modCount++
|
|
112
|
-
if (m.szModule.toLowerCase() === 'kernel32.dll') {
|
|
113
|
-
hasK32 = true
|
|
114
|
-
k32Base = m.modBaseAddr
|
|
115
|
-
}
|
|
116
|
-
} while (Module32Next(snapM, me.ptr))
|
|
117
|
-
}
|
|
118
|
-
CloseHandle(snapM)
|
|
119
|
-
t.checkTrue(`module count (${modCount}) > 0`, modCount > 0)
|
|
120
|
-
t.checkTrue('kernel32.dll in module walk + base sane', hasK32 && k32Base > 0x10000)
|
|
121
|
-
// psapi 主模块路径 === kernel32 GetModuleFileName(NULL)(同进程同源互证)
|
|
122
|
-
const hSelf2 = OpenProcess(0x400 | 0x10, 0, selfPid)
|
|
123
|
-
t.checkTrue(`OpenProcess(self) second (${hSelf2})`, hSelf2 !== NULL)
|
|
124
|
-
const exW = WCHAR.alloc(520)
|
|
125
|
-
const exLen = GetModuleFileNameEx(hSelf2 as Ptr<'HPROCESS'>, NULL, exW, 520)
|
|
126
|
-
CloseHandle(hSelf2)
|
|
127
|
-
t.checkTrue(`GetModuleFileNameEx len (${exLen})`, exLen > 0)
|
|
128
|
-
t.check('exe path psapi === kernel32',
|
|
129
|
-
exePath.toLowerCase(), WCHAR.decode(exW).toLowerCase())
|
|
130
|
-
|
|
131
|
-
t.section('CreateProcess -> pid visible -> TerminateProcess')
|
|
132
|
-
const sysW = WCHAR.alloc(300)
|
|
133
|
-
GetSystemDirectory(sysW, 300) // 尾不带反斜杠
|
|
134
|
-
// 首 token 全路径(worker 探针:裸名 PATH 解析在本环境失败 err 267/123)
|
|
135
|
-
const cmdline = `${WCHAR.decode(sysW)}\\ping.exe -n 60 127.0.0.1`
|
|
136
|
-
const pi = PROCESS_INFORMATION.alloc()
|
|
137
|
-
const created = CreateProcess(NULL, cmdline, NULL, NULL, 0,
|
|
138
|
-
0x08000000 /* CREATE_NO_WINDOW */, NULL, NULL,
|
|
139
|
-
{ cb: STARTUPINFOW.size }, pi.ptr)
|
|
140
|
-
t.checkTrue(`CreateProcess(ping) ${created ? '' : `err=${GetLastError()}`}`, created !== 0)
|
|
141
|
-
if (created) {
|
|
142
|
-
const { hProcess, hThread, dwProcessId, dwThreadId } = PROCESS_INFORMATION.decode(pi)
|
|
143
|
-
t.checkTrue(`child pid (${dwProcessId}) valid`,
|
|
144
|
-
dwProcessId > 0 && dwProcessId !== selfPid)
|
|
145
|
-
t.checkTrue(`child tid sane (${dwThreadId})`, dwThreadId > 0)
|
|
146
|
-
t.checkTrue('child pid in EnumProcesses', listPids().has(dwProcessId))
|
|
147
|
-
const codeB2 = new PtrArrayBuffer(4)
|
|
148
|
-
GetExitCodeProcess(hProcess, codeB2)
|
|
149
|
-
t.check('child alive (STILL_ACTIVE)', STILL_ACTIVE, new Int32Array(codeB2)[0])
|
|
150
|
-
// XP 怪癖:进程初始化未完成时 TerminateProcess,exit code 不保留读回 0
|
|
151
|
-
// (win7 立即杀读 42 正常;探针实测 XP 立即杀=0、等 3s 后杀=42)——先等初始化
|
|
152
|
-
Sleep(1000)
|
|
153
|
-
t.checkTrue('TerminateProcess', TerminateProcess(hProcess, 42) !== 0)
|
|
154
|
-
const waited = WaitForSingleObject(hProcess, 5000)
|
|
155
|
-
t.checkTrue(`child exited (wait=${waited})`, waited === 0 /* WAIT_OBJECT_0 */)
|
|
156
|
-
GetExitCodeProcess(hProcess, codeB2)
|
|
157
|
-
t.check('child exit code 42', 42, new Int32Array(codeB2)[0])
|
|
158
|
-
t.checkTrue('child pid gone', !listPids().has(dwProcessId))
|
|
159
|
-
CloseHandle(hProcess)
|
|
160
|
-
CloseHandle(hThread)
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
}
|