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.
Files changed (163) hide show
  1. package/README.en.md +3 -0
  2. package/README.md +2 -0
  3. package/package.json +1 -3
  4. package/qwin-nowasm-x86.exe +0 -0
  5. package/qwin-nowasm.exe +0 -0
  6. package/qwin-x86.exe +0 -0
  7. package/qwin.exe +0 -0
  8. package/examples/PdfCanvas.js +0 -38
  9. package/examples/PdfCanvas.tsx +0 -54
  10. package/examples/execd/main.js +0 -23379
  11. package/examples/execd/tray.js +0 -192
  12. package/examples/execd/worker.js +0 -122
  13. package/examples/pdf_preview2.js +0 -398
  14. package/examples/pdf_preview2.ts +0 -353
  15. package/examples/pdf_viewer.js +0 -22515
  16. package/examples/pdf_viewer.tsx +0 -228
  17. package/examples/test_gallery.js +0 -18692
  18. package/examples/test_gallery.tsx +0 -291
  19. package/examples/test_react_lazy.js +0 -18560
  20. package/examples/test_react_lazy.tsx +0 -65
  21. package/examples/test_react_listbox.js +0 -18562
  22. package/examples/test_react_listbox.tsx +0 -73
  23. package/examples/test_root_window.js +0 -18532
  24. package/examples/test_root_window.tsx +0 -41
  25. package/examples/test_tab.js +0 -18563
  26. package/examples/test_tab.tsx +0 -77
  27. package/examples/tray_demo.js +0 -86
  28. package/examples/tray_demo.tsx +0 -89
  29. package/test/add.wasm +0 -0
  30. package/test/bench_ffi_callraw.js +0 -235
  31. package/test/bench_ffi_callraw.ts +0 -235
  32. package/test/bench_ffi_overhead.js +0 -139
  33. package/test/bench_ffi_overhead.ts +0 -141
  34. package/test/bench_sendmessage.js +0 -104
  35. package/test/bench_sendmessage.ts +0 -100
  36. package/test/bench_sendmessage_nonnull.js +0 -187
  37. package/test/bench_sendmessage_nonnull.ts +0 -188
  38. package/test/complex.wasm +0 -0
  39. package/test/complex_imports.wasm +0 -0
  40. package/test/debug/http_server_e2e.js +0 -31
  41. package/test/debug/minwasm.js +0 -52
  42. package/test/debug/mupdf_debug.js +0 -86
  43. package/test/debug/wasm.js +0 -122
  44. package/test/debug/wasm_export_debug.js +0 -54
  45. package/test/debug/wasm_names.js +0 -36
  46. package/test/example.pdf +0 -0
  47. package/test/global_imports.wasm +0 -0
  48. package/test/import_func.wasm +0 -0
  49. package/test/imports.wasm +0 -0
  50. package/test/mupdf-cdn.js +0 -40
  51. package/test/mupdf-cdn.ts +0 -43
  52. package/test/run.js +0 -111
  53. package/test/run.ts +0 -116
  54. package/test/sjlj.wasm +0 -0
  55. package/test/test_basic.js +0 -7
  56. package/test/test_basic.ts +0 -9
  57. package/test/test_basic_fetch.js +0 -63
  58. package/test/test_basic_fetch.ts +0 -59
  59. package/test/test_brotli.js +0 -48
  60. package/test/test_brotli.ts +0 -52
  61. package/test/test_fetch_cache.js +0 -169
  62. package/test/test_fetch_cache.ts +0 -187
  63. package/test/test_ffi.js +0 -157
  64. package/test/test_ffi.ts +0 -170
  65. package/test/test_ffi_abi.js +0 -290
  66. package/test/test_ffi_abi.ts +0 -274
  67. package/test/test_ffi_array.js +0 -116
  68. package/test/test_ffi_array.ts +0 -141
  69. package/test/test_ffi_bind.js +0 -656
  70. package/test/test_ffi_bind.ts +0 -670
  71. package/test/test_ffi_struct.js +0 -779
  72. package/test/test_ffi_struct.ts +0 -828
  73. package/test/test_ffi_struct_layout.js +0 -495
  74. package/test/test_ffi_struct_layout.ts +0 -543
  75. package/test/test_ffi_user32.js +0 -103
  76. package/test/test_ffi_user32.ts +0 -113
  77. package/test/test_ffi_windows.js +0 -172
  78. package/test/test_ffi_windows.ts +0 -187
  79. package/test/test_files.js +0 -102
  80. package/test/test_files.ts +0 -121
  81. package/test/test_frame_encoding.js +0 -128
  82. package/test/test_frame_encoding.ts +0 -132
  83. package/test/test_gdi_draw.js +0 -681
  84. package/test/test_gdi_draw.ts +0 -725
  85. package/test/test_helper.js +0 -91
  86. package/test/test_helper.ts +0 -88
  87. package/test/test_http_import.js +0 -96
  88. package/test/test_http_import.ts +0 -93
  89. package/test/test_http_server.js +0 -214
  90. package/test/test_http_server.ts +0 -211
  91. package/test/test_import_dir.js +0 -4
  92. package/test/test_import_dir.ts +0 -5
  93. package/test/test_ipv6.js +0 -134
  94. package/test/test_ipv6.ts +0 -130
  95. package/test/test_jsx.js +0 -1314
  96. package/test/test_meta_url.js +0 -3
  97. package/test/test_meta_url.ts +0 -4
  98. package/test/test_mupdf_render.js +0 -51
  99. package/test/test_mupdf_render.ts +0 -55
  100. package/test/test_mupdf_twice.js +0 -62
  101. package/test/test_mupdf_twice.ts +0 -65
  102. package/test/test_mupdf_wasm.js +0 -39
  103. package/test/test_mupdf_wasm.ts +0 -32
  104. package/test/test_net_event.js +0 -57
  105. package/test/test_net_event.ts +0 -56
  106. package/test/test_net_fetch.js +0 -384
  107. package/test/test_net_fetch.ts +0 -373
  108. package/test/test_net_websocket.js +0 -180
  109. package/test/test_net_websocket.ts +0 -164
  110. package/test/test_polyfill.js +0 -144
  111. package/test/test_polyfill.ts +0 -159
  112. package/test/test_printer.js +0 -137
  113. package/test/test_printer.ts +0 -148
  114. package/test/test_proc.js +0 -141
  115. package/test/test_proc.ts +0 -163
  116. package/test/test_react_complex.js +0 -18678
  117. package/test/test_react_counter.js +0 -18555
  118. package/test/test_react_flex.js +0 -18871
  119. package/test/test_react_hidden.js +0 -18560
  120. package/test/test_react_listview_link.js +0 -18590
  121. package/test/test_react_notify.js +0 -18797
  122. package/test/test_react_render.js +0 -18550
  123. package/test/test_registry.js +0 -74
  124. package/test/test_registry.ts +0 -95
  125. package/test/test_sysinfo.js +0 -122
  126. package/test/test_sysinfo.ts +0 -141
  127. package/test/test_url.js +0 -173
  128. package/test/test_url.ts +0 -183
  129. package/test/test_wasm_basic.js +0 -85
  130. package/test/test_wasm_basic.ts +0 -73
  131. package/test/test_wasm_import_global.js +0 -41
  132. package/test/test_wasm_import_global.ts +0 -45
  133. package/test/test_wasm_sjlj.js +0 -145
  134. package/test/test_wasm_sjlj.ts +0 -125
  135. package/test/test_wasm_types.js +0 -97
  136. package/test/test_wasm_types.ts +0 -124
  137. package/test/test_wolfssl_read.js +0 -203
  138. package/test/test_wolfssl_read.ts +0 -184
  139. package/test/test_worker.js +0 -21
  140. package/test/test_worker.ts +0 -24
  141. package/test/test_worker_concurrent.js +0 -45
  142. package/test/test_worker_concurrent.ts +0 -56
  143. package/test/test_worker_data_url.js +0 -46
  144. package/test/test_worker_data_url.ts +0 -51
  145. package/test/test_worker_fetch.js +0 -26
  146. package/test/test_worker_fetch.ts +0 -27
  147. package/test/test_worker_net.js +0 -69
  148. package/test/test_worker_net.ts +0 -66
  149. package/test/test_worker_wasm_concurrent.js +0 -44
  150. package/test/test_worker_wasm_concurrent.ts +0 -45
  151. package/test/types.wasm +0 -0
  152. package/test/worker_fetch.js +0 -13
  153. package/test/worker_fetch.ts +0 -13
  154. package/test/worker_helper.js +0 -1
  155. package/test/worker_helper.ts +0 -1
  156. package/test/worker_http.js +0 -18
  157. package/test/worker_http.ts +0 -20
  158. package/test/worker_import_lib.js +0 -12
  159. package/test/worker_import_lib.ts +0 -13
  160. package/test/worker_module.js +0 -16
  161. package/test/worker_module.ts +0 -17
  162. package/test/worker_wasm_concurrent.js +0 -22
  163. package/test/worker_wasm_concurrent.ts +0 -22
@@ -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
- }