修复闪退问题

This commit is contained in:
cc
2026-03-21 19:49:38 +08:00
parent 3f4a4f7581
commit e32261d274
4 changed files with 86 additions and 41 deletions

View File

@@ -6,7 +6,7 @@ import * as https from 'https'
import * as http from 'http' import * as http from 'http'
import * as fzstd from 'fzstd' import * as fzstd from 'fzstd'
import * as crypto from 'crypto' import * as crypto from 'crypto'
import { app, BrowserWindow } from 'electron' import { app, BrowserWindow, dialog } from 'electron'
import { ConfigService } from './config' import { ConfigService } from './config'
import { wcdbService } from './wcdbService' import { wcdbService } from './wcdbService'
import { MessageCacheService } from './messageCacheService' import { MessageCacheService } from './messageCacheService'
@@ -292,6 +292,7 @@ class ChatService {
private readonly allGroupSessionIdsCacheTtlMs = 5 * 60 * 1000 private readonly allGroupSessionIdsCacheTtlMs = 5 * 60 * 1000
private groupMyMessageCountCacheScope = '' private groupMyMessageCountCacheScope = ''
private groupMyMessageCountMemoryCache = new Map<string, GroupMyMessageCountCacheEntry>() private groupMyMessageCountMemoryCache = new Map<string, GroupMyMessageCountCacheEntry>()
private initFailureDialogShown = false
constructor() { constructor() {
this.configService = new ConfigService() this.configService = new ConfigService()
@@ -338,6 +339,55 @@ class ChatService {
return true return true
} }
private extractErrorCode(message?: string): number | null {
const text = String(message || '').trim()
if (!text) return null
const match = text.match(/(?:错误码\s*[:]\s*|\()(-?\d{2,6})(?:\)|\b)/)
if (!match) return null
const parsed = Number(match[1])
return Number.isFinite(parsed) ? parsed : null
}
private toCodeOnlyMessage(rawMessage?: string, fallbackCode = -3999): string {
const code = this.extractErrorCode(rawMessage) ?? fallbackCode
return `错误码: ${code}`
}
private async maybeShowInitFailureDialog(errorMessage: string): Promise<void> {
if (!app.isPackaged) return
if (this.initFailureDialogShown) return
const code = this.extractErrorCode(errorMessage)
if (code === null) return
const isSecurityCode =
code === -101 ||
code === -102 ||
code === -2299 ||
code === -2301 ||
code === -2302 ||
code === -1006 ||
(code <= -2201 && code >= -2212)
if (!isSecurityCode) return
this.initFailureDialogShown = true
const detail = [
`错误码: ${code}`
].join('\n')
try {
await dialog.showMessageBox({
type: 'error',
title: 'WeFlow 启动失败',
message: '启动失败,请反馈错误码。',
detail,
buttons: ['确定'],
noLink: true
})
} catch {
// 弹窗失败不阻断主流程
}
}
/** /**
* 连接数据库 * 连接数据库
*/ */
@@ -362,7 +412,9 @@ class ChatService {
const cleanedWxid = this.cleanAccountDirName(wxid) const cleanedWxid = this.cleanAccountDirName(wxid)
const openOk = await wcdbService.open(dbPath, decryptKey, cleanedWxid) const openOk = await wcdbService.open(dbPath, decryptKey, cleanedWxid)
if (!openOk) { if (!openOk) {
return { success: false, error: 'WCDB 打开失败,请检查路径和密钥' } const detailedError = this.toCodeOnlyMessage(await wcdbService.getLastInitError())
await this.maybeShowInitFailureDialog(detailedError)
return { success: false, error: detailedError }
} }
this.connected = true this.connected = true
@@ -376,7 +428,7 @@ class ChatService {
return { success: true } return { success: true }
} catch (e) { } catch (e) {
console.error('ChatService: 连接数据库失败:', e) console.error('ChatService: 连接数据库失败:', e)
return { success: false, error: String(e) } return { success: false, error: this.toCodeOnlyMessage(String(e), -3998) }
} }
} }

View File

@@ -126,6 +126,10 @@ export class WcdbCore {
this.writeLog(`[bootstrap] setPaths resourcesPath=${resourcesPath} userDataPath=${userDataPath}`, true) this.writeLog(`[bootstrap] setPaths resourcesPath=${resourcesPath} userDataPath=${userDataPath}`, true)
} }
getLastInitError(): string | null {
return lastDllInitError
}
setLogEnabled(enabled: boolean): void { setLogEnabled(enabled: boolean): void {
this.logEnabled = enabled this.logEnabled = enabled
this.writeLog(`[bootstrap] setLogEnabled=${enabled ? '1' : '0'} env.WCDB_LOG_ENABLED=${process.env.WCDB_LOG_ENABLED || ''}`, true) this.writeLog(`[bootstrap] setLogEnabled=${enabled ? '1' : '0'} env.WCDB_LOG_ENABLED=${process.env.WCDB_LOG_ENABLED || ''}`, true)
@@ -300,23 +304,7 @@ export class WcdbCore {
} }
private formatInitProtectionError(code: number): string { private formatInitProtectionError(code: number): string {
switch (code) { return `错误码: ${code}`
case -101: return '安全校验失败:授权已过期(-101'
case -102: return '安全校验失败:关键环境文件缺失(-102'
case -2201: return '安全校验失败:未找到签名清单(-2201'
case -2202: return '安全校验失败:缺少签名文件(-2202'
case -2203: return '安全校验失败:读取签名清单失败(-2203'
case -2204: return '安全校验失败:读取签名文件失败(-2204'
case -2205: return '安全校验失败:签名内容格式无效(-2205'
case -2206: return '安全校验失败:签名清单解析失败(-2206'
case -2207: return '安全校验失败:清单平台与当前平台不匹配(-2207'
case -2208: return '安全校验失败:目标文件哈希读取失败(-2208'
case -2209: return '安全校验失败:目标文件哈希不匹配(-2209'
case -2210: return '安全校验失败:签名无效(-2210'
case -2211: return '安全校验失败:主程序 EXE 哈希不匹配(-2211'
case -2212: return '安全校验失败wcdb_api 模块哈希不匹配(-2212'
default: return `安全校验失败(错误码: ${code}`
}
} }
private isLogEnabled(): boolean { private isLogEnabled(): boolean {
@@ -640,7 +628,9 @@ export class WcdbCore {
} }
} }
this.writeLog(`[bootstrap] koffi.load begin path=${dllPath}`, true)
this.lib = this.koffi.load(dllPath) this.lib = this.koffi.load(dllPath)
this.writeLog('[bootstrap] koffi.load ok', true)
// InitProtection (Added for security) // InitProtection (Added for security)
try { try {
@@ -666,6 +656,7 @@ export class WcdbCore {
} }
for (const resPath of resourcePaths) { for (const resPath of resourcePaths) {
try { try {
this.writeLog(`[bootstrap] InitProtection call path=${resPath}`, true)
protectionCode = Number(this.wcdbInitProtection(resPath)) protectionCode = Number(this.wcdbInitProtection(resPath))
if (protectionCode === 0) { if (protectionCode === 0) {
protectionOk = true protectionOk = true
@@ -687,7 +678,7 @@ export class WcdbCore {
return false return false
} }
} catch (e) { } catch (e) {
lastDllInitError = `InitProtection symbol not found: ${String(e)}` lastDllInitError = this.formatInitProtectionError(-2301)
this.writeLog(`[bootstrap] InitProtection symbol load failed: ${String(e)}`, true) this.writeLog(`[bootstrap] InitProtection symbol load failed: ${String(e)}`, true)
return false return false
} }
@@ -1107,7 +1098,7 @@ export class WcdbCore {
const initResult = this.wcdbInit() const initResult = this.wcdbInit()
if (initResult !== 0) { if (initResult !== 0) {
console.error('WCDB 初始化失败:', initResult) console.error('WCDB 初始化失败:', initResult)
lastDllInitError = `初始化失败(错误码: ${initResult}` lastDllInitError = this.formatInitProtectionError(initResult)
return false return false
} }
@@ -1118,14 +1109,7 @@ export class WcdbCore {
const errorMsg = e instanceof Error ? e.message : String(e) const errorMsg = e instanceof Error ? e.message : String(e)
console.error('WCDB 初始化异常:', errorMsg) console.error('WCDB 初始化异常:', errorMsg)
this.writeLog(`WCDB 初始化异常: ${errorMsg}`, true) this.writeLog(`WCDB 初始化异常: ${errorMsg}`, true)
lastDllInitError = errorMsg lastDllInitError = this.formatInitProtectionError(-2302)
// 检查是否是常见的 VC++ 运行时缺失错误
if (errorMsg.includes('126') || errorMsg.includes('找不到指定的模块') ||
errorMsg.includes('The specified module could not be found')) {
lastDllInitError = '可能缺少 Visual C++ 运行时库。请安装 Microsoft Visual C++ Redistributable (x64)。'
} else if (errorMsg.includes('193') || errorMsg.includes('不是有效的 Win32 应用程序')) {
lastDllInitError = 'DLL 架构不匹配。请确保使用 64 位版本的应用程序。'
}
return false return false
} }
} }
@@ -1152,8 +1136,7 @@ export class WcdbCore {
if (!this.initialized) { if (!this.initialized) {
const initOk = await this.initialize() const initOk = await this.initialize()
if (!initOk) { if (!initOk) {
// 返回更详细的错误信息,帮助用户诊断问题 const detailedError = lastDllInitError || this.formatInitProtectionError(-2303)
const detailedError = lastDllInitError || 'WCDB 初始化失败'
return { success: false, error: detailedError } return { success: false, error: detailedError }
} }
} }
@@ -1163,7 +1146,7 @@ export class WcdbCore {
this.writeLog(`testConnection dbPath=${dbPath} wxid=${wxid} dbStorage=${dbStoragePath || 'null'}`) this.writeLog(`testConnection dbPath=${dbPath} wxid=${wxid} dbStorage=${dbStoragePath || 'null'}`)
if (!dbStoragePath || !existsSync(dbStoragePath)) { if (!dbStoragePath || !existsSync(dbStoragePath)) {
return { success: false, error: `数据库目录不存在: ${dbPath}` } return { success: false, error: this.formatInitProtectionError(-3001) }
} }
// 递归查找 session.db // 递归查找 session.db
@@ -1171,7 +1154,7 @@ export class WcdbCore {
this.writeLog(`testConnection sessionDb=${sessionDbPath || 'null'}`) this.writeLog(`testConnection sessionDb=${sessionDbPath || 'null'}`)
if (!sessionDbPath) { if (!sessionDbPath) {
return { success: false, error: `未找到 session.db 文件` } return { success: false, error: this.formatInitProtectionError(-3002) }
} }
// 分配输出参数内存 // 分配输出参数内存
@@ -1180,17 +1163,13 @@ export class WcdbCore {
if (result !== 0) { if (result !== 0) {
await this.printLogs() await this.printLogs()
let errorMsg = '数据库打开失败'
if (result === -1) errorMsg = '参数错误'
else if (result === -2) errorMsg = '密钥错误'
else if (result === -3) errorMsg = '数据库打开失败'
this.writeLog(`testConnection openAccount failed code=${result}`) this.writeLog(`testConnection openAccount failed code=${result}`)
return { success: false, error: `${errorMsg} (错误码: ${result})` } return { success: false, error: this.formatInitProtectionError(result) }
} }
const tempHandle = handleOut[0] const tempHandle = handleOut[0]
if (tempHandle <= 0) { if (tempHandle <= 0) {
return { success: false, error: '无效的数据库句柄' } return { success: false, error: this.formatInitProtectionError(-3003) }
} }
// 测试成功:使用 shutdown 清理资源(包括测试句柄) // 测试成功:使用 shutdown 清理资源(包括测试句柄)
@@ -1219,7 +1198,7 @@ export class WcdbCore {
} catch (e) { } catch (e) {
console.error('测试连接异常:', e) console.error('测试连接异常:', e)
this.writeLog(`testConnection exception: ${String(e)}`) this.writeLog(`testConnection exception: ${String(e)}`)
return { success: false, error: String(e) } return { success: false, error: this.formatInitProtectionError(-3004) }
} }
} }
@@ -1411,6 +1390,7 @@ export class WcdbCore {
*/ */
async open(dbPath: string, hexKey: string, wxid: string): Promise<boolean> { async open(dbPath: string, hexKey: string, wxid: string): Promise<boolean> {
try { try {
lastDllInitError = null
if (!this.initialized) { if (!this.initialized) {
const initOk = await this.initialize() const initOk = await this.initialize()
if (!initOk) return false if (!initOk) return false
@@ -1438,6 +1418,7 @@ export class WcdbCore {
if (!dbStoragePath || !existsSync(dbStoragePath)) { if (!dbStoragePath || !existsSync(dbStoragePath)) {
console.error('数据库目录不存在:', dbPath) console.error('数据库目录不存在:', dbPath)
this.writeLog(`open failed: dbStorage not found for ${dbPath}`) this.writeLog(`open failed: dbStorage not found for ${dbPath}`)
lastDllInitError = this.formatInitProtectionError(-3001)
return false return false
} }
@@ -1446,6 +1427,7 @@ export class WcdbCore {
if (!sessionDbPath) { if (!sessionDbPath) {
console.error('未找到 session.db 文件') console.error('未找到 session.db 文件')
this.writeLog('open failed: session.db not found') this.writeLog('open failed: session.db not found')
lastDllInitError = this.formatInitProtectionError(-3002)
return false return false
} }
@@ -1456,11 +1438,13 @@ export class WcdbCore {
console.error('打开数据库失败:', result) console.error('打开数据库失败:', result)
await this.printLogs() await this.printLogs()
this.writeLog(`open failed: openAccount code=${result}`) this.writeLog(`open failed: openAccount code=${result}`)
lastDllInitError = this.formatInitProtectionError(result)
return false return false
} }
const handle = handleOut[0] const handle = handleOut[0]
if (handle <= 0) { if (handle <= 0) {
lastDllInitError = this.formatInitProtectionError(-3003)
return false return false
} }
@@ -1470,6 +1454,7 @@ export class WcdbCore {
this.currentWxid = wxid this.currentWxid = wxid
this.currentDbStoragePath = dbStoragePath this.currentDbStoragePath = dbStoragePath
this.initialized = true this.initialized = true
lastDllInitError = null
if (this.wcdbSetMyWxid && wxid) { if (this.wcdbSetMyWxid && wxid) {
try { try {
this.wcdbSetMyWxid(this.handle, wxid) this.wcdbSetMyWxid(this.handle, wxid)
@@ -1487,6 +1472,7 @@ export class WcdbCore {
} catch (e) { } catch (e) {
console.error('打开数据库异常:', e) console.error('打开数据库异常:', e)
this.writeLog(`open exception: ${String(e)}`) this.writeLog(`open exception: ${String(e)}`)
lastDllInitError = this.formatInitProtectionError(-3004)
return false return false
} }
} }

View File

@@ -164,6 +164,10 @@ export class WcdbService {
return this.callWorker('open', { dbPath, hexKey, wxid }) return this.callWorker('open', { dbPath, hexKey, wxid })
} }
async getLastInitError(): Promise<string | null> {
return this.callWorker('getLastInitError')
}
/** /**
* 关闭数据库连接 * 关闭数据库连接
*/ */

View File

@@ -37,6 +37,9 @@ if (parentPort) {
case 'open': case 'open':
result = await core.open(payload.dbPath, payload.hexKey, payload.wxid) result = await core.open(payload.dbPath, payload.hexKey, payload.wxid)
break break
case 'getLastInitError':
result = core.getLastInitError()
break
case 'close': case 'close':
core.close() core.close()
result = { success: true } result = { success: true }