Files
CipherTalk/electron/services/nativeDecryptService.ts
T

216 lines
6.6 KiB
TypeScript

/**
* 原生 DLL 解密服务 (Worker 多线程版)
*
* 使用独立的 Worker 线程加载 DLL 并执行解密,
* 彻底避免主线程阻塞,并支持实时进度回报。
*/
import * as path from 'path'
import * as fs from 'fs'
import { app } from 'electron'
import { Worker } from 'worker_threads'
// 简单的 ID 生成器
const generateId = () => Math.random().toString(36).substring(2, 15)
interface DecryptTask {
resolve: (value: { success: boolean; error?: string }) => void
onProgress?: (current: number, total: number) => void
}
/**
* 原生解密服务
*/
export class NativeDecryptService {
private worker: Worker | null = null
private dllPath: string | null = null
private initialized: boolean = false
private initError: string | null = null
private tasks: Map<string, DecryptTask> = new Map()
constructor() {
this.init()
}
/**
* 初始化服务和 Worker
*/
private init(): void {
if (this.initialized) return
try {
// 1. 查找 DLL 路径
this.dllPath = this.findDllPath()
if (!this.dllPath) {
this.initError = '未找到 wcdb_decrypt.dll'
console.warn('[NativeDecrypt] ' + this.initError)
return
}
// 2. 查找 Worker 脚本路径
const workerScript = this.findWorkerPath()
if (!workerScript) {
this.initError = '未找到 decryptWorker.js'
console.warn('[NativeDecrypt] ' + this.initError)
return
}
console.log('[NativeDecrypt] 启动 Worker:', workerScript)
console.log('[NativeDecrypt] DLL 路径:', this.dllPath)
// 3. 启动 Worker 线程
this.worker = new Worker(workerScript, {
workerData: { dllPath: this.dllPath }
})
// 4. 监听 Worker 消息
this.worker.on('message', (msg) => this.handleWorkerMessage(msg))
this.worker.on('error', (err: Error) => {
console.error('[NativeDecrypt] Worker 错误:', err)
this.initError = `Worker error: ${err.message}`
})
this.worker.on('exit', (code) => {
if (code !== 0) {
console.error(`[NativeDecrypt] Worker 异常退出,代码: ${code}`)
this.worker = null
this.initialized = false
}
})
this.initialized = true
} catch (e) {
this.initError = `初始化失败: ${e}`
console.error('[NativeDecrypt]', this.initError)
}
}
/**
* 处理 Worker 发来的消息
*/
private handleWorkerMessage(msg: any): void {
if (msg.type === 'ready') {
console.log('[NativeDecrypt] Worker 已就绪')
return
}
const task = this.tasks.get(msg.id)
if (!task) return
switch (msg.type) {
case 'success':
task.resolve({ success: true })
this.tasks.delete(msg.id)
break
case 'error':
task.resolve({ success: false, error: msg.error })
this.tasks.delete(msg.id)
break
case 'progress':
if (task.onProgress) {
task.onProgress(msg.current, msg.total)
}
break
}
}
/**
* 查找 DLL 路径
*/
private findDllPath(): string | null {
const candidates: string[] = []
if (app.isPackaged) {
candidates.push(
path.join(process.resourcesPath, 'wcdb_decrypt.dll'),
path.join(process.resourcesPath, 'resources', 'wcdb_decrypt.dll'),
path.join(process.resourcesPath, 'app.asar.unpacked', 'resources', 'wcdb_decrypt.dll')
)
} else {
candidates.push(
path.join(app.getAppPath(), 'resources', 'wcdb_decrypt.dll'),
path.join(app.getAppPath(), 'native-dlls', 'wcdb_decrypt', 'build', 'bin', 'Release', 'wcdb_decrypt.dll')
)
}
return candidates.find(p => fs.existsSync(p)) || null
}
/**
* 查找 Worker 脚本路径
*/
private findWorkerPath(): string | null {
const candidates: string[] = []
if (app.isPackaged) {
// 生产模式:Worker 被编译到 dist-electron/workers 目录
candidates.push(
path.join(process.resourcesPath, 'app.asar.unpacked', 'dist-electron', 'workers', 'decryptWorker.js'),
path.join(process.resourcesPath, 'dist-electron', 'workers', 'decryptWorker.js'),
path.join(__dirname, 'workers', 'decryptWorker.js'),
path.join(__dirname, '..', 'workers', 'decryptWorker.js')
)
} else {
// 开发模式:Worker 在源码目录
candidates.push(
path.join(app.getAppPath(), 'electron', 'workers', 'decryptWorker.js'),
path.join(__dirname, '..', 'workers', 'decryptWorker.js')
)
}
const found = candidates.find(p => fs.existsSync(p))
if (found) {
console.log('[NativeDecrypt] 找到 Worker:', found)
} else {
console.error('[NativeDecrypt] 未找到 Worker,尝试的路径:', candidates)
}
return found || null
}
/**
* 检查服务是否可用
*/
isAvailable(): boolean {
return this.initialized && this.worker !== null
}
/**
* 异步解密数据库(通过 Worker)
*/
async decryptDatabaseAsync(
inputPath: string,
outputPath: string,
hexKey: string,
onProgress?: (current: number, total: number) => void
): Promise<{ success: boolean; error?: string }> {
if (!this.worker) {
// 如果 Worker 挂了,尝试重启
if (!this.initialized && !this.initError) {
this.init()
}
if (!this.worker) {
return { success: false, error: this.initError || 'Worker 未启动' }
}
}
return new Promise((resolve) => {
const id = generateId()
// 注册任务
this.tasks.set(id, { resolve, onProgress })
// 发送消息
this.worker!.postMessage({
type: 'decrypt',
id,
inputPath,
outputPath,
hexKey
})
})
}
}
// 导出单例
export const nativeDecryptService = new NativeDecryptService()