import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
import struct
import binascii
class PrinterSpoolAnalyzer:
"""打印机脱机文件分析器"""
STATUS_CODES = {
0: ("暂停", "打印任务被暂停"),
1: ("错误", "打印过程中发生错误"),
2: ("正在删除", "打印任务正在被删除"),
3: ("正在打印", "打印任务正在进行中"),
4: ("脱机", "打印机处于脱机状态"),
5: ("缺纸", "打印机缺纸"),
6: ("用户干预", "需要用户干预"),
7: ("正在初始化", "打印机正在初始化"),
8: ("正在预热", "打印机正在预热"),
9: ("正在处理", "打印机正在处理任务"),
10: ("正在输出", "打印机正在输出"),
11: ("就绪", "打印任务准备就绪"),
12: ("正在等待", "打印任务正在等待"),
13: ("完成", "打印任务成功完成"),
14: ("保留", "保留状态"),
15: ("未知", "未知状态")
}
def __init__(self, root):
self.root = root
self.root.title("打印机脱机文件分析器")
self.root.geometry("800x600")
self.root.resizable(True, True)
self.create_widgets()
self.current_file = None
def create_widgets(self):
"""创建GUI界面组件"""
# 文件选择区域
file_frame = ttk.LabelFrame(self.root, text="文件选择")
file_frame.pack(fill=tk.X, padx=10, pady=5)
self.file_path = tk.StringVar()
ttk.Entry(file_frame, textvariable=self.file_path, width=70).pack(
side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
ttk.Button(file_frame, text="浏览...", command=self.browse_file).pack(
side=tk.LEFT, padx=5, pady=5)
ttk.Button(file_frame, text="分析文件", command=self.analyze_file).pack(
side=tk.LEFT, padx=5, pady=5)
# 文件信息显示区域
info_frame = ttk.LabelFrame(self.root, text="文件信息")
info_frame.pack(fill=tk.BOTH, padx=10, pady=5, expand=True)
columns = ("属性", "值")
self.tree = ttk.Treeview(info_frame, columns=columns, show="headings")
self.tree.heading("属性", text="属性")
self.tree.heading("值", text="值")
self.tree.column("属性", width=150, anchor=tk.W)
self.tree.column("值", width=600, anchor=tk.W)
scrollbar = ttk.Scrollbar(info_frame, orient=tk.VERTICAL, command=self.tree.yview)
self.tree.configure(yscroll=scrollbar.set)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.tree.pack(fill=tk.BOTH, expand=True)
# 状态分析区域
status_frame = ttk.LabelFrame(self.root, text="打印状态分析")
status_frame.pack(fill=tk.X, padx=10, pady=5)
self.status_text = tk.Text(status_frame, height=5, wrap=tk.WORD)
self.status_text.pack(fill=tk.X, padx=5, pady=5)
self.status_text.config(state=tk.DISABLED)
# 十六进制查看器
hex_frame = ttk.LabelFrame(self.root, text="十六进制查看器")
hex_frame.pack(fill=tk.BOTH, padx=10, pady=5, expand=True)
self.hex_text = tk.Text(hex_frame, wrap=tk.NONE, font=("Courier", 10))
self.hex_text.pack(fill=tk.BOTH, expand=True)
scrollbar_x = ttk.Scrollbar(hex_frame, orient=tk.HORIZONTAL, command=self.hex_text.xview)
scrollbar_y = ttk.Scrollbar(hex_frame, orient=tk.VERTICAL, command=self.hex_text.yview)
self.hex_text.configure(xscrollcommand=scrollbar_x.set, yscrollcommand=scrollbar_y.set)
scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X)
scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y)
def browse_file(self):
"""打开文件选择对话框"""
file_types = [
("打印机脱机文件", "*.shd *.spl *.tmp"),
("所有文件", "*.*")
]
file_path = filedialog.askopenfilename(
title="选择打印机脱机文件",
filetypes=file_types
)
if file_path:
self.file_path.set(file_path)
self.current_file = file_path
def analyze_file(self):
"""分析选定的打印机脱机文件"""
file_path = self.file_path.get()
if not file_path or not os.path.isfile(file_path):
messagebox.showerror("错误", "请选择有效的文件")
return
try:
# 清空之前的显示
for item in self.tree.get_children():
self.tree.delete(item)
# 解析文件
file_info = self.parse_file(file_path)
# 显示文件信息
for key, value in file_info.items():
self.tree.insert("", tk.END, values=(key, value))
# 显示状态分析
self.display_status_analysis(file_info)
# 显示十六进制内容
self.display_hex_content(file_path)
except Exception as e:
messagebox.showerror("解析错误", f"解析文件时出错: {str(e)}")
def parse_file(self, file_path):
"""解析打印机脱机文件"""
file_info = {
"文件名": os.path.basename(file_path),
"文件路径": os.path.dirname(file_path),
"文件大小": f"{os.path.getsize(file_path)} 字节",
"文件类型": self.get_file_type(file_path)
}
# 根据文件类型进行特定解析
try:
with open(file_path, 'rb') as f:
if file_path.lower().endswith('.shd'):
# SHD文件格式解析
header = f.read(128)
if len(header) >= 128:
# 解析状态码
status_code = struct.unpack('<I', header[0x48:0x4C])[0]
file_info["状态码"] = f"0x{status_code:08X}"
# 解析作业ID
job_id = struct.unpack('<I', header[0x00:0x04])[0]
file_info["作业ID"] = str(job_id)
# 解析打印机名称
printer_name = header[0x08:0x28].decode('utf-16le', errors='ignore').strip('\x00')
file_info["打印机名称"] = printer_name
# 解析文档名称
doc_name = header[0x28:0x48].decode('utf-16le', errors='ignore').strip('\x00')
file_info["文档名称"] = doc_name
# 解析提交时间
submit_time = struct.unpack('<Q', header[0x60:0x68])[0]
file_info["提交时间"] = self.filetime_to_str(submit_time)
elif file_path.lower().endswith('.spl'):
# SPL文件格式解析
header = f.read(64)
if len(header) >= 64:
# 解析作业ID
job_id = struct.unpack('<I', header[0x00:0x04])[0]
file_info["作业ID"] = str(job_id)
# 解析文档名称
doc_name = header[0x08:0x28].decode('utf-16le', errors='ignore').strip('\x00')
file_info["文档名称"] = doc_name
# 解析数据类型
data_type = struct.unpack('<I', header[0x2C:0x30])[0]
file_info["数据类型"] = self.get_data_type(data_type)
except Exception as e:
file_info["解析错误"] = str(e)
return file_info
def get_file_type(self, file_path):
"""获取文件类型描述"""
ext = os.path.splitext(file_path)[1].lower()
file_types = {
'.shd': "打印机影子文件 (SHD)",
'.spl': "打印机假脱机文件 (SPL)",
'.tmp': "打印机临时文件 (TMP)"
}
return file_types.get(ext, "未知文件类型")
def get_data_type(self, data_type):
"""获取数据类型描述"""
data_types = {
0: "原始数据",
1: "EMF (增强型图元文件)",
2: "文本",
3: "XPS (XML纸张规范)",
4: "PCL (打印机命令语言)",
5: "PostScript"
}
return data_types.get(data_type, f"未知数据类型 ({data_type})")
def filetime_to_str(self, filetime):
"""将Windows FILETIME转换为可读时间"""
if filetime == 0:
return "未设置"
# FILETIME是自1601-01-01以来的100纳秒间隔数
# 转换为UNIX时间戳(自1970-01-01以来的秒数)
unix_time = (filetime - 116444736000000000) // 10000000
from datetime import datetime
return datetime.utcfromtimestamp(unix_time).strftime('%Y-%m-%d %H:%M:%S')
def display_status_analysis(self, file_info):
"""显示状态分析结果"""
self.status_text.config(state=tk.NORMAL)
self.status_text.delete(1.0, tk.END)
if "状态码" in file_info:
status_hex = file_info["状态码"]
try:
# 提取状态码的低4位(主要状态)
status_code = int(status_hex, 16) & 0xF
status_name, status_desc = self.STATUS_CODES.get(
status_code, ("未知", "无法识别的状态码"))
# 判断打印是否成功
is_success = (status_code == 13) # 13 = 完成
# 显示分析结果
self.status_text.insert(tk.END, f"状态码: {status_hex}\n")
self.status_text.insert(tk.END, f"状态: {status_name}\n")
self.status_text.insert(tk.END, f"描述: {status_desc}\n")
self.status_text.insert(tk.END, "\n打印结果: ")
if is_success:
self.status_text.insert(tk.END, "✅ 打印成功完成", "success")
else:
self.status_text.insert(tk.END, "❌ 打印未成功完成", "error")
# 设置文本样式
self.status_text.tag_config("success", foreground="green")
self.status_text.tag_config("error", foreground="red")
except ValueError:
self.status_text.insert(tk.END, "错误: 无效的状态码格式")
else:
self.status_text.insert(tk.END, "此文件类型不包含可解析的状态信息")
self.status_text.config(state=tk.DISABLED)
def display_hex_content(self, file_path):
"""以十六进制格式显示文件内容"""
self.hex_text.config(state=tk.NORMAL)
self.hex_text.delete(1.0, tk.END)
try:
with open(file_path, 'rb') as f:
content = f.read()
# 每行显示16字节
for i in range(0, len(content), 16):
# 地址偏移量
line = f"{i:08X}: "
# 十六进制表示
hex_part = ' '.join(
f"{content[j]:02X}" if j < len(content) else " "
for j in range(i, i+8)
)
hex_part += " "
hex_part += ' '.join(
f"{content[j]:02X}" if j < len(content) else " "
for j in range(i+8, i+16)
)
# ASCII表示
ascii_part = ''.join(
chr(content[j]) if 32 <= content[j] <= 126 else '.'
for j in range(i, min(i+16, len(content)))
)
self.hex_text.insert(tk.END, f"{line}{hex_part} |{ascii_part}|\n")
except Exception as e:
self.hex_text.insert(tk.END, f"读取文件时出错: {str(e)}")
self.hex_text.config(state=tk.DISABLED)
if __name__ == "__main__":
root = tk.Tk()
app = PrinterSpoolAnalyzer(root)
root.mainloop()根据这个代码修改上述问题,给出全部代码
最新发布