diff --git a/packages/host/directory-picker-native/src/win32-dialog-bindings.ts b/packages/host/directory-picker-native/src/win32-dialog-bindings.ts index 654bbc5..5b3977a 100644 --- a/packages/host/directory-picker-native/src/win32-dialog-bindings.ts +++ b/packages/host/directory-picker-native/src/win32-dialog-bindings.ts @@ -37,7 +37,7 @@ interface Koffi { function readUtf16(koffi: Koffi, address: unknown): string { const bytes = Buffer.from(koffi.view(address, 32768)) let end = 0 - while (end + 1 < bytes.length && bytes[end] !== 0) end += 2 + while (end + 1 < bytes.length && (bytes[end] !== 0 || bytes[end + 1] !== 0)) end += 2 return bytes.toString('utf16le', 0, end) } diff --git a/packages/host/directory-picker-native/tests/win32-dialog-bindings.spec.ts b/packages/host/directory-picker-native/tests/win32-dialog-bindings.spec.ts index b8ff4c3..9bef599 100644 --- a/packages/host/directory-picker-native/tests/win32-dialog-bindings.spec.ts +++ b/packages/host/directory-picker-native/tests/win32-dialog-bindings.spec.ts @@ -180,6 +180,17 @@ describe('loadWin32DialogBindings over the fake COM world', () => { expect(world.uninitialized).toBe(1) }) + it('keeps paths whose code units carry a zero low byte (BMP trigger + astral surrogate)', async () => { + // Ā (U+0100) encodes as 00 01 in UTF-16LE, and 🀀 (U+1F000) ends in the + // low surrogate DC00 (00 DC): both code units have a zero low byte + // without being the NUL terminator. readUtf16 must only stop at a full + // 00 00 unit, otherwise the path is truncated at the first such char. + const world = comWorld({ path: 'C:\\Ā区\\🀀-workspace' }) + installFakeKoffi(world) + const bindings = await (await loadBindingsModule()).loadWin32DialogBindings() + expect(runFolderDialog(bindings, '选择工作区目录', vi.fn())).toBe('C:\\Ā区\\🀀-workspace') + }) + it('maps dismissal and the S_FALSE CoInitializeEx', async () => { const world = comWorld({ showHr: HRESULT_CANCELLED, coInitHr: 1 }) installFakeKoffi(world)