quickwin 2.2.0 → 2.3.1
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 +7 -7
- package/README.md +7 -7
- package/constants.ts +1070 -0
- package/enum_map.d.ts +130 -0
- package/examples/PdfCanvas.js +2 -2
- package/examples/PdfCanvas.tsx +3 -2
- package/examples/{execd.js → execd/main.js} +717 -148
- package/examples/execd/tray.js +192 -0
- package/examples/{execd_worker.js → execd/worker.js} +7 -7
- package/examples/pdf_preview2.js +46 -46
- package/examples/pdf_preview2.ts +55 -54
- package/examples/pdf_viewer.js +34 -19
- package/examples/pdf_viewer.tsx +6 -5
- package/examples/test_gallery.js +35 -20
- package/examples/test_gallery.tsx +9 -8
- package/examples/test_react_lazy.js +34 -19
- package/examples/test_react_lazy.tsx +7 -6
- package/examples/test_react_listbox.js +34 -19
- package/examples/test_react_listbox.tsx +8 -7
- package/examples/test_root_window.js +34 -19
- package/examples/test_root_window.tsx +5 -5
- package/examples/test_tab.js +34 -19
- package/examples/test_tab.tsx +7 -6
- package/examples/tray_demo.js +8 -8
- package/examples/tray_demo.tsx +9 -8
- package/lib/dumpRects.js +2 -2
- package/lib/dumpRects.ts +3 -2
- package/lib/fetch.js +15 -15
- package/lib/fetch.ts +16 -15
- package/lib/ffi/bind.js +3 -3
- package/lib/ffi/bind.ts +3 -3
- package/lib/http-server.js +54 -29
- package/lib/http-server.ts +56 -27
- package/lib/react-qw/components/Button.js +2 -2
- package/lib/react-qw/components/CheckBox.js +4 -4
- package/lib/react-qw/components/ComboBox.js +10 -10
- package/lib/react-qw/components/DateTimePicker.js +12 -12
- package/lib/react-qw/components/Input.js +7 -7
- package/lib/react-qw/components/Link.js +4 -4
- package/lib/react-qw/components/ListBox.js +12 -12
- package/lib/react-qw/components/ListView.js +35 -35
- package/lib/react-qw/components/PathPicker.js +3 -3
- package/lib/react-qw/components/ProgressBar.js +4 -4
- package/lib/react-qw/components/RadioButton.js +4 -4
- package/lib/react-qw/components/ScrollView.js +28 -28
- package/lib/react-qw/components/Slider.js +11 -11
- package/lib/react-qw/components/Tab.js +10 -10
- package/lib/react-qw/components/Tooltip.js +8 -8
- package/lib/react-qw/components/TreeView.js +20 -20
- package/lib/react-qw/index.js +5 -5
- package/lib/react-qw/index.ts +6 -5
- package/lib/react-qw/props.js +4 -4
- package/lib/react-qw/props.ts +5 -4
- package/lib/react-qw/reconciler.js +10 -10
- package/lib/react-qw/reconciler.ts +11 -10
- package/lib/text-measure.js +3 -3
- package/lib/text-measure.ts +4 -3
- package/lib/websocket.js +17 -17
- package/lib/websocket.ts +18 -17
- package/lib/windows/advapi32.js +7 -7
- package/lib/windows/advapi32.ts +7 -7
- package/lib/windows/gdi32.js +16 -16
- package/lib/windows/gdi32.ts +16 -16
- package/lib/windows/kernel32.js +41 -2
- package/lib/windows/kernel32.ts +45 -2
- package/lib/windows/shell32.js +24 -3
- package/lib/windows/shell32.ts +27 -4
- package/lib/windows/structs.js +54 -0
- package/lib/windows/structs.ts +63 -1
- package/lib/windows/user32.js +18 -6
- package/lib/windows/user32.ts +18 -6
- package/lib/windows/winspool.js +3 -3
- package/lib/windows/winspool.ts +3 -3
- package/package.json +4 -3
- package/quickwin.d.ts +32 -10
- 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/test_ffi_bind.js +7 -7
- package/test/test_ffi_bind.ts +8 -8
- package/test/test_ffi_struct.js +2 -2
- package/test/test_ffi_struct.ts +3 -3
- package/test/test_ffi_windows.js +1 -1
- package/test/test_ffi_windows.ts +1 -1
- package/test/test_files.js +7 -7
- package/test/test_files.ts +8 -8
- package/test/test_gdi_draw.js +51 -51
- package/test/test_gdi_draw.ts +57 -57
- package/test/test_http_server.js +3 -3
- package/test/test_http_server.ts +4 -3
- package/test/test_ipv6.js +3 -3
- package/test/test_ipv6.ts +4 -3
- package/test/test_net_event.js +3 -3
- package/test/test_net_event.ts +4 -3
- package/test/test_printer.js +4 -4
- package/test/test_printer.ts +5 -5
- package/test/test_react_complex.js +36 -21
- package/test/test_react_counter.js +34 -19
- package/test/test_react_flex.js +36 -21
- package/test/test_react_hidden.js +34 -19
- package/test/test_react_listview_link.js +35 -20
- package/test/test_react_notify.js +52 -37
- package/test/test_react_render.js +34 -19
- package/test/test_registry.js +6 -6
- package/test/test_registry.ts +8 -8
- package/test/test_wolfssl_read.js +8 -8
- package/test/test_wolfssl_read.ts +9 -8
- package/examples/execd.tsx +0 -798
- package/examples/execd_worker.ts +0 -127
- package/quickwin_const.d.ts +0 -1063
package/quickwin.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
/// <reference path="
|
|
2
|
-
/// <reference path="quickwin_enum_map.d.ts" />
|
|
1
|
+
/// <reference path="enum_map.d.ts" />
|
|
3
2
|
interface ImportMeta {
|
|
4
3
|
url: string
|
|
5
4
|
}
|
|
@@ -348,6 +347,12 @@ declare module "os" {
|
|
|
348
347
|
declare module "sock" {
|
|
349
348
|
type SockHandle = number & { readonly __brand: unique symbol };
|
|
350
349
|
type SockFd = number & { readonly __brand: unique symbol };
|
|
350
|
+
// 枚举唯一定义在 constants.ts(真实 .ts,tsc/esbuild 均内联);此处别名仅供签名引用,
|
|
351
|
+
// 使用方请直接 import { AddrFamily } from 'quickwin/constants'。
|
|
352
|
+
type AddrFamily = import('./constants').AddrFamily;
|
|
353
|
+
type SockType = import('./constants').SockType;
|
|
354
|
+
type Protocol = import('./constants').Protocol;
|
|
355
|
+
type Shutdown = import('./constants').Shutdown;
|
|
351
356
|
|
|
352
357
|
function socket(domain?: AddrFamily, type?: SockType, protocol?: Protocol): SockHandle;
|
|
353
358
|
function connect(sock: SockHandle, addr: string, port: number): number;
|
|
@@ -374,6 +379,10 @@ declare module "wolfssl" {
|
|
|
374
379
|
type WOLFSSL = number & { readonly __brand: unique symbol };
|
|
375
380
|
type WOLFSSL_CTX = number & { readonly __brand: unique symbol };
|
|
376
381
|
type WOLFSSL_METHOD = number & { readonly __brand: unique symbol };
|
|
382
|
+
// 枚举唯一定义在 constants.ts(见 sock 块注释)。
|
|
383
|
+
type VerifyMode = import('./constants').VerifyMode;
|
|
384
|
+
type SniType = import('./constants').SniType;
|
|
385
|
+
type FileType = import('./constants').FileType;
|
|
377
386
|
|
|
378
387
|
function wolfSSL_library_init(): number;
|
|
379
388
|
function wolfSSL_CTX_new(method: WOLFSSL_METHOD): WOLFSSL_CTX | null;
|
|
@@ -433,6 +442,18 @@ declare module "gui" {
|
|
|
433
442
|
type HFONT = number & { readonly ptrBrand: "HFONT" };
|
|
434
443
|
type HICON = number & { readonly ptrBrand: "HICON" };
|
|
435
444
|
type WNDPROC = number & { readonly ptrBrand: "WNDPROC" };
|
|
445
|
+
// 枚举唯一定义在 constants.ts(真实 .ts,tsc/esbuild 均内联);此处别名仅供签名引用,
|
|
446
|
+
// 使用方请直接 import { WindowStyle } from 'quickwin/constants'。
|
|
447
|
+
// Gwlp/ImageType 的成员访问(如 Gwlp.WNDPROC)需写 import('./constants').Gwlp.WNDPROC
|
|
448
|
+
// ——类型别名不建命名空间,无法穿透取成员。
|
|
449
|
+
type GetWindowCmd = import('./constants').GetWindowCmd;
|
|
450
|
+
type ShowWindowCmd = import('./constants').ShowWindowCmd;
|
|
451
|
+
type SetWindowPosHwnd = import('./constants').SetWindowPosHwnd;
|
|
452
|
+
type Gwlp = import('./constants').Gwlp;
|
|
453
|
+
type NotifyIconCmd = import('./constants').NotifyIconCmd;
|
|
454
|
+
type ImageType = import('./constants').ImageType;
|
|
455
|
+
type IDI = import('./constants').IDI;
|
|
456
|
+
type ScrollBar = import('./constants').ScrollBar;
|
|
436
457
|
|
|
437
458
|
function RegisterClass(className: string, wndProc?: (hwnd: HWND, msg: number, wParam: number, lParam: number) => number): number;
|
|
438
459
|
function CreateWindow(className: string, title: string, style: number, x: number, y: number, width: number, height: number, parent: HWND | null, menu: HMENU | null): HWND | null;
|
|
@@ -450,7 +471,7 @@ declare module "gui" {
|
|
|
450
471
|
function GetWindowText(hwnd: HWND): string;
|
|
451
472
|
function GetScaleFactor(): number;
|
|
452
473
|
function CreateSystemDpiFont(): HFONT | null;
|
|
453
|
-
type GwlpReturnType<T extends Gwlp> = T extends Gwlp.WNDPROC ? WNDPROC : number;
|
|
474
|
+
type GwlpReturnType<T extends Gwlp> = T extends import('./constants').Gwlp.WNDPROC ? WNDPROC : number;
|
|
454
475
|
function GetWindowLongPtr<T extends Gwlp>(hwnd: HWND, nIndex: T): GwlpReturnType<T>;
|
|
455
476
|
function SetWindowLongPtr(hwnd: HWND, nIndex: Gwlp, newLong: number): number;
|
|
456
477
|
function UnsetWindowProc(hwnd: HWND): boolean;
|
|
@@ -475,20 +496,21 @@ declare module "gui" {
|
|
|
475
496
|
* Thin wrapper around LoadImageW. Parameter order matches Win32 LoadImageW: (hinst, name, uType?, cx?, cy?, fuLoad?).
|
|
476
497
|
* hinst: null for system resources/file path, number for module handle;
|
|
477
498
|
* name: when hinst=null, a system icon IDI or .ico file path; when hinst=module handle, a resource ID (number) or resource name (string);
|
|
478
|
-
* uType:
|
|
479
|
-
* cx/cy: target size (0 for default, use
|
|
480
|
-
* fuLoad:
|
|
481
|
-
* Example:
|
|
482
|
-
* gui.LoadImage(null,
|
|
499
|
+
* uType: ImageType constant, default IMAGE_ICON (returns HICON), pass BITMAP/CURSOR for number return;
|
|
500
|
+
* cx/cy: target size (0 for default, use LoadImageFlag.DEFAULTSIZE for system default size);
|
|
501
|
+
* fuLoad: LoadImageFlag constants. Note: loading system icons (IDI_*, hinst=null) requires LoadImageFlag.SHARED.
|
|
502
|
+
* Example: import { IDI, ImageType, LoadImageFlag } from 'quickwin/constants'
|
|
503
|
+
* gui.LoadImage(null, "app.ico", ImageType.ICON, 32, 32, LoadImageFlag.LOADFROMFILE)
|
|
504
|
+
* gui.LoadImage(null, IDI.APPLICATION, ImageType.ICON, 0, 0, LoadImageFlag.SHARED)
|
|
483
505
|
*/
|
|
484
|
-
function LoadImage<H extends (import("win").HMODULE) | null, T extends ImageType = ImageType.ICON>(
|
|
506
|
+
function LoadImage<H extends (import("win").HMODULE) | null, T extends ImageType = import('./constants').ImageType.ICON>(
|
|
485
507
|
hinst: H,
|
|
486
508
|
name: H extends null ? string | IDI : string | number,
|
|
487
509
|
uType?: T,
|
|
488
510
|
cx?: number,
|
|
489
511
|
cy?: number,
|
|
490
512
|
fuLoad?: number
|
|
491
|
-
): T extends ImageType.ICON ? HICON | null : number | null;
|
|
513
|
+
): T extends import('./constants').ImageType.ICON ? HICON | null : number | null;
|
|
492
514
|
/**
|
|
493
515
|
* Loads the first icon group (RT_GROUP_ICON) from this exe, i.e. the custom icon
|
|
494
516
|
* embedded by rcedit. Works with any resource ID including 0. Returns null if the
|
package/qwin-nowasm-x86.exe
CHANGED
|
Binary file
|
package/qwin-nowasm.exe
CHANGED
|
Binary file
|
package/qwin-x86.exe
CHANGED
|
Binary file
|
package/qwin.exe
CHANGED
|
Binary file
|
package/test/test_ffi_bind.js
CHANGED
|
@@ -88,7 +88,7 @@ export const suite = {
|
|
|
88
88
|
const rect = new PtrArrayBuffer(16);
|
|
89
89
|
const dv = new DataView(rect);
|
|
90
90
|
dv.setInt32(8, 500, true);
|
|
91
|
-
const lines = user32.DrawTextW(dc, 'hello ffi-bind', -1, rect, 1024 /*
|
|
91
|
+
const lines = user32.DrawTextW(dc, 'hello ffi-bind', -1, rect, 1024 /* DrawTextFlag.CALCRECT */);
|
|
92
92
|
t.checkTrue('DrawTextW returns line count > 0', lines > 0);
|
|
93
93
|
t.checkTrue('measured width > 0', dv.getInt32(8, true) > 0);
|
|
94
94
|
t.checkTrue('measured height > 0', dv.getInt32(12, true) > 0);
|
|
@@ -529,7 +529,7 @@ export const suite = {
|
|
|
529
529
|
t.check('closure 返回 ! 非零:正常写槽', 0x3000, readScalar(new DataView(rr3), 0, 'ptr'));
|
|
530
530
|
nnRet.dispose();
|
|
531
531
|
// 返回位负例:fn 返回 0 → printf 诊断 + 不写槽;C 侧 retbuf 初值恒零
|
|
532
|
-
// (quickjs-ffi-closure.c zero8 拷贝),终值 0 与「写了 0」不可分——
|
|
532
|
+
// (src/quickjs-ffi-closure.c zero8 拷贝),终值 0 与「写了 0」不可分——
|
|
533
533
|
// 值层面只验分发不炸、槽保持 0(诊断走 stdout,见上方 printf)。
|
|
534
534
|
const nnRet0 = closure('i32 -> <>ptr!', (_n) => 0);
|
|
535
535
|
const rr4 = new ArrayBuffer(8);
|
|
@@ -587,9 +587,9 @@ export const suite = {
|
|
|
587
587
|
const drawText = bind('user32.dll', 'DrawTextW', '<>ptr! <WCHAR>ptr i32 <BYTE>ptr i32 -> i32');
|
|
588
588
|
const dtRect = new PtrArrayBuffer(16);
|
|
589
589
|
if (dcScreen) {
|
|
590
|
-
t.checkTrue('DrawTextW 非空 DC 通过 ! 参数位', drawText(dcScreen, 'hi !', -1, dtRect, 1024 /*
|
|
590
|
+
t.checkTrue('DrawTextW 非空 DC 通过 ! 参数位', drawText(dcScreen, 'hi !', -1, dtRect, 1024 /* DrawTextFlag.CALCRECT */) > 0);
|
|
591
591
|
try {
|
|
592
|
-
drawText(0, 'hi !', -1, dtRect, 1024 /*
|
|
592
|
+
drawText(0, 'hi !', -1, dtRect, 1024 /* DrawTextFlag.CALCRECT */);
|
|
593
593
|
t.checkTrue('DrawTextW NULL DC 标 ! 后应 throw', false);
|
|
594
594
|
}
|
|
595
595
|
catch (e) {
|
|
@@ -644,13 +644,13 @@ export const suite = {
|
|
|
644
644
|
if (false) {
|
|
645
645
|
const sw = bind('user32.dll', 'ShowWindow', '<X>ptr i32@ShowWindowCmd -> i32');
|
|
646
646
|
const sp = bind('user32.dll', 'SetWindowPos', '<X>ptr <>ptr@SetWindowPosHwnd i32 i32 i32 i32 u32 -> i32');
|
|
647
|
-
sw(NULL, 5 /*
|
|
647
|
+
sw(NULL, 5 /* ShowWindowCmd.SHOW */); // 枚举成员
|
|
648
648
|
sw(NULL, 5); // 域内字面量(5 = SW_SHOW)
|
|
649
|
-
sp(NULL, 0 /*
|
|
649
|
+
sp(NULL, 0 /* SetWindowPosHwnd.TOP */, 0, 0, 0, 0, 1 /* SetWindowPosFlag.SWP_NOSIZE */);
|
|
650
650
|
// @ts-expect-error 域外字面量被拦(11152 不是任何 ShowWindow 命令)
|
|
651
651
|
sw(NULL, 0x2B90);
|
|
652
652
|
// @ts-expect-error 跨枚举误用:HWND_TOP/TOPMOST 不是 ShowWindow 命令
|
|
653
|
-
sw(NULL, 0 /*
|
|
653
|
+
sw(NULL, 0 /* SetWindowPosHwnd.TOP */);
|
|
654
654
|
}
|
|
655
655
|
},
|
|
656
656
|
};
|
package/test/test_ffi_bind.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as std from 'std'
|
|
2
|
-
import * as gui from 'gui'
|
|
3
2
|
import * as ffi from 'ffi'
|
|
4
3
|
import * as os from 'os'
|
|
5
4
|
import { Tester } from './test_helper.js'
|
|
6
5
|
import { bind, bindLib, closure, WCHAR, BYTE, type CodecMap } from '../lib/ffi/bind.js'
|
|
7
6
|
import { struct } from '../lib/ffi/struct.js'
|
|
8
7
|
import { NULL, type C_Struct, type MaybePtr, Ptr, PtrArrayBuffer, readScalar, writeScalar, PTR_SIZE, normToken } from '../lib/ffi/ctype.js'
|
|
8
|
+
import { DrawTextFlag, SetWindowPosFlag, SetWindowPosHwnd, ShowWindowCmd } from '../constants'
|
|
9
9
|
|
|
10
10
|
// 编译期断言工具(仅类型层,运行时无开销)
|
|
11
11
|
type Equal<A, B> = (<G>() => G extends A ? 1 : 2) extends (<G>() => G extends B ? 1 : 2) ? true : false
|
|
@@ -86,7 +86,7 @@ export const suite = {
|
|
|
86
86
|
const rect = new PtrArrayBuffer(16)
|
|
87
87
|
const dv = new DataView(rect)
|
|
88
88
|
dv.setInt32(8, 500, true)
|
|
89
|
-
const lines = user32.DrawTextW(dc, 'hello ffi-bind', -1, rect,
|
|
89
|
+
const lines = user32.DrawTextW(dc, 'hello ffi-bind', -1, rect, DrawTextFlag.CALCRECT)
|
|
90
90
|
t.checkTrue('DrawTextW returns line count > 0', lines > 0)
|
|
91
91
|
t.checkTrue('measured width > 0', dv.getInt32(8, true) > 0)
|
|
92
92
|
t.checkTrue('measured height > 0', dv.getInt32(12, true) > 0)
|
|
@@ -541,7 +541,7 @@ export const suite = {
|
|
|
541
541
|
t.check('closure 返回 ! 非零:正常写槽', 0x3000, readScalar(new DataView(rr3), 0, 'ptr'))
|
|
542
542
|
nnRet.dispose()
|
|
543
543
|
// 返回位负例:fn 返回 0 → printf 诊断 + 不写槽;C 侧 retbuf 初值恒零
|
|
544
|
-
// (quickjs-ffi-closure.c zero8 拷贝),终值 0 与「写了 0」不可分——
|
|
544
|
+
// (src/quickjs-ffi-closure.c zero8 拷贝),终值 0 与「写了 0」不可分——
|
|
545
545
|
// 值层面只验分发不炸、槽保持 0(诊断走 stdout,见上方 printf)。
|
|
546
546
|
const nnRet0 = closure('i32 -> <>ptr!', (_n) => 0)
|
|
547
547
|
const rr4 = new ArrayBuffer(8)
|
|
@@ -602,9 +602,9 @@ export const suite = {
|
|
|
602
602
|
const dtRect = new PtrArrayBuffer(16)
|
|
603
603
|
if (dcScreen) {
|
|
604
604
|
t.checkTrue('DrawTextW 非空 DC 通过 ! 参数位',
|
|
605
|
-
drawText(dcScreen, 'hi !', -1, dtRect,
|
|
605
|
+
drawText(dcScreen, 'hi !', -1, dtRect, DrawTextFlag.CALCRECT) > 0)
|
|
606
606
|
try {
|
|
607
|
-
drawText(0, 'hi !', -1, dtRect,
|
|
607
|
+
drawText(0, 'hi !', -1, dtRect, DrawTextFlag.CALCRECT)
|
|
608
608
|
t.checkTrue('DrawTextW NULL DC 标 ! 后应 throw', false)
|
|
609
609
|
} catch (e) { t.checkTrue('! 参数位(第 1 位)throw 指明 arg#1', String(e).includes('arg#1')) }
|
|
610
610
|
}
|
|
@@ -658,13 +658,13 @@ export const suite = {
|
|
|
658
658
|
if (false) {
|
|
659
659
|
const sw = bind('user32.dll', 'ShowWindow', '<X>ptr i32@ShowWindowCmd -> i32')
|
|
660
660
|
const sp = bind('user32.dll', 'SetWindowPos', '<X>ptr <>ptr@SetWindowPosHwnd i32 i32 i32 i32 u32 -> i32')
|
|
661
|
-
sw(NULL,
|
|
661
|
+
sw(NULL, ShowWindowCmd.SHOW) // 枚举成员
|
|
662
662
|
sw(NULL, 5) // 域内字面量(5 = SW_SHOW)
|
|
663
|
-
sp(NULL,
|
|
663
|
+
sp(NULL, SetWindowPosHwnd.TOP, 0, 0, 0, 0, SetWindowPosFlag.SWP_NOSIZE)
|
|
664
664
|
// @ts-expect-error 域外字面量被拦(11152 不是任何 ShowWindow 命令)
|
|
665
665
|
sw(NULL, 0x2B90)
|
|
666
666
|
// @ts-expect-error 跨枚举误用:HWND_TOP/TOPMOST 不是 ShowWindow 命令
|
|
667
|
-
sw(NULL,
|
|
667
|
+
sw(NULL, SetWindowPosHwnd.TOP)
|
|
668
668
|
}
|
|
669
669
|
},
|
|
670
670
|
}
|
package/test/test_ffi_struct.js
CHANGED
|
@@ -724,14 +724,14 @@ export const suite = {
|
|
|
724
724
|
t.check('数字位 @ 字段归一往返', 4, dev.key);
|
|
725
725
|
// encode 必填判定:0 ∉ 值域的 @ 字段必填(省略 = 新槽写 0 = 静默违约);
|
|
726
726
|
// 0 ∈ 值域可省。下面 { key } 行能编译即 msg/hp 可省的断言。
|
|
727
|
-
EV.encode({ key: 4 /*
|
|
727
|
+
EV.encode({ key: 4 /* MouseKeyFlag.MK_SHIFT */ });
|
|
728
728
|
// @ts-expect-error key 必填:省略即编译错(0 ∉ MouseKeyFlag)
|
|
729
729
|
EV.encode({ msg: 0 });
|
|
730
730
|
if (false) {
|
|
731
731
|
// @ts-expect-error 域外字面量(3 不是任何 MK_ 标志)
|
|
732
732
|
EV.encode({ key: 3 });
|
|
733
733
|
// @ts-expect-error 跨枚举误用(WmMsg 成员不是 MouseKeyFlag)
|
|
734
|
-
EV.encode({ key: 1 /*
|
|
734
|
+
EV.encode({ key: 1 /* WmMsg.CREATE */ });
|
|
735
735
|
}
|
|
736
736
|
// 数组糖元素带 @:lower 递归归一(normToken 剥 @)+ 元素值域
|
|
737
737
|
const EA = struct('EA', { msgs: 'u32@WmMsg[4]' });
|
package/test/test_ffi_struct.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as os from 'os'
|
|
2
|
-
import * as gui from 'gui'
|
|
3
2
|
import { Tester } from './test_helper.js'
|
|
4
3
|
import { struct, union } from '../lib/ffi/struct.js'
|
|
5
4
|
import { bind, } from '../lib/ffi/bind.js'
|
|
6
5
|
import { type MaybePtr, Ptr, PtrArrayBuffer } from '../lib/ffi/ctype.js'
|
|
7
6
|
import type { C_Struct, C_Union } from '../lib/ffi/ctype.js'
|
|
7
|
+
import { MouseKeyFlag, WmMsg } from '../constants'
|
|
8
8
|
|
|
9
9
|
// 编译期断言工具(仅类型层,运行时无开销)
|
|
10
10
|
type Equal<A, B> = (<G>() => G extends A ? 1 : 2) extends (<G>() => G extends B ? 1 : 2) ? true : false
|
|
@@ -769,14 +769,14 @@ export const suite = {
|
|
|
769
769
|
|
|
770
770
|
// encode 必填判定:0 ∉ 值域的 @ 字段必填(省略 = 新槽写 0 = 静默违约);
|
|
771
771
|
// 0 ∈ 值域可省。下面 { key } 行能编译即 msg/hp 可省的断言。
|
|
772
|
-
EV.encode({ key:
|
|
772
|
+
EV.encode({ key: MouseKeyFlag.MK_SHIFT })
|
|
773
773
|
// @ts-expect-error key 必填:省略即编译错(0 ∉ MouseKeyFlag)
|
|
774
774
|
EV.encode({ msg: 0 })
|
|
775
775
|
if (false) {
|
|
776
776
|
// @ts-expect-error 域外字面量(3 不是任何 MK_ 标志)
|
|
777
777
|
EV.encode({ key: 3 })
|
|
778
778
|
// @ts-expect-error 跨枚举误用(WmMsg 成员不是 MouseKeyFlag)
|
|
779
|
-
EV.encode({ key:
|
|
779
|
+
EV.encode({ key: WmMsg.CREATE })
|
|
780
780
|
}
|
|
781
781
|
|
|
782
782
|
// 数组糖元素带 @:lower 递归归一(normToken 剥 @)+ 元素值域
|
package/test/test_ffi_windows.js
CHANGED
|
@@ -74,7 +74,7 @@ export const suite = {
|
|
|
74
74
|
if (himl !== 0)
|
|
75
75
|
t.checkTrue('ImageListDestroy(himl) 成功', ImageListDestroy(himl) !== 0);
|
|
76
76
|
t.section('user32: LoadImage 双变体(string 自动编码 / ordinal MAKEINTRESOURCE)');
|
|
77
|
-
// Win32 IMAGE_* 真值:BITMAP=0 CURSOR=1 ICON=2;LR_SHARED=0x8000(注意
|
|
77
|
+
// Win32 IMAGE_* 真值:BITMAP=0 CURSOR=1 ICON=2;LR_SHARED=0x8000(注意 ImageType d.ts 当前值与此不一致)
|
|
78
78
|
t.checkTrue('LoadImageOrdinal(NULL, IDI_APPLICATION, IMAGE_ICON, LR_SHARED) 非 0', LoadImageOrdinal(NULL, 32512, 2, 0, 0, 0x8000) !== 0);
|
|
79
79
|
t.check('LoadImage(LOADFROMFILE, 不存在文件) → 0', 0, LoadImage(NULL, 'qw_no_such_icon_xyz.ico', 2, 0, 0, 0x10));
|
|
80
80
|
t.section('user32: 滚动条(入参对象直传 / 出参 encode → call → decode)');
|
package/test/test_ffi_windows.ts
CHANGED
|
@@ -83,7 +83,7 @@ export const suite = {
|
|
|
83
83
|
if (himl !== 0) t.checkTrue('ImageListDestroy(himl) 成功', ImageListDestroy(himl) !== 0)
|
|
84
84
|
|
|
85
85
|
t.section('user32: LoadImage 双变体(string 自动编码 / ordinal MAKEINTRESOURCE)')
|
|
86
|
-
// Win32 IMAGE_* 真值:BITMAP=0 CURSOR=1 ICON=2;LR_SHARED=0x8000(注意
|
|
86
|
+
// Win32 IMAGE_* 真值:BITMAP=0 CURSOR=1 ICON=2;LR_SHARED=0x8000(注意 ImageType d.ts 当前值与此不一致)
|
|
87
87
|
t.checkTrue('LoadImageOrdinal(NULL, IDI_APPLICATION, IMAGE_ICON, LR_SHARED) 非 0',
|
|
88
88
|
LoadImageOrdinal(NULL, 32512, 2, 0, 0, 0x8000) !== 0)
|
|
89
89
|
t.check('LoadImage(LOADFROMFILE, 不存在文件) → 0', 0,
|
package/test/test_files.js
CHANGED
|
@@ -40,7 +40,7 @@ export const suite = {
|
|
|
40
40
|
const pathC = `${dir}\\c.txt`;
|
|
41
41
|
const pathCn = `${dir}\\中文文件.txt`;
|
|
42
42
|
t.section('CreateFile + WriteFile: write payload');
|
|
43
|
-
const hW = CreateFile(pathA, GENERIC_WRITE, 0, NULL, 2 /*
|
|
43
|
+
const hW = CreateFile(pathA, GENERIC_WRITE, 0, NULL, 2 /* FileCreation.CREATE_ALWAYS */, 0x80 /* FILE_ATTRIBUTE_NORMAL */, NULL);
|
|
44
44
|
t.checkTrue(`write handle valid (${hW})`, hW !== -1); // 失败 = INVALID_HANDLE_VALUE(-1)
|
|
45
45
|
const payload = 'hello files';
|
|
46
46
|
const wData = toWide(payload);
|
|
@@ -49,34 +49,34 @@ export const suite = {
|
|
|
49
49
|
t.check('bytes written', payload.length * 2, new DataView(pWr).getUint32(0, true));
|
|
50
50
|
t.checkTrue('CloseHandle (writer)', CloseHandle(hW) !== 0);
|
|
51
51
|
t.section('CreateFile(READ) + GetFileSize + SetFilePointer + ReadFile');
|
|
52
|
-
const hR = CreateFile(pathA, GENERIC_READ, 0, NULL, 3 /*
|
|
52
|
+
const hR = CreateFile(pathA, GENERIC_READ, 0, NULL, 3 /* FileCreation.OPEN_EXISTING */, 0x80, NULL);
|
|
53
53
|
t.checkTrue(`read handle valid (${hR})`, hR !== -1);
|
|
54
54
|
t.check('GetFileSize', payload.length * 2, GetFileSize(hR, NULL));
|
|
55
|
-
t.check('SetFilePointer BEGIN → 0', 0, SetFilePointer(hR, 0, NULL, 0 /*
|
|
55
|
+
t.check('SetFilePointer BEGIN → 0', 0, SetFilePointer(hR, 0, NULL, 0 /* FileSeekFrom.BEGIN */));
|
|
56
56
|
const rData = new PtrArrayBuffer(payload.length * 2);
|
|
57
57
|
const pRd = new PtrArrayBuffer(4);
|
|
58
58
|
t.checkTrue('ReadFile ok', ReadFile(hR, rData, payload.length * 2, pRd, NULL) !== 0);
|
|
59
59
|
t.check('read roundtrip', payload, fromWide(rData, payload.length));
|
|
60
60
|
// seek 到 END-4 验证负偏移 + 读尾 4 字节
|
|
61
|
-
t.check('SetFilePointer END-4', payload.length * 2 - 4, SetFilePointer(hR, -4, NULL, 2 /*
|
|
61
|
+
t.check('SetFilePointer END-4', payload.length * 2 - 4, SetFilePointer(hR, -4, NULL, 2 /* FileSeekFrom.END */));
|
|
62
62
|
const rTail = new PtrArrayBuffer(8);
|
|
63
63
|
ReadFile(hR, rTail, 4, pRd, NULL);
|
|
64
64
|
t.check('seeked tail', payload.slice(-2), fromWide(rTail, 2));
|
|
65
65
|
CloseHandle(hR);
|
|
66
66
|
t.section('CopyFile + MoveFileEx + GetFileAttributes');
|
|
67
|
-
const hB = CreateFile(pathB, GENERIC_WRITE, 0, NULL, 2 /*
|
|
67
|
+
const hB = CreateFile(pathB, GENERIC_WRITE, 0, NULL, 2 /* FileCreation.CREATE_ALWAYS */, 0x80, NULL);
|
|
68
68
|
const bData = toWide('bb');
|
|
69
69
|
WriteFile(hB, bData, 4, pWr, NULL);
|
|
70
70
|
CloseHandle(hB);
|
|
71
71
|
t.checkTrue('CopyFile ok', CopyFile(pathB, pathC, 0) !== 0);
|
|
72
|
-
const hC = CreateFile(pathC, GENERIC_READ, 0, NULL, 3 /*
|
|
72
|
+
const hC = CreateFile(pathC, GENERIC_READ, 0, NULL, 3 /* FileCreation.OPEN_EXISTING */, 0x80, NULL);
|
|
73
73
|
t.check('copied size', 4, GetFileSize(hC, NULL));
|
|
74
74
|
t.checkTrue('CloseHandle (copy reader)', CloseHandle(hC) !== 0);
|
|
75
75
|
t.checkTrue('MoveFileEx replace ok', MoveFileEx(pathB, pathC, 0x1 /* MOVEFILE_REPLACE_EXISTING */) !== 0);
|
|
76
76
|
t.check('moved: source gone', INVALID32, GetFileAttributes(pathB));
|
|
77
77
|
t.checkTrue('dest is file (not dir)', (GetFileAttributes(pathC) & FILE_ATTRIBUTE_DIRECTORY) === 0);
|
|
78
78
|
t.section('FindFirstFile/FindNextFile: enumerate *.txt incl. CJK name');
|
|
79
|
-
const hCn = CreateFile(pathCn, GENERIC_WRITE, 0, NULL, 2 /*
|
|
79
|
+
const hCn = CreateFile(pathCn, GENERIC_WRITE, 0, NULL, 2 /* FileCreation.CREATE_ALWAYS */, 0x80, NULL);
|
|
80
80
|
CloseHandle(hCn); // 0 字节中文名文件
|
|
81
81
|
const fd = WIN32_FIND_DATAW.alloc();
|
|
82
82
|
const hFind = FindFirstFile(`${dir}\\a.txt`, fd.ptr);
|
package/test/test_files.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as gui from 'gui'
|
|
2
1
|
import { WCHAR } from '../lib/ffi/bind.js'
|
|
3
2
|
import { NULL, PtrArrayBuffer } from '../lib/ffi/ctype.js'
|
|
4
3
|
import {
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
} from '../lib/windows/kernel32.js'
|
|
9
8
|
import { WIN32_FIND_DATAW } from '../lib/windows/structs.js'
|
|
10
9
|
import { Tester } from './test_helper.js'
|
|
10
|
+
import { FileCreation, FileSeekFrom } from '../constants'
|
|
11
11
|
|
|
12
12
|
const GENERIC_READ = 0x80000000
|
|
13
13
|
const GENERIC_WRITE = 0x40000000
|
|
@@ -49,7 +49,7 @@ export const suite = {
|
|
|
49
49
|
|
|
50
50
|
t.section('CreateFile + WriteFile: write payload')
|
|
51
51
|
const hW = CreateFile(pathA, GENERIC_WRITE, 0, NULL,
|
|
52
|
-
|
|
52
|
+
FileCreation.CREATE_ALWAYS, 0x80 /* FILE_ATTRIBUTE_NORMAL */, NULL)
|
|
53
53
|
t.checkTrue(`write handle valid (${hW})`, hW !== -1) // 失败 = INVALID_HANDLE_VALUE(-1)
|
|
54
54
|
const payload = 'hello files'
|
|
55
55
|
const wData = toWide(payload)
|
|
@@ -60,17 +60,17 @@ export const suite = {
|
|
|
60
60
|
|
|
61
61
|
t.section('CreateFile(READ) + GetFileSize + SetFilePointer + ReadFile')
|
|
62
62
|
const hR = CreateFile(pathA, GENERIC_READ, 0, NULL,
|
|
63
|
-
|
|
63
|
+
FileCreation.OPEN_EXISTING, 0x80, NULL)
|
|
64
64
|
t.checkTrue(`read handle valid (${hR})`, hR !== -1)
|
|
65
65
|
t.check('GetFileSize', payload.length * 2, GetFileSize(hR, NULL))
|
|
66
|
-
t.check('SetFilePointer BEGIN → 0', 0, SetFilePointer(hR, 0, NULL,
|
|
66
|
+
t.check('SetFilePointer BEGIN → 0', 0, SetFilePointer(hR, 0, NULL, FileSeekFrom.BEGIN))
|
|
67
67
|
const rData = new PtrArrayBuffer(payload.length * 2)
|
|
68
68
|
const pRd = new PtrArrayBuffer(4)
|
|
69
69
|
t.checkTrue('ReadFile ok', ReadFile(hR, rData, payload.length * 2, pRd, NULL) !== 0)
|
|
70
70
|
t.check('read roundtrip', payload, fromWide(rData, payload.length))
|
|
71
71
|
// seek 到 END-4 验证负偏移 + 读尾 4 字节
|
|
72
72
|
t.check('SetFilePointer END-4', payload.length * 2 - 4,
|
|
73
|
-
SetFilePointer(hR, -4, NULL,
|
|
73
|
+
SetFilePointer(hR, -4, NULL, FileSeekFrom.END))
|
|
74
74
|
const rTail = new PtrArrayBuffer(8)
|
|
75
75
|
ReadFile(hR, rTail, 4, pRd, NULL)
|
|
76
76
|
t.check('seeked tail', payload.slice(-2), fromWide(rTail, 2))
|
|
@@ -78,13 +78,13 @@ export const suite = {
|
|
|
78
78
|
|
|
79
79
|
t.section('CopyFile + MoveFileEx + GetFileAttributes')
|
|
80
80
|
const hB = CreateFile(pathB, GENERIC_WRITE, 0, NULL,
|
|
81
|
-
|
|
81
|
+
FileCreation.CREATE_ALWAYS, 0x80, NULL)
|
|
82
82
|
const bData = toWide('bb')
|
|
83
83
|
WriteFile(hB, bData, 4, pWr, NULL)
|
|
84
84
|
CloseHandle(hB)
|
|
85
85
|
t.checkTrue('CopyFile ok', CopyFile(pathB, pathC, 0) !== 0)
|
|
86
86
|
const hC = CreateFile(pathC, GENERIC_READ, 0, NULL,
|
|
87
|
-
|
|
87
|
+
FileCreation.OPEN_EXISTING, 0x80, NULL)
|
|
88
88
|
t.check('copied size', 4, GetFileSize(hC, NULL))
|
|
89
89
|
t.checkTrue('CloseHandle (copy reader)', CloseHandle(hC) !== 0)
|
|
90
90
|
t.checkTrue('MoveFileEx replace ok',
|
|
@@ -95,7 +95,7 @@ export const suite = {
|
|
|
95
95
|
|
|
96
96
|
t.section('FindFirstFile/FindNextFile: enumerate *.txt incl. CJK name')
|
|
97
97
|
const hCn = CreateFile(pathCn, GENERIC_WRITE, 0, NULL,
|
|
98
|
-
|
|
98
|
+
FileCreation.CREATE_ALWAYS, 0x80, NULL)
|
|
99
99
|
CloseHandle(hCn) // 0 字节中文名文件
|
|
100
100
|
const fd = WIN32_FIND_DATAW.alloc()
|
|
101
101
|
const hFind = FindFirstFile(`${dir}\\a.txt`, fd.ptr)
|