diff --git a/electron/main.ts b/electron/main.ts index 5f54f94..040899c 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -3957,8 +3957,7 @@ function registerIpcHandlers() { // 自动下载原图 ipcMain.handle('image:startAutoDownload', async () => { - await imageDownloadService.startAutoDownload() - return { success: true } + return await imageDownloadService.startAutoDownload() }) ipcMain.handle('image:stopAutoDownload', async () => { diff --git a/electron/services/imageDownloadService.ts b/electron/services/imageDownloadService.ts index 3d978f6..199cb98 100644 --- a/electron/services/imageDownloadService.ts +++ b/electron/services/imageDownloadService.ts @@ -96,14 +96,16 @@ export class ImageDownloadService { } } - async startAutoDownload() { - if (!await this.ensureInitialized()) return - - if (this.pollTimer) return - + async startAutoDownload(): Promise<{ success: boolean; error?: string }> { + if (!await this.ensureInitialized()) { + return { success: false, error: '核心组件初始化失败,请检查环境' } + } + + if (this.pollTimer) return { success: true } + this.pollTimer = setInterval(() => this.checkAndHook(), 30000) - // Initial check - await this.checkAndHook() + // 首次尝试 Hook,并返回结果 + return await this.checkAndHook(true) } async stopAutoDownload() { @@ -114,19 +116,20 @@ export class ImageDownloadService { await this.unhook() } - private async checkAndHook() { + private async checkAndHook(isManualStart = false): Promise<{ success: boolean; error?: string }> { const pid = await this.findMainWeChatPid() - + if (!pid) { if (this.isHooked) { console.log('[ImageDownloadService] WeChat exited, unhooking') await this.unhook() } - return + // 如果是手动开启时没找到进程,不认为是严重错误,只是挂起等待 + return { success: true, error: '等待微信启动' } } if (this.isHooked && this.currentPid === pid) { - return + return { success: true } } if (this.isHooked && this.currentPid !== pid) { @@ -141,12 +144,24 @@ export class ImageDownloadService { this.isHooked = true this.currentPid = pid console.log('[ImageDownloadService] hook successful') + return { success: true } } else { const err = this.getImgHelperError() console.error(`[ImageDownloadService] hook failed: ${err}`) + // 如果是手动点击开启时失败,停止轮询并向上报错 + if (isManualStart && this.pollTimer) { + clearInterval(this.pollTimer) + this.pollTimer = null + } + return { success: false, error: err || 'Hook 失败' } } - } catch (e) { + } catch (e: any) { console.error('[ImageDownloadService] InitImgHelper call crashed:', e) + if (isManualStart && this.pollTimer) { + clearInterval(this.pollTimer) + this.pollTimer = null + } + return { success: false, error: `调用异常: ${e.message || String(e)}` } } } diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index a5bf75b..3521f77 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -327,6 +327,7 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) { const [aiFootprintEnabled, setAiFootprintEnabled] = useState(false) const [aiFootprintSystemPrompt, setAiFootprintSystemPrompt] = useState('') const [aiInsightDebugLogEnabled, setAiInsightDebugLogEnabled] = useState(false) + const [autoDownloadStatus, setAutoDownloadStatus] = useState<{ isHooked: boolean; pid: number | null; supported: boolean } | null>(null) // 检查 Hello 可用性 useEffect(() => { @@ -706,6 +707,21 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) { void refreshWhisperStatus(whisperModelDir) }, [whisperModelDir]) + useEffect(() => { + if (activeTab === 'autoDownload') { + fetchAutoDownloadStatus() + + let interval: ReturnType | undefined + if (autoDownloadHighRes) { + interval = setInterval(fetchAutoDownloadStatus, 2000) + } + + return () => { + if (interval) clearInterval(interval) + } + } + }, [activeTab, autoDownloadHighRes]) + const getErrorMessage = (error: any): string => { const raw = typeof error?.message === 'string' ? error.message : String(error ?? '') const normalized = raw.replace(/^Error:\s*/i, '').trim() @@ -1600,6 +1616,15 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) { } } + const fetchAutoDownloadStatus = async () => { + try { + const status = await (window as any).electronAPI.image.getAutoDownloadStatus() + setAutoDownloadStatus(status) + } catch (error) { + console.error('获取自动下载状态失败:', error) + } + } + const renderAppearanceTab = () => (
@@ -4671,41 +4696,112 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) { ) const renderAutoDownloadTab = () => ( -
-
- - - 开启后,WeFlow 会通过远程 Hook 技术强制微信在接收图片时下载高清原图(而非默认的缩略图)。 -
- 风险提示:Hook 涉及修改微信进程内存,虽不注入 DLL 但仍有被检测风险,请谨慎开启。 -
-
- {autoDownloadHighRes ? '已开启' : '已关闭'} - +
+
+
+ 实验性功能 +

自动下载原图

+

强制微信在接收图片时下载高清原图,而非默认的模糊缩略图。

+
+
+ +
+
+
+
+ 启用自动下载 +
+ 开启后,WeFlow 将通过远程 Hook 技术干预微信进程。 +
+
+ +
+
+
+ +
+
+ +

运行状态

+
+
+ {!autoDownloadHighRes ? ( +
服务未启动
+ ) : !autoDownloadStatus ? ( +
正在检测状态...
+ ) : !autoDownloadStatus.supported ? ( +
⚠️ 当前系统架构不支持此功能(仅支持 Win32 x64)
+ ) : autoDownloadStatus.isHooked ? ( +
+ ✓ 运行中 + 已成功挂载到微信进程 (PID: {autoDownloadStatus.pid}) +
+ ) : ( +
+ ⏳ 等待中 + 未检测到微信主进程 (Weixin.exe) 运行,请启动微信 +
+ )} +
+
+ +
+
+ +

风险提示

+
+
+
+
+ + 此功能涉及hook修改微信进程内存 +
+
+ + 虽然当前方案不直接注入 DLL,但仍存在被微信安全机制检测的风险 +
+
+ + 建议先少量测试使用,确认有无被检测的风险 +
+
+
-
) - const handleToggleAutoDownload = async () => { const newVal = !autoDownloadHighRes setAutoDownloadHighRes(newVal) - await configService.setAutoDownloadHighRes(newVal) - if (newVal) { - await (window as any).electronAPI.image.startAutoDownload() - } else { - await (window as any).electronAPI.image.stopAutoDownload() + + try { + if (newVal) { + const result = await (window as any).electronAPI.image.startAutoDownload() + if (result && !result.success) { + // 如果底层明确返回了失败 + throw new Error(result.error || '启动自动下载服务失败') + } + showMessage('自动下载已开启,正在尝试连接微信', true) + await fetchAutoDownloadStatus() + } else { + await (window as any).electronAPI.image.stopAutoDownload() + showMessage('自动下载已关闭', true) + setAutoDownloadStatus(null) + } + await configService.setAutoDownloadHighRes(newVal) + } catch (e: any) { + // 发生错误时,将开关拨回去 + setAutoDownloadHighRes(!newVal) + showMessage(`操作失败: ${e.message || String(e)}`, false) } - showMessage(newVal ? '自动下载已开启' : '自动下载已关闭', true) } const renderUpdatesTab = () => {