quickwin 2.0.0 → 2.1.0
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 +17 -22
- package/README.md +1 -3
- package/examples/exec_server.js +133 -29
- package/examples/exec_server_worker.js +1 -1
- package/examples/exec_server_worker.ts +2 -2
- package/examples/pdf_preview2.js +1 -1
- package/examples/pdf_preview2.ts +1 -1
- package/examples/pdf_viewer.js +132 -28
- package/examples/pdf_viewer.tsx +1 -1
- package/examples/test_gallery.js +131 -27
- package/examples/test_react_lazy.js +131 -27
- package/examples/test_react_listbox.js +131 -27
- package/examples/test_root_window.js +131 -27
- package/examples/test_tab.js +131 -27
- package/lib/certs.js +13 -8
- package/lib/certs.ts +13 -8
- package/lib/fetch.js +51 -3
- package/lib/fetch.ts +38 -3
- package/lib/ffi/bind.js +94 -5
- package/lib/ffi/bind.ts +107 -10
- package/lib/ffi/ctype.js +33 -7
- package/lib/ffi/ctype.ts +48 -10
- package/lib/ffi/struct.js +92 -14
- package/lib/ffi/struct.ts +108 -19
- package/lib/react-qw/components/ListView.js +1 -0
- package/lib/react-qw/components/PathPicker.js +2 -2
- package/lib/react-qw/index.js +1 -1
- package/lib/react-qw/index.ts +4 -1
- package/lib/text-measure.js +3 -1
- package/lib/text-measure.ts +4 -3
- package/lib/websocket.js +32 -2
- package/lib/websocket.ts +25 -2
- package/lib/windows/advapi32.js +53 -0
- package/lib/windows/advapi32.ts +64 -0
- package/lib/windows/comctl32.js +15 -15
- package/lib/windows/comctl32.ts +15 -16
- package/lib/windows/comdlg32.js +3 -2
- package/lib/windows/comdlg32.ts +3 -2
- package/lib/windows/gdi32.js +328 -26
- package/lib/windows/gdi32.ts +349 -26
- package/lib/windows/kernel32.js +206 -10
- package/lib/windows/kernel32.ts +225 -10
- package/lib/windows/msimg32.js +38 -0
- package/lib/windows/msimg32.ts +45 -0
- package/lib/windows/ntdll.js +3 -2
- package/lib/windows/ntdll.ts +3 -2
- package/lib/windows/ole32.js +1 -1
- package/lib/windows/ole32.ts +1 -1
- package/lib/windows/psapi.js +32 -0
- package/lib/windows/psapi.ts +37 -0
- package/lib/windows/shell32.js +5 -3
- package/lib/windows/shell32.ts +5 -3
- package/lib/windows/structs.js +135 -0
- package/lib/windows/structs.ts +144 -0
- package/lib/windows/user32.js +202 -99
- package/lib/windows/user32.ts +204 -99
- package/lib/windows/version.js +29 -0
- package/lib/windows/version.ts +35 -0
- package/lib/windows/winspool.js +71 -0
- package/lib/windows/winspool.ts +80 -0
- package/package.json +1 -1
- package/quickwin.d.ts +11 -5
- package/quickwin_const.d.ts +136 -0
- 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/test/run.js +7 -0
- package/test/run.ts +7 -0
- package/test/test_basic_fetch.js +21 -0
- package/test/test_basic_fetch.ts +17 -0
- package/test/test_ffi.js +6 -6
- package/test/test_ffi.ts +6 -6
- package/test/test_ffi_abi.js +55 -0
- package/test/test_ffi_abi.ts +56 -0
- package/test/test_ffi_array.js +116 -0
- package/test/test_ffi_array.ts +141 -0
- package/test/test_ffi_bind.js +82 -0
- package/test/test_ffi_bind.ts +71 -0
- package/test/test_files.js +102 -0
- package/test/test_files.ts +121 -0
- package/test/test_gdi_draw.js +681 -0
- package/test/test_gdi_draw.ts +725 -0
- package/test/test_net_fetch.js +4 -3
- package/test/test_net_fetch.ts +4 -3
- package/test/test_printer.js +137 -0
- package/test/test_printer.ts +148 -0
- package/test/test_proc.js +141 -0
- package/test/test_proc.ts +163 -0
- package/test/test_react_complex.js +131 -27
- package/test/test_react_counter.js +131 -27
- package/test/test_react_flex.js +131 -27
- package/test/test_react_hidden.js +131 -27
- package/test/test_react_listview_link.js +131 -27
- package/test/test_react_notify.js +131 -27
- package/test/test_react_render.js +131 -27
- package/test/test_registry.js +74 -0
- package/test/test_registry.ts +95 -0
- package/test/test_sysinfo.js +122 -0
- package/test/test_sysinfo.ts +141 -0
- package/test/test_wolfssl_read.js +45 -44
- package/test/test_wolfssl_read.ts +42 -42
package/lib/ffi/ctype.ts
CHANGED
|
@@ -29,28 +29,53 @@ const C_BASIC_TYPE = [
|
|
|
29
29
|
export type C_BasicType = (typeof C_BASIC_TYPE)[number]
|
|
30
30
|
export type C_BasicType_No_Void = Exclude<C_BasicType, 'void'>
|
|
31
31
|
|
|
32
|
-
// '!' 形随 token 走(normToken 只剥运行时不理解的 '@',运行时要执法的 '!'
|
|
33
|
-
// token 即契约,callPacked
|
|
34
|
-
|
|
32
|
+
// '!' / '*' 形随 token 走(normToken 只剥运行时不理解的 '@',运行时要执法的 '!'(0 检查)
|
|
33
|
+
// 与 '*'(数组分派)保留在 token 上——token 即契约,callPacked 按尾缀执法,无独立 flag
|
|
34
|
+
// 可丢)。'<N*>ptr' 数组位无需新词形:品牌段含 '*',整 token 仍在 <${string}>ptr 模板内。
|
|
35
|
+
export type C_BasicType_Token = Exclude<C_BasicType, 'ptr'> | `<${string}>ptr` | `<${string}>ptr!` | `<${string}>`
|
|
35
36
|
export type C_BasicType_Token_No_Void = Exclude<C_BasicType_Token, 'void'>
|
|
36
37
|
|
|
37
38
|
// 尾缀修饰:'!' = 非空担保(见 Arg/RetJsTypeOfToken 的 ! 分支与 callPacked 的运行时 0 检查),
|
|
38
39
|
// '@Name' = 枚举值域标注(类型层启用前先由 Norm 归一剥掉)。两者语义正交但作用面不相交
|
|
39
40
|
// (! 主品牌位、@ 主裸档整数位,@ 枚举不含 0 时已蕴含 !),故每 token 至多一个修饰。
|
|
40
41
|
// 组 1 恒为名字(归一名),组 2 为修饰;槽宽/ABI 只看归一形式。
|
|
41
|
-
|
|
42
|
+
// 名字形收紧为标识符([A-Za-z_]\w*)∪ 空串(<>ptr 裸地址档),尾部可挂数组字形:
|
|
43
|
+
// '*' = 零或多(<N*>ptr 入参连续数组位,元素形 = 单对象位 codec)。其余非标识符
|
|
44
|
+
// 字符('+'/'-'/'1' 开头等)不进词法 —— 与 <1abc>ptr/<X-y>ptr 同策略:正则不匹配、
|
|
45
|
+
// 绑定时落通用 Unknown token,类型层不设专属禁分支(运行时兜底,无静默通过)。旧版
|
|
46
|
+
// ([^<>\s]*) 通配曾使 <FOO+>ptr 被静默当品牌 'FOO+' 接受——语义字形必须结构可见。
|
|
47
|
+
const PTR_TOKEN_RE = /^<([A-Za-z_]\w*|)>ptr(!|@[A-Za-z_]\w*)?$/
|
|
42
48
|
|
|
43
49
|
export function isCPtrToken(t: string): t is `<${string}>ptr` | `${string}ptr!` | `${string}ptr@${string}` { return PTR_TOKEN_RE.test(t) }
|
|
50
|
+
|
|
51
|
+
// '<N>' 裸品牌形 = 按值传参位(<N>ptr 传地址 / <N> 传值)。名字形同指针形(标识符,
|
|
52
|
+
// 空名 <> 仅 ptr 档有),不带任何修饰 —— 按值位无 '!'(值不为 NULL)、无 '@'(值域由
|
|
53
|
+
// codec 推导、不标枚举)、无 '*'(数组走 structArray encoder 的值域):含修饰的形
|
|
54
|
+
// 正则不匹配,落绑定时通用 Unknown token(与 '<X+>ptr' 同策略,运行时兜底)。
|
|
55
|
+
const VAL_TOKEN_RE = /^<([A-Za-z_]\w*)>$/
|
|
56
|
+
|
|
57
|
+
export function isCValToken(t: string): t is `<${string}>` { return VAL_TOKEN_RE.test(t) }
|
|
58
|
+
export function valName<const N extends string>(t: `<${N}>`): N {
|
|
59
|
+
const m = VAL_TOKEN_RE.exec(t)
|
|
60
|
+
return (m?.[1] || "") as N
|
|
61
|
+
}
|
|
44
62
|
export function ptrName<const N extends string>(t: `<${N}>ptr` | `<${N}>ptr!`): N {
|
|
45
63
|
const m = PTR_TOKEN_RE.exec(t)
|
|
46
64
|
return (m?.[1] || "") as N
|
|
47
65
|
}
|
|
48
66
|
/**
|
|
49
|
-
* @description `<${string}>ptr` / `<${string}>ptr!` -> 'ptr'
|
|
67
|
+
* @description `<${string}>ptr` / `<${string}>ptr!` -> 'ptr'。按值形 `<${string}>` 无
|
|
68
|
+
* 标量槽等价物 —— 运行时 throw(返回位由 parseSig 拒、closure 入参位由 closure() 拒,
|
|
69
|
+
* 正常路径不可达),返回类型同时 Exclude 掉按值形:消费方(readRet / dispatchClosure
|
|
70
|
+
* 的 IA32_ARG_SIZE 索引)拿不到会误导编译期的标量误判。
|
|
50
71
|
*/
|
|
51
|
-
export const cTokenToType = <const T extends string>(
|
|
72
|
+
export const cTokenToType = <const T extends string>(
|
|
73
|
+
t: T,
|
|
74
|
+
): Exclude<T, `<${string}>ptr` | `<${string}>ptr!` | `<${string}>`> | 'ptr' => {
|
|
52
75
|
if (isCPtrToken(t)) return 'ptr'
|
|
53
|
-
|
|
76
|
+
if (isCValToken(t))
|
|
77
|
+
throw new Error(`ffi: cTokenToType: by-value token "${t}" has no scalar slot type`)
|
|
78
|
+
return t as Exclude<T, `<${string}>ptr` | `<${string}>ptr!` | `<${string}>`>
|
|
54
79
|
}
|
|
55
80
|
|
|
56
81
|
// C 的数字类型档:可作结构体字段 / bind 签名 token。排除项理由见 §1。
|
|
@@ -202,9 +227,16 @@ declare global {
|
|
|
202
227
|
// 泛型体内)由 `string & keyof map` = 全键联合兜底成非 never —— 否则 BindFn 塌
|
|
203
228
|
// (never)=>never 使内层强转失去可比性。
|
|
204
229
|
export type ArgJsTypeOfToken<K extends string, L> =
|
|
230
|
+
// 非标识符名字形('<X+>ptr'/'<X*>ptr'/'<1abc>ptr')不设专属分支:落 '<N>ptr'
|
|
231
|
+
// 通配、运行时 Unknown token 兜底('*' 已非 token 字形 —— 数组物化走
|
|
232
|
+
// structArray(def) encoder 的值域推导,token/引擎/类型层零数组概念)。
|
|
205
233
|
K extends `<${infer N}>ptr!` ? Ptr<N> | L[N & keyof L] :
|
|
206
234
|
K extends `<${string}>ptr@${infer E extends keyof EnumMap}` ? EnumMap[E] :
|
|
207
235
|
K extends `<${infer N}>ptr` ? MaybePtr<N> | L[N & keyof L] :
|
|
236
|
+
// '<N>' 按值位:值域 = 注册布局的 encode 入参形(对象直传),无 MaybePtr —— 按值
|
|
237
|
+
// 位不收 NULL / 地址(引擎按值语义编码,地址进位是必然错位)。未注册布局 →
|
|
238
|
+
// L[never] → 整位塌 never(与拼错 token 同策略)。
|
|
239
|
+
K extends `<${infer N}>` ? L[N & keyof L] :
|
|
208
240
|
K extends `${C_Number}@${infer E extends keyof EnumMap}` ? EnumMap[E] :
|
|
209
241
|
TokenArgJsTypeMap[K & keyof TokenArgJsTypeMap] extends never ? never
|
|
210
242
|
: TokenArgJsTypeMap[K & keyof TokenArgJsTypeMap]
|
|
@@ -234,11 +266,17 @@ export type RetJsTypeOfToken<K extends string, D> =
|
|
|
234
266
|
export function normToken(t: string): C_BasicType_Token {
|
|
235
267
|
if (isCPtrToken(t)) {
|
|
236
268
|
// 只剥 '@Name'(枚举标注是纯类型层语义,运行时不校验、不理解);'!' 保留在
|
|
237
|
-
// token 上——运行时要执法的语义(0 检查)跟着 token 走到 callPacked
|
|
238
|
-
// 无独立 flag 可被解构丢失。槽宽/ABI 判型走 isCPtrToken
|
|
269
|
+
// token 上——运行时要执法的语义(0 检查)跟着 token 走到 callPacked,
|
|
270
|
+
// 无独立 flag 可被解构丢失。槽宽/ABI 判型走 isCPtrToken,带修饰不受影响。
|
|
239
271
|
const m = PTR_TOKEN_RE.exec(t)
|
|
240
|
-
|
|
272
|
+
const name = m?.[1] || ''
|
|
273
|
+
const mod = m?.[2]
|
|
274
|
+
// 剥 '@'(纯类型层语义);'!' 形随 token 走。('*' 等词法外字符不设专属分支:
|
|
275
|
+
// 不匹配正则,落末尾 Unknown token —— 与 '<X+>ptr' 同待遇。)
|
|
276
|
+
return (mod?.startsWith('@') ? `<${name}>ptr` : t) as C_BasicType_Token
|
|
241
277
|
}
|
|
278
|
+
// 按值形 '<N>' 无修饰可剥(见 VAL_TOKEN_RE 注释),原样归一。
|
|
279
|
+
if (isCValToken(t)) return t
|
|
242
280
|
// 整数档尾缀 '@Name'(枚举值域标注,与 ptr 位的 '@' 同源):剥掉归一到原 token,
|
|
243
281
|
// 槽宽/ABI 只看归一形式。@ 后缀须为纯枚举名形 —— 'u32@WmMsg:3' 这类混进位域/数组
|
|
244
282
|
// 串的直接 throw,不静默吞成纯标量(布局会错);名 ∈ EnumMap 的语义校验留给类型层
|
package/lib/ffi/struct.js
CHANGED
|
@@ -354,28 +354,31 @@ function createStruct(t) {
|
|
|
354
354
|
size,
|
|
355
355
|
structAlign: maxEffectiveAlign,
|
|
356
356
|
decode: (p) => {
|
|
357
|
-
// number = native 拥有的品牌指针(Codec 的 decode(p)
|
|
358
|
-
//
|
|
357
|
+
// number = native 拥有的品牌指针(Codec 的 decode(p) 形态):readBytes 一次
|
|
358
|
+
// native memcpy 拷出 size 字节再解码(替代逐字节 readByte 循环,N 次跨语言
|
|
359
|
+
// 调用降为 1 次);ArrayBuffer = 调用方持有的 out buffer 直读。
|
|
359
360
|
// 顶层不收 offset——out 参数恒从起点读,嵌套偏移由字段布局(doDecode 的 base)承担。
|
|
360
|
-
if (typeof p === 'number')
|
|
361
|
-
|
|
362
|
-
const u8 = new Uint8Array(buf);
|
|
363
|
-
for (let i = 0; i < size; i++)
|
|
364
|
-
u8[i] = ffi.readByte(p + i);
|
|
365
|
-
return doDecode(new DataView(buf), 0, fields);
|
|
366
|
-
}
|
|
361
|
+
if (typeof p === 'number')
|
|
362
|
+
return doDecode(new DataView(ffi.readBytes(p, size)), 0, fields);
|
|
367
363
|
return doDecode(new DataView(p), 0, fields);
|
|
368
364
|
},
|
|
369
|
-
alloc: (() =>
|
|
370
|
-
|
|
365
|
+
alloc: ((n) => {
|
|
366
|
+
// fail-loud:n < 1 / 非整数绝不静默产出 PAB(0)(其 .ptr 是"非 0 的 1 字节
|
|
367
|
+
// 堆指针",会骗过 C 的 NULL 防御)—— 空位语义由调用方显式传 NULL。
|
|
368
|
+
if (n !== undefined && (!Number.isInteger(n) || n < 1))
|
|
369
|
+
throw new Error(`ffi-struct: alloc(n) requires an integer n >= 1 ` +
|
|
370
|
+
`(PAB(0) never escapes — pass NULL for an empty pointer position)`);
|
|
371
|
+
return new PtrArrayBuffer(size * (n ?? 1));
|
|
372
|
+
}),
|
|
373
|
+
encode: ((v, buf, offset) => {
|
|
371
374
|
// fail-loud:无参 = 分配语义,走 alloc();这里绝不静默零填充 —— 类型层由
|
|
372
|
-
// EncodeIn 拦 '!' 字段省略,动态/JS 调用方由本 throw
|
|
373
|
-
//
|
|
375
|
+
// EncodeIn 拦 '!' 字段省略,动态/JS 调用方由本 throw 兜住。顶层默认从起点写,
|
|
376
|
+
// offset 显式给写入点(数组物化原语,见接口注释)。
|
|
374
377
|
if (v === undefined || v === null)
|
|
375
378
|
throw new Error(`ffi-struct: encode(v) requires an argument; ` +
|
|
376
379
|
`use alloc() for a zeroed out-param slot`);
|
|
377
380
|
const out = buf ?? new PtrArrayBuffer(size);
|
|
378
|
-
doEncode(new DataView(out), 0, fields, v);
|
|
381
|
+
doEncode(new DataView(out), offset ?? 0, fields, v);
|
|
379
382
|
return out;
|
|
380
383
|
}),
|
|
381
384
|
offsetOf: (name) => {
|
|
@@ -398,6 +401,81 @@ function buildDecl(kind, member, pack) {
|
|
|
398
401
|
decl[k] = v;
|
|
399
402
|
return decl;
|
|
400
403
|
}
|
|
404
|
+
/** 结构/联合体的数组编解码对工厂:返回 `{ encode, decode }`。
|
|
405
|
+
* encode —— 单值 encoder 的数组版:收元素数组 → 按本布局 size 步长紧密排列
|
|
406
|
+
* (stride = size,已含尾部对齐 padding),返回带品牌 .ptr 的 PAB —— 与 C 的
|
|
407
|
+
* `const T apt[]` 同构。值域刻意收普通数组(调用方持有的 `const pts = [...]` 变量
|
|
408
|
+
* 直传,非空元组会拒之);[] / 非数组 / 单对象运行时 throw —— 任何路径都不产出
|
|
409
|
+
* PAB(0)(其 .ptr 是"非 0 的 1 字节堆指针",会骗过 C 的 NULL 防御;空位语义由
|
|
410
|
+
* 调用方显式传 NULL)。注册进 bind 的 encoders 表(`{ POINT: structArray(POINT) }`)
|
|
411
|
+
* 后,普通 `<POINT>ptr` 位的值域即元素数组(值域由 encoder 推导,token/引擎/类型层
|
|
412
|
+
* 零数组概念);直接调用 `.encode(els).ptr` 亦可。
|
|
413
|
+
* decode —— 出参数组读回(与 def.decode 的 ArrayBuffer/地址双态对称):
|
|
414
|
+
* - buffer 形(含 PAB):count? 缺省自算 byteLength/size —— 要求整数倍,% ≠ 0
|
|
415
|
+
* throw(混合缓冲如 EnumPrinters 的"结构 + 尾部字符串"必须显式 count);显式
|
|
416
|
+
* count 校验 count*size ≤ byteLength(写错 count → 越界读的防线,buf 形可校才
|
|
417
|
+
* 校;不验品牌,与 def.decode 的 buffer 形同款)。
|
|
418
|
+
* - 裸地址形:count 必填(地址无长度信息),逐字节拷读(def.decode 的 number 形
|
|
419
|
+
* 同机制);长度不可校 → 信任 C 的 count。
|
|
420
|
+
* - count = 0 → [](读 0 个真空安全,与 encode 禁空的 PAB(0) 理由刻意不对称)。
|
|
421
|
+
* 实现:encode 走纯公开 API(alloc + encode offset 循环);decode 在本文件内复用
|
|
422
|
+
* computeStructLayout/doDecode。 */
|
|
423
|
+
export function structArray(def) {
|
|
424
|
+
const { fields, size } = computeStructLayout(def.__struct);
|
|
425
|
+
const readN = (dv, n) => {
|
|
426
|
+
const out = [];
|
|
427
|
+
for (let i = 0; i < n; i++)
|
|
428
|
+
out.push(doDecode(dv, i * size, fields));
|
|
429
|
+
return out;
|
|
430
|
+
};
|
|
431
|
+
return {
|
|
432
|
+
encode: (els) => {
|
|
433
|
+
if (!Array.isArray(els) || els.length === 0)
|
|
434
|
+
throw new Error(`ffi-struct: structArray(def).encode(els) requires a ` +
|
|
435
|
+
`non-empty element array (pass NULL for an empty pointer position)`);
|
|
436
|
+
const out = def.alloc(els.length);
|
|
437
|
+
for (let i = 0; i < els.length; i++)
|
|
438
|
+
def.encode(els[i], out, i * def.size);
|
|
439
|
+
return out;
|
|
440
|
+
},
|
|
441
|
+
decode: ((p, count) => {
|
|
442
|
+
// fail-loud 校验矩阵(见 JSDoc):显式 count 信调用方但拦越界;缺省只对
|
|
443
|
+
// 有 byteLength 的 buffer 形自算;裸地址必带 count。
|
|
444
|
+
if (count !== undefined && (!Number.isInteger(count) || count < 0))
|
|
445
|
+
throw new Error(`ffi-struct: structArray.decode(p, count) requires a ` +
|
|
446
|
+
`non-negative integer count, got ${count}`);
|
|
447
|
+
if (typeof p === 'number') {
|
|
448
|
+
if (count === undefined)
|
|
449
|
+
throw new Error(`ffi-struct: structArray.decode(address, count) ` +
|
|
450
|
+
`requires an explicit count (a bare address carries no length)`);
|
|
451
|
+
if (count === 0)
|
|
452
|
+
return [];
|
|
453
|
+
// readBytes 一次 native memcpy 拷出整段再解码(def.decode 的 number 形同机制)。
|
|
454
|
+
return readN(new DataView(ffi.readBytes(p, count * size)), count);
|
|
455
|
+
}
|
|
456
|
+
let n;
|
|
457
|
+
if (count !== undefined) {
|
|
458
|
+
n = count;
|
|
459
|
+
// 越界防线:写错 count 是越界读的唯一入口,buf 形有 byteLength 就校死。
|
|
460
|
+
if (n * size > p.byteLength)
|
|
461
|
+
throw new Error(`ffi-struct: structArray.decode: count ${n} × ` +
|
|
462
|
+
`size ${size} = ${n * size} exceeds buffer byteLength ${p.byteLength}`);
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
// 自算形态:byteLength 必须是纯元素序列(size = 0 的退化布局同样落此
|
|
466
|
+
// throw —— 0 % 0 = NaN),混合缓冲显式传 count。
|
|
467
|
+
if (p.byteLength % size !== 0)
|
|
468
|
+
throw new Error(`ffi-struct: structArray.decode: byteLength ` +
|
|
469
|
+
`${p.byteLength} is not a multiple of size ${size} ` +
|
|
470
|
+
`(mixed buffer? pass count explicitly)`);
|
|
471
|
+
n = p.byteLength / size;
|
|
472
|
+
}
|
|
473
|
+
if (n === 0)
|
|
474
|
+
return [];
|
|
475
|
+
return readN(new DataView(p), n);
|
|
476
|
+
}),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
401
479
|
export function struct(a, b, c) {
|
|
402
480
|
const isNamed = typeof a === 'string';
|
|
403
481
|
const raw = (isNamed ? b : a);
|
package/lib/ffi/struct.ts
CHANGED
|
@@ -214,13 +214,18 @@ export type StructDef<T extends { '#': 'struct' | 'union' }, N extends string =
|
|
|
214
214
|
// 数组元素含必填 ⇒ 该键必给,值后序合成到内层)。带 buf = 写入调用方既有
|
|
215
215
|
// PtrArrayBuffer(返回其本身,供链式读回)——encode 出口一律 PAB,裸 ArrayBuffer
|
|
216
216
|
// 不出 codec(与 CodecMap 同约束)。动态调用方无参 encode 由运行时 throw 兜底。
|
|
217
|
-
|
|
217
|
+
/** n(默认 1)= 按本布局 size 步长连续分配 n 个元素的零填充缓冲(出参数组场景,
|
|
218
|
+
* 品牌 .ptr 直接喂 <N>ptr 位)。n < 1 / 非整数运行时 throw ——
|
|
219
|
+
* 任何路径都不产出 PAB(0)(其 .ptr 是"非 0 的 1 字节堆指针",会骗过 C 的 NULL
|
|
220
|
+
* 防御;空位语义由调用方显式传 NULL)。 */
|
|
221
|
+
alloc(n?: number): PtrArrayBuffer<N>
|
|
218
222
|
encode(v: EncodeIn<T>): PtrArrayBuffer<N>
|
|
219
|
-
// 带 buf = 写入调用方既有 PtrArrayBuffer
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
|
|
223
|
+
// 带 buf = 写入调用方既有 PtrArrayBuffer(复用/预分配形态);带 offset = 写入点
|
|
224
|
+
// 相对 buf 起点的字节偏移(数组物化/自定义内存管理的底层写入原语,内部即 doEncode
|
|
225
|
+
// 的 base)。返回值仍是 buf 本身 —— offset 形态下其 .ptr 对不上写入点,写入点地址
|
|
226
|
+
// = buf.ptr + offset(自算术,品牌随算术丢失同 CreatePipe 先例);越界写由 DataView
|
|
227
|
+
// RangeError 兜底,无需额外校验。
|
|
228
|
+
encode(v: EncodeIn<T>, buf: PtrArrayBuffer<any>, offset?: number): PtrArrayBuffer<any>
|
|
224
229
|
offsetOf(name: string): number
|
|
225
230
|
}
|
|
226
231
|
|
|
@@ -592,27 +597,31 @@ function createStruct(t: C_Struct | C_Union): any {
|
|
|
592
597
|
size,
|
|
593
598
|
structAlign: maxEffectiveAlign,
|
|
594
599
|
decode: (p: ArrayBuffer | number) => {
|
|
595
|
-
// number = native 拥有的品牌指针(Codec 的 decode(p)
|
|
596
|
-
//
|
|
600
|
+
// number = native 拥有的品牌指针(Codec 的 decode(p) 形态):readBytes 一次
|
|
601
|
+
// native memcpy 拷出 size 字节再解码(替代逐字节 readByte 循环,N 次跨语言
|
|
602
|
+
// 调用降为 1 次);ArrayBuffer = 调用方持有的 out buffer 直读。
|
|
597
603
|
// 顶层不收 offset——out 参数恒从起点读,嵌套偏移由字段布局(doDecode 的 base)承担。
|
|
598
|
-
if (typeof p === 'number')
|
|
599
|
-
|
|
600
|
-
const u8 = new Uint8Array(buf)
|
|
601
|
-
for (let i = 0; i < size; i++) u8[i] = ffi.readByte(p + i)
|
|
602
|
-
return doDecode(new DataView(buf), 0, fields)
|
|
603
|
-
}
|
|
604
|
+
if (typeof p === 'number')
|
|
605
|
+
return doDecode(new DataView(ffi.readBytes(p, size)), 0, fields)
|
|
604
606
|
return doDecode(new DataView(p), 0, fields)
|
|
605
607
|
},
|
|
606
|
-
alloc: (() =>
|
|
607
|
-
|
|
608
|
+
alloc: ((n?: number) => {
|
|
609
|
+
// fail-loud:n < 1 / 非整数绝不静默产出 PAB(0)(其 .ptr 是"非 0 的 1 字节
|
|
610
|
+
// 堆指针",会骗过 C 的 NULL 防御)—— 空位语义由调用方显式传 NULL。
|
|
611
|
+
if (n !== undefined && (!Number.isInteger(n) || n < 1))
|
|
612
|
+
throw new Error(`ffi-struct: alloc(n) requires an integer n >= 1 ` +
|
|
613
|
+
`(PAB(0) never escapes — pass NULL for an empty pointer position)`)
|
|
614
|
+
return new PtrArrayBuffer(size * (n ?? 1))
|
|
615
|
+
}) as StructDef<any, any>['alloc'],
|
|
616
|
+
encode: ((v: any, buf?: PtrArrayBuffer<any>, offset?: number) => {
|
|
608
617
|
// fail-loud:无参 = 分配语义,走 alloc();这里绝不静默零填充 —— 类型层由
|
|
609
|
-
// EncodeIn 拦 '!' 字段省略,动态/JS 调用方由本 throw
|
|
610
|
-
//
|
|
618
|
+
// EncodeIn 拦 '!' 字段省略,动态/JS 调用方由本 throw 兜住。顶层默认从起点写,
|
|
619
|
+
// offset 显式给写入点(数组物化原语,见接口注释)。
|
|
611
620
|
if (v === undefined || v === null)
|
|
612
621
|
throw new Error(`ffi-struct: encode(v) requires an argument; ` +
|
|
613
622
|
`use alloc() for a zeroed out-param slot`)
|
|
614
623
|
const out = buf ?? new PtrArrayBuffer(size)
|
|
615
|
-
doEncode(new DataView(out), 0, fields, v)
|
|
624
|
+
doEncode(new DataView(out), offset ?? 0, fields, v)
|
|
616
625
|
return out
|
|
617
626
|
}) as StructDef<any, any>['encode'],
|
|
618
627
|
offsetOf: (name: string) => {
|
|
@@ -640,6 +649,86 @@ function buildDecl(kind: 'struct' | 'union', member: Record<string, unknown>, pa
|
|
|
640
649
|
return decl as unknown as C_Struct | C_Union
|
|
641
650
|
}
|
|
642
651
|
|
|
652
|
+
/** decode 双态重载:有 byteLength 的 buffer 形 count 可缺省(自算);裸地址无长度
|
|
653
|
+
* 信息,count 必填(类型层拦,缺省调用编译红)。 */
|
|
654
|
+
type StructArrayDecode<T extends { '#': 'struct' | 'union' }, N extends string> = {
|
|
655
|
+
(p: ArrayBuffer, count?: number): ShapeOfC<T>[]
|
|
656
|
+
(p: Ptr<N>, count: number): ShapeOfC<T>[]
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/** 结构/联合体的数组编解码对工厂:返回 `{ encode, decode }`。
|
|
660
|
+
* encode —— 单值 encoder 的数组版:收元素数组 → 按本布局 size 步长紧密排列
|
|
661
|
+
* (stride = size,已含尾部对齐 padding),返回带品牌 .ptr 的 PAB —— 与 C 的
|
|
662
|
+
* `const T apt[]` 同构。值域刻意收普通数组(调用方持有的 `const pts = [...]` 变量
|
|
663
|
+
* 直传,非空元组会拒之);[] / 非数组 / 单对象运行时 throw —— 任何路径都不产出
|
|
664
|
+
* PAB(0)(其 .ptr 是"非 0 的 1 字节堆指针",会骗过 C 的 NULL 防御;空位语义由
|
|
665
|
+
* 调用方显式传 NULL)。注册进 bind 的 encoders 表(`{ POINT: structArray(POINT) }`)
|
|
666
|
+
* 后,普通 `<POINT>ptr` 位的值域即元素数组(值域由 encoder 推导,token/引擎/类型层
|
|
667
|
+
* 零数组概念);直接调用 `.encode(els).ptr` 亦可。
|
|
668
|
+
* decode —— 出参数组读回(与 def.decode 的 ArrayBuffer/地址双态对称):
|
|
669
|
+
* - buffer 形(含 PAB):count? 缺省自算 byteLength/size —— 要求整数倍,% ≠ 0
|
|
670
|
+
* throw(混合缓冲如 EnumPrinters 的"结构 + 尾部字符串"必须显式 count);显式
|
|
671
|
+
* count 校验 count*size ≤ byteLength(写错 count → 越界读的防线,buf 形可校才
|
|
672
|
+
* 校;不验品牌,与 def.decode 的 buffer 形同款)。
|
|
673
|
+
* - 裸地址形:count 必填(地址无长度信息),逐字节拷读(def.decode 的 number 形
|
|
674
|
+
* 同机制);长度不可校 → 信任 C 的 count。
|
|
675
|
+
* - count = 0 → [](读 0 个真空安全,与 encode 禁空的 PAB(0) 理由刻意不对称)。
|
|
676
|
+
* 实现:encode 走纯公开 API(alloc + encode offset 循环);decode 在本文件内复用
|
|
677
|
+
* computeStructLayout/doDecode。 */
|
|
678
|
+
export function structArray<T extends { '#': 'struct' | 'union' }, N extends string = ''>(
|
|
679
|
+
def: StructDef<T, N>,
|
|
680
|
+
) {
|
|
681
|
+
const { fields, size } = computeStructLayout(def.__struct)
|
|
682
|
+
const readN = (dv: DataView, n: number): unknown[] => {
|
|
683
|
+
const out: unknown[] = []
|
|
684
|
+
for (let i = 0; i < n; i++) out.push(doDecode(dv, i * size, fields))
|
|
685
|
+
return out
|
|
686
|
+
}
|
|
687
|
+
return {
|
|
688
|
+
encode: (els: readonly EncodeIn<T>[]): PtrArrayBuffer<N> => {
|
|
689
|
+
if (!Array.isArray(els) || els.length === 0)
|
|
690
|
+
throw new Error(`ffi-struct: structArray(def).encode(els) requires a ` +
|
|
691
|
+
`non-empty element array (pass NULL for an empty pointer position)`)
|
|
692
|
+
const out = def.alloc(els.length)
|
|
693
|
+
for (let i = 0; i < els.length; i++) def.encode(els[i], out, i * def.size)
|
|
694
|
+
return out
|
|
695
|
+
},
|
|
696
|
+
decode: ((p: ArrayBuffer | number, count?: number): unknown[] => {
|
|
697
|
+
// fail-loud 校验矩阵(见 JSDoc):显式 count 信调用方但拦越界;缺省只对
|
|
698
|
+
// 有 byteLength 的 buffer 形自算;裸地址必带 count。
|
|
699
|
+
if (count !== undefined && (!Number.isInteger(count) || count < 0))
|
|
700
|
+
throw new Error(`ffi-struct: structArray.decode(p, count) requires a ` +
|
|
701
|
+
`non-negative integer count, got ${count}`)
|
|
702
|
+
if (typeof p === 'number') {
|
|
703
|
+
if (count === undefined)
|
|
704
|
+
throw new Error(`ffi-struct: structArray.decode(address, count) ` +
|
|
705
|
+
`requires an explicit count (a bare address carries no length)`)
|
|
706
|
+
if (count === 0) return []
|
|
707
|
+
// readBytes 一次 native memcpy 拷出整段再解码(def.decode 的 number 形同机制)。
|
|
708
|
+
return readN(new DataView(ffi.readBytes(p, count * size)), count)
|
|
709
|
+
}
|
|
710
|
+
let n: number
|
|
711
|
+
if (count !== undefined) {
|
|
712
|
+
n = count
|
|
713
|
+
// 越界防线:写错 count 是越界读的唯一入口,buf 形有 byteLength 就校死。
|
|
714
|
+
if (n * size > p.byteLength)
|
|
715
|
+
throw new Error(`ffi-struct: structArray.decode: count ${n} × ` +
|
|
716
|
+
`size ${size} = ${n * size} exceeds buffer byteLength ${p.byteLength}`)
|
|
717
|
+
} else {
|
|
718
|
+
// 自算形态:byteLength 必须是纯元素序列(size = 0 的退化布局同样落此
|
|
719
|
+
// throw —— 0 % 0 = NaN),混合缓冲显式传 count。
|
|
720
|
+
if (p.byteLength % size !== 0)
|
|
721
|
+
throw new Error(`ffi-struct: structArray.decode: byteLength ` +
|
|
722
|
+
`${p.byteLength} is not a multiple of size ${size} ` +
|
|
723
|
+
`(mixed buffer? pass count explicitly)`)
|
|
724
|
+
n = p.byteLength / size
|
|
725
|
+
}
|
|
726
|
+
if (n === 0) return []
|
|
727
|
+
return readN(new DataView(p), n)
|
|
728
|
+
}) as StructArrayDecode<T, N>,
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
643
732
|
/** 平铺字段表定义结构体(token/`X[n]`/`unit:width`/`'#'` 声明均可,见 ctype.ts §4)。
|
|
644
733
|
* 给 name 则 alloc().ptr 与 decode 入参带 Ptr<name> 品牌。 */
|
|
645
734
|
type StructOf<M, N extends string = ''> = StructDef<{ '#': 'struct', '#pack'?: number } & M, N>
|
|
@@ -47,6 +47,7 @@ function getCellFont(hwnd, style) {
|
|
|
47
47
|
const cur = gui.SendMessage(hwnd, 49 /* gui.WmMsg.GETFONT */, 0, 0);
|
|
48
48
|
if (cur) {
|
|
49
49
|
const lf = LOGFONTW.alloc();
|
|
50
|
+
// GETFONT 的 LRESULT 断言为 HFONT(gdi32 <HGDIOBJ>ptr 位)
|
|
50
51
|
if (!GetObject(cur, LOGFONTW.size, lf))
|
|
51
52
|
return null;
|
|
52
53
|
base = LOGFONTW.decode(lf);
|
|
@@ -23,7 +23,7 @@ function openFileDialog(owner, filter, title, multiple) {
|
|
|
23
23
|
lpstrTitle: titleWide?.ptr ?? 0,
|
|
24
24
|
Flags: flags,
|
|
25
25
|
});
|
|
26
|
-
const ret = GetOpenFileName(ofn);
|
|
26
|
+
const ret = GetOpenFileName(ofn.ptr);
|
|
27
27
|
if (!ret)
|
|
28
28
|
return null;
|
|
29
29
|
if (!multiple) {
|
|
@@ -42,7 +42,7 @@ function openFolderDialog(owner, title) {
|
|
|
42
42
|
lpszTitle: titleWide?.ptr ?? 0,
|
|
43
43
|
ulFlags: 0x00000041,
|
|
44
44
|
});
|
|
45
|
-
const pidl = SHBrowseForFolder(bi);
|
|
45
|
+
const pidl = SHBrowseForFolder(bi.ptr);
|
|
46
46
|
if (!pidl)
|
|
47
47
|
return null;
|
|
48
48
|
const pathBuf = WCHAR.alloc(260);
|
package/lib/react-qw/index.js
CHANGED
|
@@ -50,7 +50,7 @@ export function createRoot(container) {
|
|
|
50
50
|
else {
|
|
51
51
|
hwnd = container;
|
|
52
52
|
}
|
|
53
|
-
const root = reconciler.createContainer(hwnd, 0, null, false, null, '',
|
|
53
|
+
const root = reconciler.createContainer(hwnd, 0, null, false, null, '', (error, info) => console.log('[React uncaught]', error, error.stack, info.componentStack), (error, info) => console.log('[React caught]', error, error.stack, info.componentStack), (error, info) => console.log('[React recoverable]', error, error.stack, info.componentStack), noop);
|
|
54
54
|
const rootInst = { hwnd, type: '_root', props: { type: '_root', style: { flexDirection: 'column', alignItems: 'stretch' } }, children: [] };
|
|
55
55
|
instancesByHwnd.set(hwnd, rootInst);
|
|
56
56
|
if (typeof container === 'object')
|
package/lib/react-qw/index.ts
CHANGED
|
@@ -64,7 +64,10 @@ export function createRoot(container: gui.HWND | RootWindowConfig) {
|
|
|
64
64
|
}
|
|
65
65
|
const root = reconciler.createContainer(
|
|
66
66
|
hwnd, 0, null, false, null, '',
|
|
67
|
-
|
|
67
|
+
(error, info) => console.log('[React uncaught]', error, error.stack, info.componentStack),
|
|
68
|
+
(error, info) => console.log('[React caught]', error, error.stack, info.componentStack),
|
|
69
|
+
(error, info) => console.log('[React recoverable]', error, error.stack, info.componentStack),
|
|
70
|
+
noop,
|
|
68
71
|
)
|
|
69
72
|
const rootInst: Instance = { hwnd, type: '_root', props: { type: '_root', style: { flexDirection: 'column', alignItems: 'stretch' } }, children: [] }
|
|
70
73
|
instancesByHwnd.set(hwnd, rootInst)
|
package/lib/text-measure.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NULL } from './ffi/ctype.js';
|
|
1
2
|
import * as gui from 'gui';
|
|
2
3
|
import { DrawText, GetDC, ReleaseDC } from './windows/user32.js';
|
|
3
4
|
import { SelectObject } from './windows/gdi32.js';
|
|
@@ -21,8 +22,9 @@ export function measureTextForHwnd(hwnd, text) {
|
|
|
21
22
|
const hdc = GetDC(hwnd);
|
|
22
23
|
if (!hdc)
|
|
23
24
|
return { width: 0, height: 0 };
|
|
25
|
+
// SendMessage 返回 LRESULT 裸数;GETFONT 语义为 HFONT——断言进 GDI 对象品牌链
|
|
24
26
|
const hFont = gui.SendMessage(hwnd, 49 /* gui.WmMsg.GETFONT */, 0, 0);
|
|
25
|
-
const oldFont = hFont ? SelectObject(hdc, hFont) :
|
|
27
|
+
const oldFont = hFont ? SelectObject(hdc, hFont) : NULL;
|
|
26
28
|
const result = measureText(hdc, text, 0);
|
|
27
29
|
if (hFont)
|
|
28
30
|
SelectObject(hdc, oldFont);
|
package/lib/text-measure.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { NULL, type Ptr } from './ffi/ctype.js'
|
|
2
2
|
import * as gui from 'gui'
|
|
3
3
|
import { DrawText, GetDC, ReleaseDC } from './windows/user32.js'
|
|
4
4
|
import { SelectObject } from './windows/gdi32.js'
|
|
@@ -24,8 +24,9 @@ export function getButtonIdealSize(hwnd: gui.HWND): { width: number; height: num
|
|
|
24
24
|
export function measureTextForHwnd(hwnd: gui.HWND, text: string): { width: number; height: number } {
|
|
25
25
|
const hdc = GetDC(hwnd)
|
|
26
26
|
if (!hdc) return { width: 0, height: 0 }
|
|
27
|
-
|
|
28
|
-
const
|
|
27
|
+
// SendMessage 返回 LRESULT 裸数;GETFONT 语义为 HFONT——断言进 GDI 对象品牌链
|
|
28
|
+
const hFont = gui.SendMessage(hwnd, gui.WmMsg.GETFONT, 0, 0) as Ptr<'HGDIOBJ'>
|
|
29
|
+
const oldFont = hFont ? SelectObject(hdc, hFont) : NULL
|
|
29
30
|
const result = measureText(hdc, text, 0)
|
|
30
31
|
if (hFont) SelectObject(hdc, oldFont)
|
|
31
32
|
ReleaseDC(hwnd, hdc)
|
package/lib/websocket.js
CHANGED
|
@@ -345,7 +345,21 @@ class WebSocketImpl {
|
|
|
345
345
|
while (true) {
|
|
346
346
|
let data;
|
|
347
347
|
if (isWSS && this._ssl) {
|
|
348
|
-
|
|
348
|
+
const buf = new ArrayBuffer(8192);
|
|
349
|
+
const n = wolfssl.wolfSSL_read(this._ssl, buf);
|
|
350
|
+
if (n > 0) {
|
|
351
|
+
data = buf.slice(0, n);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
const err = wolfssl.wolfSSL_get_error(this._ssl, n);
|
|
355
|
+
if (err === 2 /* wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_READ */ ||
|
|
356
|
+
err === 3 /* wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_WRITE */)
|
|
357
|
+
break;
|
|
358
|
+
if (n === 0 || err === 6 /* wolfssl.ErrorCode.WOLFSSL_ERROR_ZERO_RETURN */)
|
|
359
|
+
break;
|
|
360
|
+
doError(new Error('TLS read failed: ' + err));
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
349
363
|
}
|
|
350
364
|
else if (this._sock !== null && this._sock >= 0) {
|
|
351
365
|
data = sock.recv(this._sock, 8192);
|
|
@@ -443,7 +457,23 @@ class WebSocketImpl {
|
|
|
443
457
|
while (true) {
|
|
444
458
|
let data;
|
|
445
459
|
if (this._ssl) {
|
|
446
|
-
|
|
460
|
+
const buf = new ArrayBuffer(8192);
|
|
461
|
+
const n = wolfssl.wolfSSL_read(this._ssl, buf);
|
|
462
|
+
if (n > 0) {
|
|
463
|
+
data = buf.slice(0, n);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
const err = wolfssl.wolfSSL_get_error(this._ssl, n);
|
|
467
|
+
if (err === 2 /* wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_READ */ ||
|
|
468
|
+
err === 3 /* wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_WRITE */)
|
|
469
|
+
break;
|
|
470
|
+
if (n === 0 || err === 6 /* wolfssl.ErrorCode.WOLFSSL_ERROR_ZERO_RETURN */)
|
|
471
|
+
break;
|
|
472
|
+
this._fireError(new Error('TLS read failed: ' + err));
|
|
473
|
+
if (this._state !== State.CLOSED)
|
|
474
|
+
this._setState(State.CLOSED);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
447
477
|
}
|
|
448
478
|
else if (this._sock !== null && this._sock >= 0) {
|
|
449
479
|
data = sock.recv(this._sock, 8192);
|
package/lib/websocket.ts
CHANGED
|
@@ -363,7 +363,18 @@ class WebSocketImpl {
|
|
|
363
363
|
while (true) {
|
|
364
364
|
let data: ArrayBuffer | null
|
|
365
365
|
if (isWSS && this._ssl) {
|
|
366
|
-
|
|
366
|
+
const buf = new ArrayBuffer(8192)
|
|
367
|
+
const n = wolfssl.wolfSSL_read(this._ssl, buf)
|
|
368
|
+
if (n > 0) {
|
|
369
|
+
data = buf.slice(0, n)
|
|
370
|
+
} else {
|
|
371
|
+
const err = wolfssl.wolfSSL_get_error(this._ssl, n)
|
|
372
|
+
if (err === wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_READ ||
|
|
373
|
+
err === wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_WRITE) break
|
|
374
|
+
if (n === 0 || err === wolfssl.ErrorCode.WOLFSSL_ERROR_ZERO_RETURN) break
|
|
375
|
+
doError(new Error('TLS read failed: ' + err))
|
|
376
|
+
return
|
|
377
|
+
}
|
|
367
378
|
} else if (this._sock !== null && this._sock >= 0) {
|
|
368
379
|
data = sock.recv(this._sock, 8192)
|
|
369
380
|
} else { break }
|
|
@@ -460,7 +471,19 @@ class WebSocketImpl {
|
|
|
460
471
|
while (true) {
|
|
461
472
|
let data: ArrayBuffer | null
|
|
462
473
|
if (this._ssl) {
|
|
463
|
-
|
|
474
|
+
const buf = new ArrayBuffer(8192)
|
|
475
|
+
const n = wolfssl.wolfSSL_read(this._ssl, buf)
|
|
476
|
+
if (n > 0) {
|
|
477
|
+
data = buf.slice(0, n)
|
|
478
|
+
} else {
|
|
479
|
+
const err = wolfssl.wolfSSL_get_error(this._ssl, n)
|
|
480
|
+
if (err === wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_READ ||
|
|
481
|
+
err === wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_WRITE) break
|
|
482
|
+
if (n === 0 || err === wolfssl.ErrorCode.WOLFSSL_ERROR_ZERO_RETURN) break
|
|
483
|
+
this._fireError(new Error('TLS read failed: ' + err))
|
|
484
|
+
if (this._state !== State.CLOSED) this._setState(State.CLOSED)
|
|
485
|
+
return
|
|
486
|
+
}
|
|
464
487
|
} else if (this._sock !== null && this._sock >= 0) {
|
|
465
488
|
data = sock.recv(this._sock, 8192)
|
|
466
489
|
} else { break }
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// advapi32 绑定(注册表 API;键名/值名一律 W 版 UTF-16)
|
|
2
|
+
import { bind } from '../ffi/bind.js';
|
|
3
|
+
// dll 名部分应用(工厂转调保泛型 infer,见 lib/windows/user32.ts 头注释)
|
|
4
|
+
const b = (name, sig) => bind('advapi32.dll', name, sig);
|
|
5
|
+
/** 创建/打开子键(子键不存在则创建,含中间层级)
|
|
6
|
+
* @returns 0 = ERROR_SUCCESS
|
|
7
|
+
* @param args_0 hKey 父键:gui.HKey.CURRENT_USER / LOCAL_MACHINE 预定义键,或本族
|
|
8
|
+
* Open/Create 返回的真实句柄(此位双来源,不标 @HKey——裸位收两种)
|
|
9
|
+
* @param args_1 lpSubKey 子键路径('Software\\X\\Y',可 NULL = 打开 hKey 自身副本)
|
|
10
|
+
* @param args_2 Reserved 保留,恒 0
|
|
11
|
+
* @param args_3 lpClass 键类名(一般 NULL)
|
|
12
|
+
* @param args_4 dwOptions 保留属性(恒 0)
|
|
13
|
+
* @param args_5 samDesired 访问掩码 gui.RegAccess.READ / SET_VALUE(标准组合常量按成员收录)
|
|
14
|
+
* @param args_6 lpSecurityAttributes 安全描述符(一般 NULL)
|
|
15
|
+
* @param args_7 phkResult 出参:键句柄(PtrArrayBuffer(8),readPtr 按架构读回)
|
|
16
|
+
* @param args_8 lpdwDisposition 出参:新建/已存在标记(可 NULL) */
|
|
17
|
+
export const RegCreateKeyEx = /*@__PURE__*/ b('RegCreateKeyExW', '<>ptr <WCHAR>ptr u32 <WCHAR>ptr u32 u32@RegAccess <>ptr <BYTE>ptr <BYTE>ptr -> i32');
|
|
18
|
+
/** 打开已有子键(不创建)
|
|
19
|
+
* @returns 0 = ERROR_SUCCESS;2 = ERROR_FILE_NOT_FOUND(子键不存在)
|
|
20
|
+
* @param args_0 hKey 父键(同 RegCreateKeyEx 的双来源语义)
|
|
21
|
+
* @param args_1 lpSubKey 子键路径(NULL = 打开 hKey 自身副本)
|
|
22
|
+
* @param args_2 ulOptions 保留,恒 0
|
|
23
|
+
* @param args_3 samDesired 访问掩码 gui.RegAccess.READ / SET_VALUE
|
|
24
|
+
* @param args_4 phkResult 出参:键句柄 */
|
|
25
|
+
export const RegOpenKeyEx = /*@__PURE__*/ b('RegOpenKeyExW', '<>ptr <WCHAR>ptr u32 u32@RegAccess <BYTE>ptr -> i32');
|
|
26
|
+
/** 关闭键句柄;只关 Open/Create 返回的真实句柄——预定义键(HKEY_CURRENT_USER 等)
|
|
27
|
+
* 不要关
|
|
28
|
+
* @returns 0 = ERROR_SUCCESS */
|
|
29
|
+
export const RegCloseKey = /*@__PURE__*/ b('RegCloseKey', '<>ptr -> i32');
|
|
30
|
+
/** 写键值
|
|
31
|
+
* @returns 0 = ERROR_SUCCESS
|
|
32
|
+
* @param args_0 hKey 键句柄(须 KEY_SET_VALUE 权限,含于 gui.RegAccess.SET_VALUE)
|
|
33
|
+
* @param args_1 lpValueName 值名(NULL = 键的默认值)
|
|
34
|
+
* @param args_2 Reserved 保留,恒 0
|
|
35
|
+
* @param args_3 dwType 值类型 gui.RegType.SZ(UTF-16 字符串)
|
|
36
|
+
* @param args_4 lpData 数据缓冲(REG_SZ:UTF-16LE 含结尾 NUL)
|
|
37
|
+
* @param args_5 cbData 数据字节数(REG_SZ = (字符数+1)*2) */
|
|
38
|
+
export const RegSetValueEx = /*@__PURE__*/ b('RegSetValueExW', '<>ptr <WCHAR>ptr u32 u32@RegType <BYTE>ptr u32 -> i32');
|
|
39
|
+
/** 读键值(两跳模式:第一跳 lpData=NULL 取尺寸/类型,第二跳按尺寸带缓冲读数据)
|
|
40
|
+
* @returns 0 = ERROR_SUCCESS;2 = ERROR_FILE_NOT_FOUND(值不存在)
|
|
41
|
+
* @param args_0 hKey 键句柄(须 KEY_QUERY_VALUE 权限,含于 gui.RegAccess.READ)
|
|
42
|
+
* @param args_1 lpValueName 值名(NULL = 键的默认值)
|
|
43
|
+
* @param args_2 lpReserved 保留,恒 NULL
|
|
44
|
+
* @param args_3 lpType 出参:值类型(可 NULL)
|
|
45
|
+
* @param args_4 lpData 数据缓冲(可 NULL = 只查尺寸)
|
|
46
|
+
* @param args_5 lpcbData 双向:入 = lpData 缓冲字节数(须预写)、出 = 实际字节数 */
|
|
47
|
+
export const RegQueryValueEx = /*@__PURE__*/ b('RegQueryValueExW', '<>ptr <WCHAR>ptr <>ptr <BYTE>ptr <BYTE>ptr <BYTE>ptr -> i32');
|
|
48
|
+
/** 删除子键(连值一起删);须父键 KEY_CREATE_SUB_KEY 权限——自己的 HKCU 直接传
|
|
49
|
+
* gui.HKey.CURRENT_USER 即可
|
|
50
|
+
* @returns 0 = ERROR_SUCCESS;2 = 子键不存在
|
|
51
|
+
* @param args_0 hKey 父键
|
|
52
|
+
* @param args_1 lpSubKey 要删的子键路径(不可 NULL) */
|
|
53
|
+
export const RegDeleteKey = /*@__PURE__*/ b('RegDeleteKeyW', '<>ptr <WCHAR>ptr -> i32');
|