-
Notifications
You must be signed in to change notification settings - Fork 2.3k
fix: 在 Windows 平台加入中文路径支持 #753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||||||||||||||||
| import { join, dirname, basename } from 'path' | ||||||||||||||||||||
| import { appendFileSync, existsSync, mkdirSync, readdirSync, statSync, readFileSync } from 'fs' | ||||||||||||||||||||
| import { appendFileSync, existsSync, mkdirSync, readdirSync, statSync, readFileSync, symlinkSync, rmdirSync, linkSync } from 'fs' | ||||||||||||||||||||
| import { tmpdir } from 'os' | ||||||||||||||||||||
| import * as fzstd from 'fzstd' | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -22,6 +22,9 @@ export class WcdbCore { | |||||||||||||||||||
| private currentKey: string | null = null | ||||||||||||||||||||
| private currentWxid: string | null = null | ||||||||||||||||||||
| private currentDbStoragePath: string | null = null | ||||||||||||||||||||
| private kernel32: any = null | ||||||||||||||||||||
| private dbJunctionPath: string | null = null | ||||||||||||||||||||
| private dbJunctionTarget: string | null = null | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // 函数引用 | ||||||||||||||||||||
| private wcdbInitProtection: any = null | ||||||||||||||||||||
|
|
@@ -131,6 +134,57 @@ export class WcdbCore { | |||||||||||||||||||
| private lastCursorForceReopenAt = 0 | ||||||||||||||||||||
| private readonly cursorForceReopenCooldownMs = 15000 | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private resolveShortPath(longPath: string): string | null { | ||||||||||||||||||||
| if (process.platform !== 'win32' || !this.kernel32?.GetShortPathNameW) return longPath | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| const buf = Buffer.alloc(520) // MAX_PATH * 2 for UTF-16 | ||||||||||||||||||||
| const len = this.kernel32.GetShortPathNameW(longPath, buf, 260) | ||||||||||||||||||||
| if (len > 0 && len < 260) { | ||||||||||||||||||||
| return buf.toString('utf16le', 0, len * 2) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||
| this.writeLog(`[wcdbCore] resolveShortPath failed: ${String(e)}`, true) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| return null | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** | ||||||||||||||||||||
| * 为包含非 ASCII 字符的路径创建 Windows Junction Point | ||||||||||||||||||||
| */ | ||||||||||||||||||||
| private ensureDbJunction(dbBasePath: string): string { | ||||||||||||||||||||
| if (process.platform !== 'win32') return dbBasePath | ||||||||||||||||||||
| if (!/[^\x00-\x7F]/.test(dbBasePath)) return dbBasePath | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| const mountPoint = join(tmpdir(), 'weflow_db_junc') | ||||||||||||||||||||
| if (this.dbJunctionPath === mountPoint && this.dbJunctionTarget === dbBasePath && existsSync(mountPoint)) { | ||||||||||||||||||||
| return mountPoint | ||||||||||||||||||||
| } | ||||||||||||||||||||
| if (existsSync(mountPoint)) { | ||||||||||||||||||||
| try { rmdirSync(mountPoint) } catch {} | ||||||||||||||||||||
| } | ||||||||||||||||||||
| symlinkSync(dbBasePath, mountPoint, 'junction') | ||||||||||||||||||||
| this.dbJunctionPath = mountPoint | ||||||||||||||||||||
|
Comment on lines
+154
to
+166
|
||||||||||||||||||||
| this.dbJunctionTarget = dbBasePath | ||||||||||||||||||||
| this.writeLog(`[wcdbCore] junction created: ${mountPoint} -> ${dbBasePath}`, true) | ||||||||||||||||||||
| return mountPoint | ||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||
| this.writeLog(`[wcdbCore] junction failed: ${String(e)}`, true) | ||||||||||||||||||||
| return dbBasePath | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private cleanupDbJunction() { | ||||||||||||||||||||
| if (process.platform !== 'win32' || !this.dbJunctionPath) return | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| if (existsSync(this.dbJunctionPath)) rmdirSync(this.dbJunctionPath) | ||||||||||||||||||||
| this.writeLog(`[wcdbCore] junction removed: ${this.dbJunctionPath}`, true) | ||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||
| this.writeLog(`[wcdbCore] junction removal failed: ${String(e)}`, true) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| this.dbJunctionPath = null | ||||||||||||||||||||
| this.dbJunctionTarget = null | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| setPaths(resourcesPath: string, userDataPath: string): void { | ||||||||||||||||||||
| this.resourcesPath = resourcesPath | ||||||||||||||||||||
| this.userDataPath = userDataPath | ||||||||||||||||||||
|
|
@@ -707,6 +761,15 @@ export class WcdbCore { | |||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (process.platform === 'win32') { | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| this.kernel32 = this.koffi.load('kernel32.dll') | ||||||||||||||||||||
| this.kernel32.GetShortPathNameW = this.kernel32.func('uint32 __stdcall GetShortPathNameW(const char16* longPath, _Out_ char16* shortPath, uint32 bufferSize)') | ||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||
| this.writeLog(`[bootstrap] failed to load kernel32 for short paths: ${String(e)}`, true) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| this.writeLog(`[bootstrap] koffi.load begin path=${dllPath}`, true) | ||||||||||||||||||||
| this.lib = this.koffi.load(dllPath) | ||||||||||||||||||||
| this.writeLog('[bootstrap] koffi.load ok', true) | ||||||||||||||||||||
|
|
@@ -1187,13 +1250,6 @@ export class WcdbCore { | |||||||||||||||||||
| // wcdb_status wcdb_cloud_init(int32_t interval_seconds) | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| this.wcdbCloudInit = this.lib.func('int32 wcdb_cloud_init(int32 intervalSeconds)') | ||||||||||||||||||||
| } catch { | ||||||||||||||||||||
| this.wcdbCloudInit = null | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // wcdb_status wcdb_cloud_report(const char* stats_json) | ||||||||||||||||||||
| try { | ||||||||||||||||||||
| this.wcdbCloudReport = this.lib.func('int32 wcdb_cloud_report(const char* statsJson)') | ||||||||||||||||||||
| } catch { | ||||||||||||||||||||
|
||||||||||||||||||||
| } catch { | |
| } catch { | |
| this.wcdbCloudInit = null | |
| } | |
| // wcdb_status wcdb_cloud_report() | |
| try { | |
| this.wcdbCloudReport = this.lib.func('int32 wcdb_cloud_report()') | |
| } catch { |
Copilot
AI
Apr 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
testConnection() 调用 ensureDbJunction() 后在多处失败分支直接返回(如 dbStorage/session.db 不存在、openAccount 失败),目前不会清理可能已创建的 junction。建议将 junction 清理放到 finally,并在成功/失败路径都执行回收。
Copilot
AI
Apr 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
testConnection() 的异常兜底现在直接返回 String(e),会丢失此前用 formatInitProtectionError(-3004) 生成的统一错误码/文案;前端(如 WelcomePage)有按 -3001 等错误码做分支处理的逻辑。建议保持返回统一的格式化错误(并可额外记录原始异常到日志)。
| return { success: false, error: String(e) } | |
| return { success: false, error: this.formatInitProtectionError(-3004) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里用
require('path').dirname与顶部的 ESM import 风格不一致,而且已从path引入了join。建议改为直接从pathimportdirname(或复用已有 import),避免在 TS 文件里混用 require。