.lastevent、!analyze(dump分析、异常错误码查询)

本文介绍Windbg中的.lastevent命令及!analyze扩展命令的使用方法,帮助理解程序异常情况,包括异常错误码解析、异常记录查看及堆栈信息分析。

.lastevent

.lastevent 命令显示最近一次发生的异常或事件。

[cpp]  view plain  copy
  1. 0:000> .lastevent  
  2. Last event: 1534.f4c: Break instruction exception - code 80000003 (first chance)  
  3.   debugger time: Tue May 22 10:47:26.962 2012 (GMT+8)  
  4. 0:000> ~  
  5. .  0  Id: 1534.e8c Suspend: 1 Teb: 7ffdf000 Unfrozen  
  6.    1  Id: 1534.1338 Suspend: 1 Teb: 7ffde000 Unfrozen  
  7. #  2  Id: 1534.f4c Suspend: 1 Teb: 7ffdd000 Unfrozen  

我们可以看出,当前为2号线程发生异常,线程0前面的点号(.)表示它是当前线程。线程2前面的数字号(#)表示它是产生异常或调试器附加到进程时活动的线程。如果使用CTRL+C、 CTRL+BREAK或Debug | Break中断到调试器,总是会产生一个 0x80000003异常代码。

[cpp]  view plain  copy
  1. 0:000> .lastevent  
  2. Last event: 1664.4184: Access violation - code c0000005 (first/second chance not available)  
  3.   debugger time: Thu Aug 13 11:20:44.037 2015 (GMT+8)  

异常错误码查询

异常
描述
EXCEPTION_ACCESS_VIOLATION
0xC0000005
程序企图读写一个不可访问的地址时引发的异常。例如企图读取0地址处的内存。
EXCEPTION_ARRAY_BOUNDS_EXCEEDED
0xC000008C
数组访问越界时引发的异常。
EXCEPTION_BREAKPOINT
0x80000003
触发断点时引发的异常。
EXCEPTION_DATATYPE_MISALIGNMENT
0x80000002
程序读取一个未经对齐的数据时引发的异常。
EXCEPTION_FLT_DENORMAL_OPERAND
0xC000008D
如果浮点数操作的操作数是非正常的,则引发该异常。所谓非正常,即它的值太小以至于不能用标准格式表示出来。
EXCEPTION_FLT_DIVIDE_BY_ZERO
0xC000008E
浮点数除法的除数是0时引发该异常。
EXCEPTION_FLT_INEXACT_RESULT
0xC000008F
浮点数操作的结果不能精确表示成小数时引发该异常。
EXCEPTION_FLT_INVALID_OPERATION
0xC0000090
该异常表示不包括在这个表内的其它浮点数异常。
EXCEPTION_FLT_OVERFLOW
0xC0000091
浮点数的指数超过所能表示的最大值时引发该异常。
EXCEPTION_FLT_STACK_CHECK
0xC0000092
进行浮点数运算时栈发生溢出或下溢时引发该异常。
EXCEPTION_FLT_UNDERFLOW
0xC0000093
浮点数的指数小于所能表示的最小值时引发该异常。
EXCEPTION_ILLEGAL_INSTRUCTION
0xC000001D
程序企图执行一个无效的指令时引发该异常。
EXCEPTION_IN_PAGE_ERROR
0xC0000006
程序要访问的内存页不在物理内存中时引发的异常。
EXCEPTION_INT_DIVIDE_BY_ZERO
0xC0000094
整数除法的除数是0时引发该异常。
EXCEPTION_INT_OVERFLOW
0xC0000095
整数操作的结果溢出时引发该异常。
EXCEPTION_INVALID_DISPOSITION
0xC0000026
异常处理器返回一个无效的处理的时引发该异常。
EXCEPTION_NONCONTINUABLE_EXCEPTION
0xC0000025
发生一个不可继续执行的异常时,如果程序继续执行,则会引发该异常。
EXCEPTION_PRIV_INSTRUCTION
0xC0000096
程序企图执行一条当前CPU模式不允许的指令时引发该异常。
EXCEPTION_SINGLE_STEP
0x80000004
标志寄存器的TF位为1时,每执行一条指令就会引发该异常。主要用于单步调试。
EXCEPTION_STACK_OVERFLOW
0xC00000FD
栈溢出时引发该异常。

!analyze

!analyze扩展显示当前异常或bug check的信息。一般使用!analyze -v

分析参考:https://msdn.microsoft.com/en-us/library/windows/hardware/ff560201(v=vs.85).aspx

如下述为一个对NULL指针赋值生成的dump,自动生成dump可以参考16.windbg-.dump(转储文件)

[cpp]  view plain  copy
  1. 0:000> !analyze -v  
  2. *******************************************************************************  
  3. *                                                                             *  
  4. *                        Exception Analysis                                   *  
  5. *                                                                             *  
  6. *******************************************************************************  
  7.   
  8. *************************************************************************  
  9. ***                                                                   ***  
  10. ***                                                                   ***  
  11. ***    Your debugger is not using the correct symbols                 ***  
  12. ***                                                                   ***  
  13. ***    In order for this command to work properly, your symbol path   ***  
  14. ***    must point to .pdb files that have full type information.      ***  
  15. ***                                                                   ***  
  16. ***    Certain .pdb files (such as the public OS symbols) do not      ***  
  17. ***    contain the required information.  Contact the group that      ***  
  18. ***    provided you with these symbols if you need this command to    ***  
  19. ***    work.                                                          ***  
  20. ***                                                                   ***  
  21. ***    Type referenced: kernel32!pNlsUserInfo                         ***  
  22. ***                                                                   ***  
  23. *************************************************************************  
  24. *************************************************************************  
  25. ***                                                                   ***  
  26. ***                                                                   ***  
  27. ***    Your debugger is not using the correct symbols                 ***  
  28. ***                                                                   ***  
  29. ***    In order for this command to work properly, your symbol path   ***  
  30. ***    must point to .pdb files that have full type information.      ***  
  31. ***                                                                   ***  
  32. ***    Certain .pdb files (such as the public OS symbols) do not      ***  
  33. ***    contain the required information.  Contact the group that      ***  
  34. ***    provided you with these symbols if you need this command to    ***  
  35. ***    work.                                                          ***  
  36. ***                                                                   ***  
  37. ***    Type referenced: kernel32!pNlsUserInfo                         ***  
  38. ***                                                                   ***  
  39. *************************************************************************  
首先被提示,这是没有pdb文件 的

[cpp]  view plain  copy
  1. FAULTING_IP:   
  2. test2+1002  
  3. 01211002 8900            mov     dword ptr [eax],eax  
FAULTING_IP:出现错误时的指令,这句明显就是把eax赋到[eax]中

[cpp]  view plain  copy
  1. EXCEPTION_RECORD:  ffffffff -- (.exr 0xffffffffffffffff)  
EXCEPTION_RECORD:崩溃时的异常记录,可以使用.exr查看
[cpp]  view plain  copy
  1. ExceptionAddress: 01211002 (test2+0x00001002)  
  2.    ExceptionCode: c0000005 (Access violation)  
  3.   ExceptionFlags: 00000000  
  4. NumberParameters: 2  
  5.    Parameter[0]: 00000001  
  6.    Parameter[1]: 00000000  
  7. Attempt to write to address 00000000  
这个就很详细了,尝试向0地址写入,它其实就是调试中用到的结构体:可以直接通过MSDN查到它的定义

[cpp]  view plain  copy
  1. typedef struct _EXCEPTION_RECORD {  
  2.     DWORD    ExceptionCode;  
  3.     DWORD ExceptionFlags;  
  4.     struct _EXCEPTION_RECORD *ExceptionRecord;  
  5.     PVOID ExceptionAddress;  
  6.     DWORD NumberParameters;  
  7.     ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];  
  8.     } EXCEPTION_RECORD;  
注意的是:上面的NumberParameter:2表示ExceptionInformation有两个附加异常码,1和0,对于多数异常来说,这些附加异常码是没有什么用的,MSDN: For most exception codes, the array elements are undefined.只有以下两个被定义了

当ExceptionCode为EXCEPTION_ACCESS_VIOLATION时,ExceptionInformation[0]=0表示线程试图读取不可访问的数据ExceptionInformation[0]=1

表示线程试图写入不可访问的地址,很明显,这里表示第二种,具体参考MSDN

[cpp]  view plain  copy
  1. DEFAULT_BUCKET_ID:  NULL_POINTER_READ  
DEFAULT_BUCKET_ID:表示了本次错误属于哪种通用失败

[cpp]  view plain  copy
  1. BUGCHECK_STR:  APPLICATION_FAULT_NULL_POINTER_READ_NULL_POINTER_WRITE  
The BUGCHECK_STR field shows the exception code. The name is a misnomer—the term  bug check  actually signifies a kernel-mode crash. In user-mode debugging, the exception code will be displayed—in this case, 0x80000003.

[cpp]  view plain  copy
  1. LAST_CONTROL_TRANSFER:  from 76b6337a to 01211002  
堆栈的最后调用:

The LAST_CONTROL_TRANSFER field shows the last call on the stack. In this case, the code at address0x76b6337acalled a function at 0x1211002. You can use these addresses with the ln (List Nearest Symbols) command to determine what modules and functions these addresses reside in.

[cpp]  view plain  copy
  1. STACK_TEXT:    
  2. WARNING: Stack unwind information not available. Following frames may be wrong.  
  3. 0020fbb8 76b6337a 7efde000 0020fc04 77e092b2 test2+0x1002  
  4. 0020fbc4 77e092b2 7efde000 596d1564 00000000 kernel32!BaseThreadInitThunk+0xe  
  5. 0020fc04 77e09285 012112b6 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70  
  6. 0020fc1c 00000000 012112b6 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b  
堆信息

[cpp]  view plain  copy
  1. STACK_COMMAND:  ~0s; .ecxr ; kb  
打印堆栈的命令

[cpp]  view plain  copy
  1. SYMBOL_NAME:  test2+1002  
对应的符号名称

[cpp]  view plain  copy
  1. IMAGE_NAME:  test2.exe  
对应的模块名称




3.符号文件简介:

符号文件对于调试程序是相当重要的,通常符号文件中包含以下内容

全局变量的名字和地址

函数名,地址及其原型

帧指针优化数据

局部变量的名字和地址

源文件路径以及每个符号的行号

变量,结构等的类型信息

整理模块1界面代码,生成完整代码#!/usr/bin/env python3 # -*- coding: utf-8 -*- # 标准库导入 import json import os import sys import tkinter as tk import time import uuid from pathlib import Path from tkinter import ttk, messagebox, scrolledtext import logging from collections import defaultdict, Counter import queue import threading # 项目路径设置 def add_project_to_path(): """确保项目根目录在Python路径中""" project_root = Path(__file__).parent.parent.absolute() if str(project_root) not in sys.path: sys.path.insert(0, str(project_root)) add_project_to_path() # 核心模块导入 try: from core.global_config import GlobalConfig from core.event_system import Event, EventType from core.event_center import event_center print("成功导入核心模块") except ImportError as import_err: print(f"核心导入失败: {import_err}") sys.exit(1) # 本地模块导入 try: from input_analysis import InputAnalysisModule except ImportError as local_err: print(f"输入分析模块导入失败: {local_err}") sys.exit(1) class InputAnalysisUI: def __init__(self, parent_frame): self.logger = logging.getLogger(f"{__name__}.InputAnalysisUI") self.parent = parent_frame self.token = str(uuid.uuid4()) self._is_analyzing = False self._last_analysis_time = None self._setup_dynamic_area() self._register_dynamic_handlers() try: # 修复:正确初始化模块 self.module = InputAnalysisModule(event_center, EventType) # 修复:正确注册回调函数 self.module.register_callback("analysis_result", self._handle_analysis_result) except Exception as init_err: messagebox.showerror("初始化错误", f"模块初始化失败: {init_err}") raise self.labels = ["排除号码", "前区", "后区", "推荐号码", "前区", "后区"] self.dynamic_data = { "排除号码": {"前区": [], "后区": []}, "推荐号码": {"前区": [], "后区": []} } self._setup_ui() self._register_event_handlers() self._load_saved_data() def _setup_dynamic_area(self): """设置动态区UI""" dynamic_frame = ttk.LabelFrame(self.frame, text=" 动态区 ") dynamic_frame.pack(fill=tk.X, padx=20, pady=10) # 推荐号码标签 ttk.Label(dynamic_frame, text="推荐号码").pack(anchor=tk.W, padx=10, pady=5) # 前区推荐 front_frame = ttk.Frame(dynamic_frame) front_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(front_frame, text="前区:").pack(side=tk.LEFT) self.dynamic_front_var = tk.StringVar() ttk.Label(front_frame, textvariable=self.dynamic_front_var).pack(side=tk.LEFT, padx=5) # 后区推荐 back_frame = ttk.Frame(dynamic_frame) back_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(back_frame, text="后区:").pack(side=tk.LEFT) self.dynamic_back_var = tk.StringVar() ttk.Label(back_frame, textvariable=self.dynamic_back_var).pack(side=tk.LEFT, padx=5) # 动态区按钮 btn_frame = ttk.Frame(dynamic_frame) btn_frame.pack(fill=tk.X, pady=5) buttons = [ ("运行", self.run_analysis), ("清除", self.clear_dynamic_data), ("保存", self.save_dynamic_data), ("刷新", self.refresh_to_pool) ] for text, cmd in buttons: ttk.Button(btn_frame, text=text, command=cmd, width=8).pack( side=tk.LEFT, padx=5 ) def _register_dynamic_handlers(self): """注册动态区事件处理器""" event_center.subscribe( event_type=EventType.DYNAMIC_UPDATE, token=self.token, callback=self._handle_dynamic_update ) def _handle_dynamic_update(self, event: Event): """处理动态区更新事件""" if event.source != GlobalConfig.MODULE1_ID: return data = event.data.get("推荐号码", {}) self.dynamic_labels["推荐号码"] = data # 更新UI self.dynamic_front_var.set(", ".join(map(str, data.get("前区", [])))) self.dynamic_back_var.set(", ".join(map(str, data.get("后区", [])))) def run_analysis(self): """运行动态区分析""" self._on_analyze_clicked() def clear_dynamic_data(self): """清除动态区数据""" self.dynamic_labels["推荐号码"] = {"前区": [], "后区": []} self.dynamic_front_var.set("") self.dynamic_back_var.set("") logger.info("动态区数据已清除") def save_dynamic_data(self): """保存动态区数据""" try: save_path = os.path.join(GlobalConfig.DESKTOP_PATH, "动态区数据.json") with open(save_path, 'w', encoding='utf-8') as f: json.dump(self.dynamic_labels, f, ensure_ascii=False, indent=2) logger.info(f"动态区数据已保存至: {save_path}") messagebox.showinfo("保存成功", f"数据已保存至:\n{save_path}") except Exception as e: logger.error(f"保存失败: {e}") messagebox.showerror("保存失败", str(e)) def refresh_to_pool(self): """刷新数据到号码池""" # 创建刷新事件 refresh_event = Event( event_id=str(uuid.uuid4()), type=EventType.POOL_UPDATE, source=self.token, target='number_pool', data={ "前区:": self.dynamic_labels["推荐号码"].get("前区", []), "后区:": self.dynamic_labels["推荐号码"].get("后区", []) } ) event_center.publish(refresh_event) logger.info("动态区数据已刷新到号码池") messagebox.showinfo("刷新成功", "数据已刷新到号码池") @staticmethod def _validate_number(text: str, min_val: int, max_val: int) -> bool: """验证输入的数字是否在指定范围内""" if not text: return True return text.isdigit() and min_val <= int(text) <= max_val def _validate_inputs(self): """验证所有输入有效性""" valid = True for entry in self.exclude_front_entries: if entry.get() and not self._validate_number(entry.get(), 1, 35): entry.config(foreground='red') valid = False else: entry.config(foreground='black') for entry in self.exclude_back_entries: if entry.get() and not self._validate_number(entry.get(), 1, 12): entry.config(foreground='red') valid = False else: entry.config(foreground='black') return valid @staticmethod def _format_number(num_str: str) -> str: """格式化数字为两位数""" if not num_str.isdigit(): return num_str num = int(num_str) return f"{num:02d}" if 1 <= num <= 9 else str(num) def _setup_ui(self): """初始化UI组件 - 按照模块3的成功设计""" self.frame = ttk.LabelFrame( self.parent, text="输入分析模块", font=('微软雅黑', 12, 'bold') ) self.frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 模块ID标识 module_id_label = ttk.Label( self.frame, text=f"模块ID: {GlobalConfig.MODULE_IDS['MODULE1_ID']}", font=('微软雅黑', 8), foreground='gray' ) module_id_label.pack(anchor=tk.NE, padx=10, pady=5) # === 排除号码区 === exclude_frame = ttk.LabelFrame(self.frame, text=" 排除号码 ", font=('微软雅黑', 10, 'bold')) exclude_frame.pack(fill=tk.X, padx=20, pady=10, ipady=5) # 排除号码标题 ttk.Label(exclude_frame, text=self.labels[0], font=('微软雅黑', 10, 'bold')).pack( anchor=tk.W, padx=10, pady=(5, 0)) # 前区排除 ttk.Label(exclude_frame, text=self.labels[1], font=('微软雅黑', 10)).pack( anchor=tk.W, padx=10, pady=5) front_entries_frame = ttk.Frame(exclude_frame) front_entries_frame.pack(fill=tk.X, padx=10, pady=5) self.exclude_front_entries = [] for i in range(10): # 10个前区输入框 entry_frame = ttk.Frame(front_entries_frame) entry_frame.pack(side=tk.LEFT, padx=2) ttk.Label(entry_frame, text=f"{i + 1}:").pack(side=tk.LEFT) entry = ttk.Entry(entry_frame, width=3, font=('微软雅黑', 10)) entry.pack(side=tk.LEFT) # 添加验证函数 - 前区限制1-35 entry.config(validate="key", validatecommand=( entry.register(lambda text: self._validate_number(text, 1, 35)), '%P')) entry.bind("<KeyRelease>", self._auto_format_entry) entry.bind("<Left>", lambda e, d=-1: self._navigate_entry(e, d)) entry.bind("<Right>", lambda e, d=1: self._navigate_entry(e, d)) self.exclude_front_entries.append(entry) # 后区排除 ttk.Label(exclude_frame, text=self.labels[2], font=('微软雅黑', 10)).pack( anchor=tk.W, padx=10, pady=(10, 5)) back_entries_frame = ttk.Frame(exclude_frame) back_entries_frame.pack(fill=tk.X, padx=10, pady=5) self.exclude_back_entries = [] for i in range(10): # 10个后区输入框 entry_frame = ttk.Frame(back_entries_frame) entry_frame.pack(side=tk.LEFT, padx=2) ttk.Label(entry_frame, text=f"{i + 1}:").pack(side=tk.LEFT) entry = ttk.Entry(entry_frame, width=3, font=('微软雅黑', 10)) entry.pack(side=tk.LEFT) # 添加验证函数 - 后区限制1-12 entry.config(validate="key", validatecommand=( entry.register(lambda text: self._validate_number(text, 1, 12)), '%P')) entry.bind("<KeyRelease>", self._auto_format_entry) entry.bind("<Left>", lambda e, d=-1: self._navigate_entry(e, d)) entry.bind("<Right>", lambda e, d=1: self._navigate_entry(e, d)) self.exclude_back_entries.append(entry) # === 推荐号码区 === recommend_frame = ttk.LabelFrame(self.frame, text=" 推荐号码 ", font=('微软雅黑', 10, 'bold')) recommend_frame.pack(fill=tk.X, padx=20, pady=10, ipady=5) # 推荐号码标题 ttk.Label(recommend_frame, text=self.labels[3], font=('微软雅黑', 10, 'bold')).pack( anchor=tk.W, padx=10, pady=(5, 0)) # 前区推荐 front_rec_frame = ttk.Frame(recommend_frame) front_rec_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(front_rec_frame, text=self.labels[4], font=('微软雅黑', 10)).pack(side=tk.LEFT) self.recommend_front_var = tk.StringVar() ttk.Entry( front_rec_frame, textvariable=self.recommend_front_var, state='readonly', font=('微软雅黑', 10), readonlybackground='#f0f5f0' ).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) # 后区推荐 back_rec_frame = ttk.Frame(recommend_frame) back_rec_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(back_rec_frame, text=self.labels[5], font=('微软雅黑', 10)).pack(side=tk.LEFT) self.recommend_back_var = tk.StringVar() ttk.Entry( back_rec_frame, textvariable=self.recommend_back_var, state='readonly', font=('微软雅黑', 10), readonlybackground='#f0f5f0' ).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) # === 结果区 === result_frame = ttk.LabelFrame(self.frame, text=" 分析结果 ", font=('微软雅黑', 10, 'bold')) result_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10, ipady=5) # 结果文本框 self.result_text = scrolledtext.ScrolledText( result_frame, wrap=tk.WORD, font=('微软雅黑', 10), height=6 ) self.result_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) self.result_text.insert(tk.END, "请设置排除号码后点击'运行分析'按钮开始分析\n") self.result_text.config(state=tk.DISABLED) # === 控制按钮区域(右下角对齐)=== btn_frame = ttk.Frame(self.frame) btn_frame.pack(fill=tk.X, pady=10, anchor=tk.SE) # 按钮容器(右对齐) btn_container = ttk.Frame(btn_frame) btn_container.pack(side=tk.RIGHT, padx=5) buttons = [ ("运行", self._on_analyze_clicked), ("保存", self._save_config), ("清除", self._clear_entries), ("刷新", self._refresh_data) ] for text, command in buttons: btn = ttk.Button(btn_container, text=text, width=10, command=command) btn.pack(side=tk.LEFT, padx=2) self.analyze_btn = btn_container.winfo_children()[0] # 获取运行分析按钮引用 def _safe_update_ui(self, callback, *args): """线程安全的UI更新""" if not self.frame.winfo_exists(): return self.frame.after(0, lambda: callback(*args)) def _register_event_handlers(self): """注册事件处理器""" if hasattr(EventType, 'ANALYSIS_RESULT'): event_center.subscribe( event_type=EventType.ANALYSIS_RESULT, token=self.token, callback=self._handle_analysis_result ) else: self.logger.warning("EventType缺少ANALYSIS_RESULT定义") def _handle_module_run(self, event): """处理模块运行事件""" if not hasattr(event, 'data'): self.logger.error("收到无效的运行事件: 缺少data属性") return if event.target != getattr(self.module, 'MODULE_ID', 'input_analysis'): return self._on_analyze_clicked() def _load_saved_data(self): """加载保存的数据""" save_path = GlobalConfig.MODULE1_SAVE_PATH if not os.path.exists(save_path): return try: with open(save_path, 'r', encoding='utf-8') as f: saved_data = json.load(f) # 恢复前区排除号码 if 'exclude_front' in saved_data: for i, num in enumerate(saved_data['exclude_front']): if i < len(self.exclude_front_entries) and num: try: formatted_num = f"{int(num):02d}" self.exclude_front_entries[i].delete(0, tk.END) self.exclude_front_entries[i].insert(0, formatted_num) except ValueError: continue # 恢复后区排除号码 if 'exclude_back' in saved_data: for i, num in enumerate(saved_data['exclude_back']): if i < len(self.exclude_back_entries) and num: try: formatted_num = f"{int(num):02d}" self.exclude_back_entries[i].delete(0, tk.END) self.exclude_back_entries[i].insert(0, formatted_num) except ValueError: continue except Exception as load_err: self.logger.error(f"加载保存数据失败: {load_err}") self._update_result_text(f"加载保存数据失败: {load_err}") def _save_config(self): """保存当前配置""" save_data = { "exclude_front": [], "exclude_back": [] } # 收集前区排除号码 for entry in self.exclude_front_entries: if entry.get().strip(): try: save_data["exclude_front"].append(int(entry.get())) except ValueError: pass # 收集后区排除号码 for entry in self.exclude_back_entries: if entry.get().strip(): try: save_data["exclude_back"].append(int(entry.get())) except ValueError: pass # 保存到文件 try: with open(GlobalConfig.MODULE1_SAVE_PATH, 'w', encoding='utf-8') as f: json.dump(save_data, f, ensure_ascii=False, indent=2) self._update_result_text("配置保存成功") except Exception as save_err: self.logger.error(f"保存配置失败: {save_err}") self._update_result_text(f"保存配置失败: {save_err}") def _clear_entries(self): """清除所有输入框""" for entry in self.exclude_front_entries + self.exclude_back_entries: entry.delete(0, tk.END) self._update_result_text("输入已清除", clear=True) def _refresh_data(self): """刷新数据""" self._load_saved_data() self._update_result_text("数据已刷新") def _on_analyze_clicked(self): """处理分析按钮点击事件""" if not self._validate_inputs(): messagebox.showwarning("输入错误", "请检查输入的数字是否在有效范围内") return exclude_front = [] exclude_back = [] try: # 收集前区排除号码 for entry in self.exclude_front_entries: if entry.get().strip(): try: exclude_front.append(int(entry.get())) except ValueError: pass # 收集后区排除号码 for entry in self.exclude_back_entries: if entry.get().strip(): try: exclude_back.append(int(entry.get())) except ValueError: pass # 准备分析数据 analysis_data = { 'exclude_front': exclude_front, 'exclude_back': exclude_back } # 确定事件类型 run_event_type = getattr(EventType, 'MODULE_RUN', getattr(EventType, 'MODULE_READY', 'module_run')) # 修复:正确创建事件对象 command_event = Event( event_id=str(uuid.uuid4()), type=run_event_type, source=self.token, target=getattr(self.module, 'MODULE_ID', 'input_analysis'), data=analysis_data # 确保提供data参数 ) event_center.publish(command_event) self.analyze_btn.config(state='disabled') self._update_result_text("分析...", clear=True) except Exception as analyze_error: messagebox.showerror("输入错误", f"无效的输入格式: {analyze_error}") self.analyze_btn.config(state='normal') def run_analysis(self): """运行分析""" if self.module: result = self.module.analyze() print(result) else: print("分析模块未初始化") def _handle_analysis_result(self, event: Event): """处理分析结果事件""" if not hasattr(event, 'data'): self.logger.error("收到无效的分析结果事件: 缺少data属性") return self.frame.after(0, lambda: self._display_results(event.data)) def _handle_error(self, event: Event): """处理错误事件""" error_msg = event.data.get('error', '未知错误') if hasattr(event, 'data') else '未知错误' self.frame.after(0, lambda: self._show_error(error_msg)) self.frame.after(0, lambda: self.analyze_btn.config(state='normal')) def _display_results(self, data): """显示分析结果""" if not isinstance(data, dict): self.logger.error(f"无效的结果数据类型: {type(data)}") return try: # 更新推荐号码 if 'recommended_fronts' in data: self.recommend_front_var.set(' '.join(map(str, data['recommended_fronts']))) if 'recommended_backs' in data: self.recommend_back_var.set(' '.join(map(str, data['recommended_backs']))) # 更新结果文本框 result_text = f"分析完成: {time.strftime('%H:%M:%S')}\n" if 'recommended_fronts' in data: result_text += f"前区推荐: {', '.join(map(str, data['recommended_fronts']))}\n" if 'recommended_backs' in data: result_text += f"后区推荐: {', '.join(map(str, data['recommended_backs']))}\n" self._update_result_text(result_text) self.analyze_btn.config(state='normal') except Exception as display_err: self._show_error(f"结果显示错误: {display_err}") def _update_result_text(self, text, clear=False): """更新结果文本框内容""" self.result_text.config(state=tk.NORMAL) if clear: self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, text + "\n") self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) def _show_error(self, error_msg): """显示错误信息""" messagebox.showerror("分析错误", error_msg) self._update_result_text(f"分析失败: {error_msg}") self.recommend_front_var.set("分析失败") self.recommend_back_var.set("分析失败") def _auto_format_entry(self, event): """自动格式化输入框内容""" entry = event.widget current = entry.get().strip() if current.isdigit(): formatted = self._format_number(current) if formatted != current: entry.delete(0, tk.END) entry.insert(0, formatted) if len(current) == 2: self._focus_adjacent_entry(entry, 1) def _focus_adjacent_entry(self, current_entry, direction): """焦点跳转到相邻输入框""" all_entries = self.exclude_front_entries + self.exclude_back_entries try: current_index = all_entries.index(current_entry) target_index = current_index + direction if 0 <= target_index < len(all_entries): all_entries[target_index].focus() except ValueError: pass def _navigate_entry(self, event, direction): """使用方向键在输入框间导航""" self._focus_adjacent_entry(event.widget, direction) if __name__ == "__main__": root = tk.Tk() root.title("输入分析模块测试") root.geometry("600x600") main_frame = ttk.Frame(root) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) try: InputAnalysisUI(main_frame) except Exception as e: messagebox.showerror("致命错误", f"应用程序初始化失败: {e}") sys.exit(1) root.mainloop()
最新发布
08-27
写出完整代码::未使用的 import 语句 'import logging',意外实参,意外实参,意外实参意外实参意外实参意外实参,形参 'data' 未填,,2025-08-21 19:45:04,242 - __main__ - ERROR - 系统启动失败: InputAnalysisModule.register_callback() missing 1 required positional argument: 'callback_func' Traceback (most recent call last): File "C:\Users\Administrator\Desktop\project\main.py", line 158, in main main_ui = MainWindow(root, pool) # 确保MainWindow接受正确的参数 ^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\Administrator\Desktop\project\ui\main_window.py", line 114, in __init__ self._create_main_interface() File "C:\Users\Administrator\Desktop\project\ui\main_window.py", line 127, in _create_main_interface self.main_interface = MainInterface(self.root, self.pool) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\Administrator\Desktop\project\ui\main_interface.py", line 58, in __init__ self._init_modules() File "C:\Users\Administrator\Desktop\project\ui\main_interface.py", line 145, in _init_modules self._register_module_callback(self.module1) File "C:\Users\Administrator\Desktop\project\ui\main_interface.py", line 202, in _register_module_callback module.register_callback(self._handle_analysis_result) TypeError: InputAnalysisModule.register_callback() missing 1 required positional argument: 'callback_func' 进程已结束,退出代码为 1 代码#!/usr/bin/env python3 # -*- coding: utf-8 -*- # 标准库导入 import json import logging import os import sys import tkinter as tk import time import uuid from pathlib import Path from tkinter import ttk, messagebox, scrolledtext import logging # 项目路径设置 def add_project_to_path(): """确保项目根目录在Python路径中""" project_root = Path(__file__).parent.parent.absolute() if str(project_root) not in sys.path: sys.path.insert(0, str(project_root)) add_project_to_path() # 核心模块导入 try: from core.global_config import GlobalConfig from core.event_system import Event, EventType from core.event_center import event_center from tkinter import Frame, Label, Button print("成功导入核心模块") except ImportError as import_err: print(f"核心导入失败: {import_err}") sys.exit(1) # 本地模块导入 try: from input_analysis import InputAnalysisModule except ImportError as local_err: print(f"输入分析模块导入失败: {local_err}") sys.exit(1) class InputAnalysisUI: def __init__(self, parent_frame): self.logger = logging.getLogger(f"{__name__}.InputAnalysisUI") self.parent = parent_frame self.token = str(uuid.uuid4()) self._is_analyzing = False self._last_analysis_time = None try: # 修复:使用正确的模块初始化 self.module = InputAnalysisModule() # 修复:正确注册回调函数 self.module.register_callback("analysis_result", self._handle_analysis_result) except Exception as init_err: messagebox.showerror("初始化错误", f"模块初始化失败: {init_err}") raise # 修复:移除内部类定义 self.labels = [ "排除号码", "前区", "后区", "推荐号码", "前区", "后区" ] self.dynamic_data = { "排除号码": {"前区": [], "后区": []}, "推荐号码": {"前区": [], "后区": []} } self._setup_ui() self._register_event_handlers() self._load_saved_data() @staticmethod def _validate_number(text: str, min_val: int, max_val: int) -> bool: """验证输入的数字是否在指定范围内""" if not text: return True return text.isdigit() and min_val <= int(text) <= max_val def _validate_inputs(self): """验证所有输入有效性""" valid = True for entry in self.exclude_front_entries: if entry.get() and not self._validate_number(entry.get(), 1, 35): entry.config(foreground='red') valid = False else: entry.config(foreground='black') # 对后区做同样验证 for entry in self.exclude_back_entries: if entry.get() and not self._validate_number(entry.get(), 1, 12): entry.config(foreground='red') valid = False else: entry.config(foreground='black') return valid @staticmethod def _format_number(num_str: str) -> str: """格式化数字为两位数""" if not num_str.isdigit(): return num_str num = int(num_str) return f"{num:02d}" if 1 <= num <= 9 else str(num) def _setup_ui(self): """修复:使用正确的父组件类型""" self.frame = ttk.LabelFrame( self.parent, # 使用传递的parent_frame作为父组件 text="输入分析模块", font=('微软雅黑', 12, 'bold') ) self.frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 模块ID标识 module_id_label = ttk.Label( self.frame, text=f"模块ID: {GlobalConfig.MODULE_IDS['MODULE1_ID']}", font=('微软雅黑', 8), foreground='gray' ) module_id_label.pack(anchor=tk.NE, padx=10, pady=5) # 排除号码区 exclude_frame = ttk.LabelFrame(self.frame, text=" 排除号码 ", font=('微软雅黑', 10)) exclude_frame.pack(fill=tk.X, padx=20, pady=10, ipady=5) # 创建输入框的通用方法 def create_number_entries(parent, label_text, count, min_val, max_val): ttk.Label(parent, text=label_text, font=('微软雅黑', 10)).pack( anchor=tk.W, padx=10, pady=5) frame = ttk.Frame(parent) frame.pack(fill=tk.X, padx=10, pady=5) entries = [] for i in range(count): entry_frame = ttk.Frame(frame) entry_frame.pack(side=tk.LEFT, padx=2) ttk.Label(entry_frame, text=f"{i + 1}:").pack(side=tk.LEFT) entry = ttk.Entry(entry_frame, width=3, font=('微软雅黑', 10)) entry.pack(side=tk.LEFT) entry.config( validate="key", validatecommand=( entry.register( lambda text, mn=min_val, mx=max_val: self._validate_number(text, mn, mx) ), '%P' ) ) entry.bind("<KeyRelease>", self._auto_format_entry) entry.bind("<Left>", lambda evt, d=-1: self._navigate_entry(evt, d)) entry.bind("<Right>", lambda evt, d=1: self._navigate_entry(evt, d)) entries.append(entry) return entries # 排除号码标题 ttk.Label(exclude_frame, text=self.labels[0], font=('微软雅黑', 10, 'bold')).pack( anchor=tk.W, padx=10, pady=(5, 0)) # 创建前区和后区输入框 self.exclude_front_entries = create_number_entries( exclude_frame, self.labels[1], 10, 1, 35) self.exclude_back_entries = create_number_entries( exclude_frame, self.labels[2], 10, 1, 12) # 推荐号码区 recommend_frame = ttk.LabelFrame(self.frame, text=" 推荐号码 ", font=('微软雅黑', 10)) recommend_frame.pack(fill=tk.X, padx=20, pady=10, ipady=5) # 推荐号码标题 ttk.Label(recommend_frame, text=self.labels[3], font=('微软雅黑', 10, 'bold')).pack( anchor=tk.W, padx=10, pady=(5, 0)) # 前区推荐 front_rec_frame = ttk.Frame(recommend_frame) front_rec_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(front_rec_frame, text=self.labels[4], font=('微软雅黑', 10)).pack(side=tk.LEFT) self.recommend_front_var = tk.StringVar() ttk.Entry( front_rec_frame, textvariable=self.recommend_front_var, state='readonly', font=('微软雅黑', 10), readonlybackground='#f0f5f0' ).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) # 后区推荐 back_rec_frame = ttk.Frame(recommend_frame) back_rec_frame.pack(fill=tk.X, padx=10, pady=5) ttk.Label(back_rec_frame, text=self.labels[5], font=('微软雅黑', 10)).pack(side=tk.LEFT) self.recommend_back_var = tk.StringVar() ttk.Entry( back_rec_frame, textvariable=self.recommend_back_var, state='readonly', font=('微软雅黑', 10), readonlybackground='#f0f5f0' ).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) # 结果区 result_frame = ttk.LabelFrame(self.frame, text=" 分析结果 ", font=('微软雅黑', 10)) result_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10, ipady=5) # 结果文本框 self.result_text = scrolledtext.ScrolledText( result_frame, wrap=tk.WORD, font=('微软雅黑', 10), height=6 ) self.result_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) self.result_text.insert(tk.END, "请设置排除号码后点击'运行'按钮开始分析\n") self.result_text.config(state=tk.DISABLED) # 按钮区域 btn_frame = ttk.Frame(self.frame) btn_frame.pack(fill=tk.X, pady=10) self.analyze_btn = ttk.Button( btn_frame, text="运行分析", command=self._on_analyze_clicked ) self.analyze_btn.pack(side=tk.LEFT, padx=5) self.save_btn = ttk.Button( btn_frame, text="保存配置", command=self._save_config ) self.save_btn.pack(side=tk.LEFT, padx=5) def _safe_update_ui(self, callback, *args): """线程安全的UI更新""" if not self.frame.winfo_exists(): return self.frame.after(0, lambda: callback(*args)) def _register_event_handlers(self): """注册事件处理器""" # 修复:添加事件类型检查 if hasattr(EventType, 'ANALYSIS_RESULT'): event_center.subscribe( event_type=EventType.ANALYSIS_RESULT, token=self.token, callback=lambda event: self._handle_analysis_result(event) ) else: self.logger.warning("EventType缺少ANALYSIS_RESULT定义") def _handle_module_run(self, event): """处理模块运行事件""" if not hasattr(event, 'data'): self.logger.error("收到无效的运行事件: 缺少data属性") return if event.target != getattr(self.module, 'MODULE_ID', 'input_analysis'): return self._on_analyze_clicked() # 复用现有分析逻辑 def _load_saved_data(self): """加载保存的数据""" save_path = GlobalConfig.MODULE1_SAVE_PATH if not os.path.exists(save_path): return try: with open(save_path, 'r', encoding='utf-8') as f: saved_data = json.load(f) # 恢复前区排除号码 if 'exclude_front' in saved_data: for i, num in enumerate(saved_data['exclude_front']): if i < len(self.exclude_front_entries) and num: try: formatted_num = f"{int(num):02d}" self.exclude_front_entries[i].delete(0, tk.END) self.exclude_front_entries[i].insert(0, formatted_num) except ValueError: continue # 恢复后区排除号码 if 'exclude_back' in saved_data: for i, num in enumerate(saved_data['exclude_back']): if i < len(self.exclude_back_entries) and num: try: formatted_num = f"{int(num):02d}" self.exclude_back_entries[i].delete(0, tk.END) self.exclude_back_entries[i].insert(0, formatted_num) except ValueError: continue except Exception as load_err: self.logger.error(f"加载保存数据失败: {load_err}") self._update_result_text(f"加载保存数据失败: {load_err}") def _save_config(self): """保存当前配置""" save_data = { "exclude_front": [], "exclude_back": [] } # 收集前区排除号码 for entry in self.exclude_front_entries: if entry.get().strip(): try: save_data["exclude_front"].append(int(entry.get())) except ValueError: pass # 收集后区排除号码 for entry in self.exclude_back_entries: if entry.get().strip(): try: save_data["exclude_back"].append(int(entry.get())) except ValueError: pass # 保存到文件 try: with open(GlobalConfig.MODULE1_SAVE_PATH, 'w', encoding='utf-8') as f: json.dump(save_data, f, ensure_ascii=False, indent=2) self._update_result_text("配置保存成功") except Exception as save_err: self.logger.error(f"保存配置失败: {save_err}") self._update_result_text(f"保存配置失败: {save_err}") def _on_analyze_clicked(self): """处理分析按钮点击事件""" if not hasattr(Event, '__annotations__') or 'event_id' not in Event.__annotations__: self.logger.warning("Event类缺少event_id属性定义") exclude_front = [] exclude_back = [] try: # 收集前区排除号码 for entry in self.exclude_front_entries: if entry.get().strip(): try: exclude_front.append(int(entry.get())) except ValueError: pass # 收集后区排除号码 for entry in self.exclude_back_entries: if entry.get().strip(): try: exclude_back.append(int(entry.get())) except ValueError: pass # 准备分析数据 analysis_data = { 'exclude_front': exclude_front, 'exclude_back': exclude_back } # 确定事件类型 run_event_type = getattr(EventType, 'MODULE_RUN', getattr(EventType, 'MODULE_READY', 'module_run')) # 发送分析命令事件 command_event = Event( event_id=str(uuid.uuid4()), type=run_event_type, source=self.token, target=getattr(self.module, 'MODULE_ID', 'input_analysis'), data=analysis_data ) event_center.publish(command_event) self.analyze_btn.config(state='disabled') self._update_result_text("分析...", clear=True) except Exception as analyze_error: messagebox.showerror("输入错误", f"无效的输入格式: {analyze_error}") self.analyze_btn.config(state='normal') def run_analysis(self): """修复:使用正确的分析对象""" if self.module: result = self.module.analyze() print(result) # 或者将结果显示在UI上 else: print("分析模块未初始化") def _handle_analysis_result(self, event: Event): """处理分析结果事件""" if not hasattr(event, 'data'): self.logger.error("收到无效的分析结果事件: 缺少data属性") return self.frame.after(0, lambda: self._display_results(event.data)) def _handle_error(self, event: Event): """处理错误事件""" error_msg = event.data.get('error', '未知错误') if hasattr(event, 'data') else '未知错误' self.frame.after(0, lambda: self._show_error(error_msg)) self.frame.after(0, lambda: self.analyze_btn.config(state='normal')) def _display_results(self, data): """显示分析结果""" if not isinstance(data, dict): self.logger.error(f"无效的结果数据类型: {type(data)}") return try: # 更新推荐号码 if 'recommended_fronts' in data: self.recommend_front_var.set(' '.join(map(str, data['recommended_fronts']))) if 'recommended_backs' in data: self.recommend_back_var.set(' '.join(map(str, data['recommended_backs']))) # 更新结果文本框 result_text = f"分析完成: {time.strftime('%H:%M:%S')}\n" if 'recommended_fronts' in data: result_text += f"前区推荐: {', '.join(map(str, data['recommended_fronts']))}\n" if 'recommended_backs' in data: result_text += f"后区推荐: {', '.join(map(str, data['recommended_backs']))}\n" self._update_result_text(result_text) self.analyze_btn.config(state='normal') except Exception as display_err: self._show_error(f"结果显示错误: {display_err}") def _update_result_text(self, text, clear=False): """更新结果文本框内容""" self.result_text.config(state=tk.NORMAL) if clear: self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, text + "\n") self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) def _show_error(self, error_msg): """显示错误信息""" messagebox.showerror("分析错误", error_msg) self._update_result_text(f"分析失败: {error_msg}") self.recommend_front_var.set("分析失败") self.recommend_back_var.set("分析失败") def _auto_format_entry(self, event): """自动格式化输入框内容""" entry = event.widget current = entry.get().strip() if current.isdigit(): formatted = self._format_number(current) if formatted != current: entry.delete(0, tk.END) entry.insert(0, formatted) if len(current) == 2: self._focus_adjacent_entry(entry, 1) def _focus_adjacent_entry(self, current_entry, direction): """焦点跳转到相邻输入框""" all_entries = self.exclude_front_entries + self.exclude_back_entries try: current_index = all_entries.index(current_entry) target_index = current_index + direction if 0 <= target_index < len(all_entries): all_entries[target_index].focus() except ValueError: pass def _navigate_entry(self, event, direction): """使用方向键在输入框间导航""" self._focus_adjacent_entry(event.widget, direction) if __name__ == "__main__": root = tk.Tk() root.title("输入分析模块测试") root.geometry("600x600") main_frame = ttk.Frame(root) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) try: InputAnalysisUI(main_frame) except Exception as e: messagebox.showerror("致命错误", f"应用程序初始化失败: {e}") sys.exit(1) root.mainloop()
08-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值