feat(box): 增强崩溃报告功能并添加单实例锁- 重新设计崩溃报告界面,增加更多详细信息

- 添加单实例锁机制,防止多个实例同时运行
- 更新UI样式,优化用户体验
- 修复一些小问题
This commit is contained in:
tzdwindows 7
2025-06-01 08:39:07 +08:00
parent e475e84851
commit 4dfdec9055
9 changed files with 625 additions and 116 deletions

View File

@@ -72,6 +72,7 @@ dependencies {
implementation 'org.graalvm.python:python-embedding:24.2.1' implementation 'org.graalvm.python:python-embedding:24.2.1'
implementation files('libs/JNC-1.0-jnc.jar') implementation files('libs/JNC-1.0-jnc.jar')
implementation files('libs/dog api 1.3.jar')
implementation 'org.fxmisc.richtext:richtextfx:0.11.0' // 更新后的richtextfx implementation 'org.fxmisc.richtext:richtextfx:0.11.0' // 更新后的richtextfx
implementation 'org.bitbucket.mstrobel:procyon-core:0.5.36' // 使用JitPack版本 implementation 'org.bitbucket.mstrobel:procyon-core:0.5.36' // 使用JitPack版本

View File

@@ -1,3 +1,3 @@
#Current Loaded Language #Current Loaded Language
#Thu May 01 15:04:30 CST 2025 #Sat May 31 10:30:35 CST 2025
loadedLanguage=system\:zh_CN loadedLanguage=system\:zh_CN

View File

@@ -24,6 +24,7 @@ andShow.pluginInfo.writer=\u62A5\u544A\u5DF2\u4FDD\u5B58\u81F3:
andShow.pluginInfo.writer.title=\u5BFC\u51FA\u6210\u529F andShow.pluginInfo.writer.title=\u5BFC\u51FA\u6210\u529F
andShow.pluginInfo.writer.error=\u5BFC\u51FA\u5931\u8D25: andShow.pluginInfo.writer.error=\u5BFC\u51FA\u5931\u8D25:
andShow.pluginInfo.error.writer.title=\u9519\u8BEF andShow.pluginInfo.error.writer.title=\u9519\u8BEF
save.crash.report=\u4FDD\u5B58\u5D29\u6E83\u62A5\u544A
progressBarManager.title=\u52A0\u8F7D\u4E2D... progressBarManager.title=\u52A0\u8F7D\u4E2D...
@@ -117,3 +118,7 @@ settings.4.load_theme_success.2=' \u5DF2\u6210\u529F\u52A0\u8F7D\uFF01
settings.4.load_theme_success.3=\u6210\u529F settings.4.load_theme_success.3=\u6210\u529F
settings.4.load_theme_error=\u52A0\u8F7D\u4E3B\u9898\u5931\u8D25: settings.4.load_theme_error=\u52A0\u8F7D\u4E3B\u9898\u5931\u8D25:
settings.4.load_theme_error.title=\u9519\u8BEF settings.4.load_theme_error.title=\u9519\u8BEF
core.plugins=\u6838\u5FC3\u63D2\u4EF6
plugin.info=\u63D2\u4EF6\u4FE1\u606F
error.details=\u9519\u8BEF\u8BE6\u60C5

BIN
library/DogAgent.dll Normal file

Binary file not shown.

BIN
libs/dog api 1.3.jar Normal file

Binary file not shown.

View File

@@ -1,3 +1,4 @@
#Obfuscation Mapping Table #Obfuscation Mapping Table
#Sun Feb 23 18:01:01 CST 2025 #Sun Feb 23 18:01:01 CST 2025
com/axis/innovators/box/AxisInnovatorsBox=a/b/c/d/A com/axis/innovators/box/AxisInnovatorsBox=a/b/c/d/A

View File

@@ -20,16 +20,29 @@ import com.axis.innovators.box.verification.UserTags;
import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.FlatLightLaf;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.config.Configuration;
import org.api.dog.agent.VirtualMachine;
import org.jetbrains.annotations.NotNull;
import org.tzd.lm.LM; import org.tzd.lm.LM;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.*;
import java.io.*; import java.io.*;
import java.util.ArrayList; import java.lang.instrument.Instrumentation;
import java.util.Arrays; import java.lang.management.*;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/** /**
* 主类 * 主类
@@ -69,6 +82,7 @@ public class AxisInnovatorsBox {
try { try {
LibraryLoad.loadLibrary("FridaNative"); LibraryLoad.loadLibrary("FridaNative");
LibraryLoad.loadLibrary("ThrowSafely"); LibraryLoad.loadLibrary("ThrowSafely");
LibraryLoad.loadLibrary("DogAgent");
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to load the 'FridaNative' library", e); logger.error("Failed to load the 'FridaNative' library", e);
} }
@@ -103,6 +117,7 @@ public class AxisInnovatorsBox {
* 组织崩溃报告 * 组织崩溃报告
*/ */
public void organizingCrashReports(Exception e) { public void organizingCrashReports(Exception e) {
SwingUtilities.invokeLater(() -> {
String systemOut = Log4j2OutputStream.systemOutContent.toString(); String systemOut = Log4j2OutputStream.systemOutContent.toString();
String systemErr = Log4j2OutputStream.systemErrContent.toString(); String systemErr = Log4j2OutputStream.systemErrContent.toString();
@@ -115,117 +130,554 @@ public class AxisInnovatorsBox {
"\n========== 异常堆栈跟踪 ==========\n" + stackTrace; "\n========== 异常堆栈跟踪 ==========\n" + stackTrace;
SwingUtilities.invokeLater(() -> createAndShowGUI(report)); SwingUtilities.invokeLater(() -> createAndShowGUI(report));
});
} }
private void createAndShowGUI(String reportContent) { private void createAndShowGUI(String reportContent) {
JDialog dialog = new JDialog((Frame) null, // 使用 JFrame 作为顶级窗口
LanguageManager.getLoadedLanguages().getText("andShow.title"), true); JFrame dialog = new JFrame(LanguageManager.getLoadedLanguages().getText("andShow.title"));
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
dialog.setLayout(new BorderLayout(10, 10));
try { // 1. 添加窗口关闭监听器,在关闭时调用 quit()
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); dialog.addWindowListener(new WindowAdapter() {
} catch (Exception ignored) {} @Override
public void windowClosed(WindowEvent e) {
quit();
}
JPanel topPanel = new JPanel(new BorderLayout(10, 10)); @Override
public void windowClosing(WindowEvent e) {
quit();
}
});
// 2. 设置窗口置顶
dialog.setAlwaysOnTop(true);
dialog.setLayout(new BorderLayout(15, 15));
dialog.getRootPane().setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
// 顶部面板 - 使用卡片布局增强层次感
JPanel topPanel = new JPanel(new BorderLayout(10, 15));
topPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createMatteBorder(0, 0, 1, 0, new Color(220, 220, 220)),
BorderFactory.createEmptyBorder(15, 15, 15, 15)
));
// 图标和标题
JPanel headerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 15, 0));
Icon errorIcon = UIManager.getIcon("OptionPane.errorIcon"); Icon errorIcon = UIManager.getIcon("OptionPane.errorIcon");
JLabel iconLabel = new JLabel(errorIcon); JLabel iconLabel = new JLabel(errorIcon);
JLabel titleLabel = new JLabel(LanguageManager.getLoadedLanguages().getText("andShow.title.2")); JLabel titleLabel = new JLabel(LanguageManager.getLoadedLanguages().getText("andShow.title.2"));
titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 16));
titleLabel.setForeground(new Color(200, 0, 0));
headerPanel.add(iconLabel);
headerPanel.add(titleLabel);
// 说明文本
JLabel feedbackLabel = new JLabel(LanguageManager.getLoadedLanguages().getText("andShow.title.3")); JLabel feedbackLabel = new JLabel(LanguageManager.getLoadedLanguages().getText("andShow.title.3"));
feedbackLabel.setHorizontalAlignment(SwingConstants.CENTER); feedbackLabel.setFont(feedbackLabel.getFont().deriveFont(14f));
feedbackLabel.setForeground(new Color(80, 80, 80));
feedbackLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 10));
JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 10)); topPanel.add(headerPanel, BorderLayout.NORTH);
titlePanel.add(iconLabel);
titlePanel.add(titleLabel);
topPanel.add(titlePanel, BorderLayout.NORTH);
topPanel.add(feedbackLabel, BorderLayout.CENTER); topPanel.add(feedbackLabel, BorderLayout.CENTER);
topPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// 内容区域 - 使用现代样式
JTextArea contentArea = new JTextArea(reportContent); JTextArea contentArea = new JTextArea(reportContent);
contentArea.setEditable(false); contentArea.setEditable(false);
contentArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); contentArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
contentArea.setBackground(new Color(30, 30, 35));
contentArea.setCaretColor(Color.WHITE);
contentArea.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 15));
// 插件信息
StringBuilder pluginInfo = new StringBuilder(); StringBuilder pluginInfo = new StringBuilder();
pluginInfo.append( pluginInfo.append(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title"))
LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title")) .append("\n\n");
.append("\n");
List<PluginDescriptor> corePluginList = new ArrayList<>(); List<PluginDescriptor> corePluginList = new ArrayList<>();
for (PluginDescriptor plugin : PluginLoader.getLoadedPlugins()) { for (PluginDescriptor plugin : PluginLoader.getLoadedPlugins()) {
pluginInfo.append(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title.1")).append(plugin.getName()).append("\n"); pluginInfo.append("").append(plugin.getName()).append("\n");
pluginInfo.append(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title.2")).append(plugin.getDescription()).append("\n"); pluginInfo.append(" ").append(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title.2"))
pluginInfo.append(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title.3")).append(plugin.getSupportedVersions()).append("\n\n"); .append(plugin.getDescription()).append("\n");
pluginInfo.append(" ").append(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title.3"))
.append(plugin.getSupportedVersions()).append("\n\n");
if (plugin.getTransformers() != null) { if (plugin.getTransformers() != null) {
corePluginList.add(plugin); corePluginList.add(plugin);
} }
} }
pluginInfo.append("=== 核心插件 ===\n"); pluginInfo.append("=== ").append(LanguageManager.getLoadedLanguages().getText("core.plugins")).append(" ===\n\n");
for (PluginDescriptor corePlugin : corePluginList) { for (PluginDescriptor corePlugin : corePluginList) {
pluginInfo.append("核心插件主类: ").append(corePlugin.getName()).append("\n"); pluginInfo.append(" ").append(corePlugin.getName()).append("\n");
pluginInfo.append("核心转换器位置: ").append(corePlugin.getTransformers()).append("\n"); pluginInfo.append(" ").append(LanguageManager.getLoadedLanguages().getText("transformer.location"))
.append(corePlugin.getTransformers()).append("\n");
} }
contentArea.append("\n\n=== 插件信息 ===\n"); contentArea.append("\n\n=== " +
LanguageManager.getLoadedLanguages().getText("plugin.info") +
" ===\n");
contentArea.append(pluginInfo.toString()); contentArea.append(pluginInfo.toString());
JScrollPane scrollPane = new JScrollPane(contentArea); JScrollPane scrollPane = new JScrollPane(contentArea);
scrollPane.setBorder(BorderFactory.createTitledBorder("错误详细信息")); scrollPane.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(new Color(180, 180, 180)),
LanguageManager.getLoadedLanguages().getText("error.details")),
BorderFactory.createEmptyBorder(5, 5, 5, 5)
));
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10)); // 按钮面板
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 15, 10));
buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
// 创建按钮
JButton exportButton = new JButton(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.exportButton")); JButton exportButton = new JButton(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.exportButton"));
JButton closeButton = new JButton(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.closeButton")); JButton closeButton = new JButton(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.closeButton"));
exportButton.setForeground(Color.BLACK); // 设置按钮样式
closeButton.setForeground(Color.BLACK); setupModernButton(exportButton, new Color(0, 115, 207), new Color(0, 95, 180));
setupModernButton(closeButton, new Color(100, 100, 100), new Color(70, 70, 70));
exportButton.setBackground(new Color(0, 120, 215)); exportButton.addActionListener(e -> {
exportButton.setFocusPainted(false);
closeButton.setBackground(new Color(79, 79, 79));
closeButton.setFocusPainted(false);
exportButton.addActionListener((ActionEvent e) -> {
JFileChooser fileChooser = new JFileChooser(); JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("保存崩溃报告"); fileChooser.setDialogTitle(LanguageManager.getLoadedLanguages().getText("save.crash.report"));
fileChooser.setSelectedFile(new File("crash_report.txt"));
int userSelection = fileChooser.showSaveDialog(dialog); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
if (userSelection == JFileChooser.APPROVE_OPTION) { Date date = new Date(System.currentTimeMillis());
File fileToSave = fileChooser.getSelectedFile(); String timestamp = formatter.format(date);
try (FileWriter writer = new FileWriter(fileToSave)) {
writer.write(reportContent + "\n" + pluginInfo); TimeZone tz = TimeZone.getDefault();
String timezone = tz.getDisplayName(tz.inDaylightTime(date), TimeZone.SHORT).replace(":", "");
String filename = "AxisInnovatorsBox崩溃诊断报告_" + timestamp + "_" + timezone + ".zip";
fileChooser.setSelectedFile(new File(filename));
// 设置ZIP文件过滤器
fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("ZIP Files", "zip"));
if (fileChooser.showSaveDialog(dialog) == JFileChooser.APPROVE_OPTION) {
File zipFile = fileChooser.getSelectedFile();
if (!zipFile.getName().toLowerCase().endsWith(".zip")) {
zipFile = new File(zipFile.getAbsolutePath() + ".zip");
}
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {
// 1. 添加崩溃报告文件
ZipEntry crashEntry = new ZipEntry("basic_information.txt");
zos.putNextEntry(crashEntry);
String reportData = reportContent + "\n\n" + pluginInfo;
zos.write(reportData.getBytes(StandardCharsets.UTF_8));
zos.closeEntry();
// 2. 添加log4j日志文件
addLog4jLogsToZip(zos);
// 3. 添加其他调试文件
addDebugFilesToZip(zos);
// 成功消息
String message = LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.writer") +
"\n" + zipFile.getAbsolutePath();
JOptionPane.showMessageDialog(dialog, JOptionPane.showMessageDialog(dialog,
LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.writer") message,
+ "\n" + fileToSave.getAbsolutePath(),
LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.writer.title"), LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.writer.title"),
JOptionPane.INFORMATION_MESSAGE); JOptionPane.INFORMATION_MESSAGE);
} catch (IOException ex) { } catch (IOException ex) {
String errorMessage = LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.writer.error") +
"\n" + ex.getMessage();
JOptionPane.showMessageDialog(dialog, JOptionPane.showMessageDialog(dialog,
LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.writer.error") errorMessage,
+ "\n" + ex.getMessage(),
LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.error.writer.title"), LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.error.writer.title"),
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
} }
} }
}); });
closeButton.addActionListener(e -> quit()); // 3. 添加错误音效并关闭窗口
closeButton.addActionListener(e -> {
playErrorSound(); // 播放错误音效
dialog.dispose(); // 关闭窗口
quit(); // 调用退出函数
});
buttonPanel.add(exportButton); buttonPanel.add(exportButton);
buttonPanel.add(closeButton); buttonPanel.add(closeButton);
// 组装主界面
dialog.add(topPanel, BorderLayout.NORTH); dialog.add(topPanel, BorderLayout.NORTH);
dialog.add(scrollPane, BorderLayout.CENTER); dialog.add(scrollPane, BorderLayout.CENTER);
dialog.add(buttonPanel, BorderLayout.SOUTH); dialog.add(buttonPanel, BorderLayout.SOUTH);
dialog.setSize(800, 600); dialog.setSize(900, 650);
dialog.setLocationRelativeTo(null); dialog.setLocationRelativeTo(null);
// 播放错误音效
playErrorSound();
// 显示窗口
dialog.setVisible(true); dialog.setVisible(true);
} }
// 播放错误音效的方法
private void playErrorSound() {
try {
// 使用系统默认的错误音效
Toolkit.getDefaultToolkit().beep();
// 或者播放自定义音效
/*
File soundFile = new File("error_sound.wav");
if (soundFile.exists()) {
AudioInputStream audioIn = AudioSystem.getAudioInputStream(soundFile);
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
}
*/
} catch (Exception ex) {
// 忽略音效播放错误
}
}
private void addLog4jLogsToZip(ZipOutputStream zos) throws IOException {
LoggerContext context = (LoggerContext) LogManager.getContext(false);
Configuration config = context.getConfiguration();
Set<String> addedFiles = new HashSet<>();
// 获取所有配置的附加器
Collection<Appender> appenders = config.getAppenders().values();
for (Appender appender : appenders) {
processAppender(zos, addedFiles, appender);
}
}
private void processAppender(ZipOutputStream zos, Set<String> addedFiles,
Appender appender) throws IOException {
if (appender instanceof FileAppender) {
FileAppender fileAppender = (FileAppender) appender;
String fileName = fileAppender.getFileName();
if (fileName != null && !addedFiles.contains(fileName)) {
addFileToZip(zos, new File(fileName), "logs/");
addedFiles.add(fileName);
}
}
if (appender instanceof RollingFileAppender rollingAppender) {
addRollingLogFiles(zos, rollingAppender, addedFiles);
}
}
private void addRollingLogFiles(ZipOutputStream zos, RollingFileAppender appender,
Set<String> addedFiles) throws IOException {
String fileName = appender.getFileName();
if (fileName == null) {
return;
}
File logFile = new File(fileName);
File logDir = logFile.getParentFile();
String baseName = getString(appender);
if (logDir != null && logDir.exists()) {
File[] logFiles = logDir.listFiles((dir, name) ->
name.startsWith(baseName) || name.startsWith("box"));
if (logFiles != null) {
for (File file : logFiles) {
String absolutePath = file.getAbsolutePath();
if (!addedFiles.contains(absolutePath)) {
addFileToZip(zos, file, "logs/");
addedFiles.add(absolutePath);
}
}
}
}
}
private static @NotNull String getString(RollingFileAppender appender) {
String baseName;
String filePattern = appender.getFilePattern();
if (filePattern != null) {
// 移除目录部分
int lastSlash = Math.max(filePattern.lastIndexOf('/'), filePattern.lastIndexOf('\\'));
if (lastSlash > 0) {
filePattern = filePattern.substring(lastSlash + 1);
}
int patternStart = filePattern.indexOf('%');
if (patternStart == -1) patternStart = filePattern.indexOf('$');
if (patternStart > 0) {
baseName = filePattern.substring(0, patternStart);
} else {
baseName = "box";
}
} else {
baseName = "box";
}
return baseName;
}
private void addDebugFilesToZip(ZipOutputStream zos) throws IOException {
// 1. 添加系统信息文件
addFileToZip(zos, generateSystemInfoFile(), "debug_files/system_info.txt");
// 2. 添加JVM加载的类信息
addFileToZip(zos, generateClassLoaderInfo(), "debug_files/class_loader_info.txt");
// 3. 添加内存状态信息
addFileToZip(zos, generateMemoryInfo(), "debug_files/memory_info.txt");
// 4. 添加线程堆栈信息
addFileToZip(zos, generateThreadDump(), "debug_files/thread_dump.txt");
// 5. 添加GC信息
addFileToZip(zos, generateGCInfo(), "debug_files/gc_info.txt");
// 6. 添加系统属性
addFileToZip(zos, generateSystemProperties(), "debug_files/system_properties.txt");
// 7. 添加环境变量
addFileToZip(zos, generateEnvironmentVariables(), "debug_files/environment_variables.txt");
}
private File generateClassLoaderInfo() throws IOException {
File tempFile = File.createTempFile("class_loader", ".txt");
try (PrintWriter writer = new PrintWriter(tempFile)) {
writer.println("===== Class Loader Hierarchy =====");
Instrumentation instrumentation = null;
try {
VirtualMachine vm = VirtualMachine.getVirtualMachine(ProcessHandle.current().pid(), true);
instrumentation = vm.getInstrumentation();
} catch (Exception e) {
writer.println("Failed to attach to VM: " + e.getMessage());
e.printStackTrace(writer);
}
if (instrumentation != null) {
writer.println("\n===== Loaded Classes =====");
Class<?>[] allClasses = instrumentation.getAllLoadedClasses();
writer.println("Total Classes: " + allClasses.length);
Map<ClassLoader, List<Class<?>>> classesByLoader = Arrays.stream(allClasses)
.collect(Collectors.groupingBy(
cls -> cls.getClassLoader() == null ?
BootstrapClassLoader.INSTANCE :
cls.getClassLoader()
));
for (Map.Entry<ClassLoader, List<Class<?>>> entry : classesByLoader.entrySet()) {
ClassLoader loader = entry.getKey();
List<Class<?>> classes = entry.getValue();
String loaderName = (loader == BootstrapClassLoader.INSTANCE) ?
"Bootstrap ClassLoader" :
loader.toString();
writer.println("\nClassLoader: " + loaderName);
writer.println("Classes Count: " + classes.size());
for (Class<?> cls : classes) {
writer.println(" - " + cls.getName());
}
}
}
}
tempFile.deleteOnExit();
return tempFile;
}
private static class BootstrapClassLoader extends ClassLoader {
static final BootstrapClassLoader INSTANCE = new BootstrapClassLoader();
private BootstrapClassLoader() {}
@Override
public String toString() {
return "Bootstrap ClassLoader";
}
}
private File generateMemoryInfo() throws IOException {
File tempFile = File.createTempFile("memory", ".txt");
try (PrintWriter writer = new PrintWriter(tempFile)) {
MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean();
writer.println("===== Heap Memory Usage =====");
MemoryUsage heapUsage = memoryMxBean.getHeapMemoryUsage();
writer.println("Init: " + formatMemory(heapUsage.getInit()));
writer.println("Used: " + formatMemory(heapUsage.getUsed()));
writer.println("Committed: " + formatMemory(heapUsage.getCommitted()));
writer.println("Max: " + formatMemory(heapUsage.getMax()));
writer.println("\n===== Non-Heap Memory Usage =====");
MemoryUsage nonHeapUsage = memoryMxBean.getNonHeapMemoryUsage();
writer.println("Init: " + formatMemory(nonHeapUsage.getInit()));
writer.println("Used: " + formatMemory(nonHeapUsage.getUsed()));
writer.println("Committed: " + formatMemory(nonHeapUsage.getCommitted()));
writer.println("Max: " + formatMemory(nonHeapUsage.getMax()));
writer.println("\n===== Memory Pool Details =====");
List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean pool : pools) {
writer.println("\nPool: " + pool.getName());
writer.println("Type: " + pool.getType());
MemoryUsage usage = pool.getUsage();
writer.println("Usage: " + formatMemory(usage.getUsed()) + " / " + formatMemory(usage.getCommitted()));
}
}
tempFile.deleteOnExit();
return tempFile;
}
private String formatMemory(long bytes) {
if (bytes < 1024) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(1024));
char unit = "KMGTPE".charAt(exp - 1);
return String.format("%.1f %sB", bytes / Math.pow(1024, exp), unit);
}
private File generateThreadDump() throws IOException {
File tempFile = File.createTempFile("thread_dump", ".txt");
try (PrintWriter writer = new PrintWriter(tempFile)) {
ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMxBean.dumpAllThreads(true, true);
writer.println("===== Thread Dump (" + threadInfos.length + " threads) =====");
for (ThreadInfo threadInfo : threadInfos) {
writer.println("\nThread #" + threadInfo.getThreadId() + ": " + threadInfo.getThreadName());
writer.println("State: " + threadInfo.getThreadState());
writer.println("Stack Trace:");
for (StackTraceElement stackTraceElement : threadInfo.getStackTrace()) {
writer.println(" " + stackTraceElement);
}
}
}
tempFile.deleteOnExit();
return tempFile;
}
private File generateGCInfo() throws IOException {
File tempFile = File.createTempFile("gc_", ".txt");
try (PrintWriter writer = new PrintWriter(tempFile)) {
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
writer.println("===== Garbage Collection =====");
for (GarbageCollectorMXBean gcBean : gcBeans) {
writer.println("\nCollector: " + gcBean.getName());
writer.println("Collections: " + gcBean.getCollectionCount());
writer.println("Time: " + gcBean.getCollectionTime() + " ms");
}
}
tempFile.deleteOnExit();
return tempFile;
}
// 生成系统属性
private File generateSystemProperties() throws IOException {
File tempFile = File.createTempFile("system_props", ".txt");
try (PrintWriter writer = new PrintWriter(tempFile)) {
Properties props = System.getProperties();
writer.println("===== System Properties =====");
for (String name : props.stringPropertyNames()) {
writer.println(name + " = " + props.getProperty(name));
}
}
tempFile.deleteOnExit();
return tempFile;
}
// 生成环境变量
private File generateEnvironmentVariables() throws IOException {
File tempFile = File.createTempFile("env_vars", ".txt");
try (PrintWriter writer = new PrintWriter(tempFile)) {
Map<String, String> env = System.getenv();
writer.println("===== Environment Variables =====");
for (Map.Entry<String, String> entry : env.entrySet()) {
writer.println(entry.getKey() + " = " + entry.getValue());
}
}
tempFile.deleteOnExit();
return tempFile;
}
// 添加目录到ZIP递归
private void addDirectoryToZip(ZipOutputStream zos, File dir, String basePath) throws IOException {
if (!dir.exists() || !dir.isDirectory()) return;
for (File file : dir.listFiles()) {
String entryPath = basePath + "/" + file.getName();
if (file.isDirectory()) {
addDirectoryToZip(zos, file, entryPath);
} else {
addFileToZip(zos, file, entryPath);
}
}
}
private void addFileToZip(ZipOutputStream zos, File file, String entryPath) throws IOException {
if (!file.exists()) return;
String entryName = entryPath + file.getName();
ZipEntry zipEntry = new ZipEntry(entryName);
zos.putNextEntry(zipEntry);
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) > 0) {
zos.write(buffer, 0, length);
}
}
zos.closeEntry();
}
// 生成系统信息文件的方法
private File generateSystemInfoFile() throws IOException {
File tempFile = File.createTempFile("system_info", ".txt");
try (PrintWriter writer = new PrintWriter(tempFile)) {
writer.println("===== System Information =====");
writer.println("OS: " + System.getProperty("os.name"));
writer.println("Version: " + System.getProperty("os.version"));
writer.println("Java Version: " + System.getProperty("java.version"));
writer.println("User: " + System.getProperty("user.name"));
writer.println("\n===== Runtime Information =====");
writer.println("Free Memory: " + Runtime.getRuntime().freeMemory() / (1024 * 1024) + " MB");
writer.println("Max Memory: " + Runtime.getRuntime().maxMemory() / (1024 * 1024) + " MB");
}
tempFile.deleteOnExit();
return tempFile;
}
private void setupModernButton(JButton button, Color bgColor, Color hoverColor) {
button.setFont(button.getFont().deriveFont(Font.BOLD));
button.setFocusPainted(false);
button.setBorder(BorderFactory.createEmptyBorder(8, 20, 8, 20));
button.setBackground(bgColor);
button.setForeground(Color.WHITE);
button.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
button.setBackground(hoverColor);
}
@Override
public void mouseExited(MouseEvent e) {
button.setBackground(bgColor);
}
});
button.getModel().addChangeListener(e -> {
if (button.getModel().isPressed()) {
button.setBackground(hoverColor.darker());
} else {
button.setBackground(button.getModel().isRollover() ? hoverColor : bgColor);
}
});
}
/** /**
* 初始化Log4j2 * 初始化Log4j2
*/ */
@@ -316,9 +768,6 @@ public class AxisInnovatorsBox {
windowsJDialog.repaint(); windowsJDialog.repaint();
} }
//public void execute(Runnable runnable){
// thread.
//}
/** /**
* 重新加载窗口 * 重新加载窗口
@@ -394,6 +843,7 @@ public class AxisInnovatorsBox {
main.runWindow(); main.runWindow();
} catch (Exception e) { } catch (Exception e) {
logger.error("There was a problem starting the main thread", e); logger.error("There was a problem starting the main thread", e);
if (main.ex != null)
main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.organizingCrashReports(e); main.organizingCrashReports(e);
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -407,8 +857,10 @@ public class AxisInnovatorsBox {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}, "TrayThread").start(); }, "TrayThread").start();
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed to load plugins", e); logger.error("Failed to load plugins", e);
if (main.ex != null)
main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.organizingCrashReports(e); main.organizingCrashReports(e);
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -418,8 +870,10 @@ public class AxisInnovatorsBox {
main.thread.start(); main.thread.start();
} catch (Exception e) { } catch (Exception e) {
logger.error("In unexpected errors", e); logger.error("In unexpected errors", e);
if (main.ex != null)
main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.organizingCrashReports(e); main.organizingCrashReports(e);
throw new RuntimeException(e);
} }
} }

View File

@@ -9,6 +9,12 @@ import com.axis.innovators.box.tools.FolderCreator;
import com.axis.innovators.box.register.LanguageManager; import com.axis.innovators.box.register.LanguageManager;
import javax.swing.*; import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -16,7 +22,23 @@ import java.util.Map;
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class Main { public class Main {
// 单实例锁文件位置(系统临时目录)
private static final String LOCK_FILE = System.getProperty("java.io.tmpdir") + "/axis_innovators_box.lock";
private static FileLock lock = null;
private static RandomAccessFile lockFile = null;
private static FileChannel lockChannel = null;
public static void main(String[] args) { public static void main(String[] args) {
if (!acquireLock()) {
JOptionPane.showMessageDialog(
null,
"程序已在运行中,无法启动多个实例",
"错误",
JOptionPane.ERROR_MESSAGE
);
System.exit(1);
}
FolderCleaner.cleanFolder(FolderCreator.getLogsFolder(), 10); FolderCleaner.cleanFolder(FolderCreator.getLogsFolder(), 10);
LanguageManager.loadSavedLanguage(); LanguageManager.loadSavedLanguage();
@@ -38,15 +60,63 @@ public class Main {
ModernJarViewer viewer = new ModernJarViewer(null, path); ModernJarViewer viewer = new ModernJarViewer(null, path);
viewer.setVisible(true); viewer.setVisible(true);
}); });
releaseLock(); // 释放锁(窗口模式)
return; return;
} }
if (".html".equals(extension)) { if (".html".equals(extension)) {
MainApplication.popupHTMLWindow(path); MainApplication.popupHTMLWindow(path);
releaseLock(); // 释放锁(窗口模式)
return; return;
} }
} }
AxisInnovatorsBox.run(args); AxisInnovatorsBox.run(args);
} }
/**
* 尝试获取文件锁(单实例检查)
*/
private static boolean acquireLock() {
try {
lockFile = new RandomAccessFile(LOCK_FILE, "rw");
lockChannel = lockFile.getChannel();
lock = lockChannel.tryLock();
return lock != null;
} catch (OverlappingFileLockException e) {
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/**
* 释放文件锁
*/
private static void releaseLock() {
try {
if (lock != null && lock.isValid()) {
lock.release();
}
if (lockChannel != null) {
lockChannel.close();
}
if (lockFile != null) {
lockFile.close();
}
// 可选:删除锁文件
new File(LOCK_FILE).delete();
} catch (IOException e) {
e.printStackTrace();
}
}
// 添加JVM关闭钩子确保锁释放
static {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
releaseLock();
}));
}
} }

View File

@@ -36,8 +36,6 @@ public class MainWindow extends JFrame {
private final Map<JComponent, Integer> cardElevations = new HashMap<>(); private final Map<JComponent, Integer> cardElevations = new HashMap<>();
private final Color CARD_COLOR = Color.WHITE; private final Color CARD_COLOR = Color.WHITE;
private final List<ToolCategory> categories = new ArrayList<>(); private final List<ToolCategory> categories = new ArrayList<>();
private final boolean isBackground = true;
private final boolean isBlur = true;
private SystemTray systemTray; private SystemTray systemTray;
//private TrayIcon trayIcon; //private TrayIcon trayIcon;
@@ -62,7 +60,6 @@ public class MainWindow extends JFrame {
categories.add(category); categories.add(category);
} }
public void initUI() { public void initUI() {
setTitle(LanguageManager.getLoadedLanguages().getText("mainWindow.title")); setTitle(LanguageManager.getLoadedLanguages().getText("mainWindow.title"));
setDefaultCloseOperation(EXIT_ON_CLOSE); setDefaultCloseOperation(EXIT_ON_CLOSE);
@@ -78,13 +75,14 @@ public class MainWindow extends JFrame {
mainPanel.setOpaque(true); mainPanel.setOpaque(true);
mainPanel.setLayout(new BorderLayout(20, 20)); mainPanel.setLayout(new BorderLayout(20, 20));
mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30)); mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 30, 30, 30));
mainPanel.add(createHeader(), BorderLayout.NORTH); mainPanel.add(createHeader(), BorderLayout.NORTH);
mainPanel.add(createCategoryTabs(), BorderLayout.CENTER); mainPanel.add(createCategoryTabs(), BorderLayout.CENTER);
//mainPanel.add(createFooter(), BorderLayout.SOUTH); 底部菜单
add(mainPanel); add(mainPanel);
createSystemTray(); //createSystemTray();
addWindowListener(new WindowAdapter() { addWindowListener(new WindowAdapter() {
@Override @Override
public void windowClosing(WindowEvent e) { public void windowClosing(WindowEvent e) {
@@ -93,47 +91,25 @@ public class MainWindow extends JFrame {
}); });
} }
private void createSystemTray() { private JPanel createFooter() {
if (!SystemTray.isSupported()) { JPanel footer = new JPanel();
logger.error("系统托盘不支持!"); footer.setLayout(new BoxLayout(footer, BoxLayout.X_AXIS));
return; footer.setBorder(BorderFactory.createEmptyBorder(15, 30, 15, 30));
}
// 初始化系统托盘 JLabel version = new JLabel("轴创工具箱 v1.0");
systemTray = SystemTray.getSystemTray(); version.setFont(new Font("微软雅黑", Font.PLAIN, 12));
version.setForeground(new Color(120, 120, 120));
// 1. 加载并处理圆角图标(修正图像类型) footer.add(version);
ImageIcon rawIcon = LoadIcon.loadIcon("logo.png", 64); footer.add(Box.createHorizontalGlue());
Image roundedImage = createRoundedIcon(rawIcon.getImage(), 16);
// 2. 创建支持中文的弹出菜单 JLabel status = new JLabel("已加载 " + categories.size() + " 个分类, " +
PopupMenu popup = new PopupMenu(); categories.stream().mapToInt(c -> c.getTools().size()).sum() + " 个工具");
status.setFont(new Font("微软雅黑", Font.PLAIN, 12));
status.setForeground(new Color(120, 120, 120));
footer.add(status);
// 3. 创建菜单项使用标准AWT组件 return footer;
MenuItem openItem = createBaseMenuItem("打开主界面", e -> setVisible(true));
MenuItem decompileItem = createBaseMenuItem("打开反编译工具", e ->
new ModernJarViewer(null).setVisible(true));
MenuItem exitItem = createBaseMenuItem("退出程序", e -> AxisInnovatorsBox.getMain().quit());
// 4. 构建菜单结构
popup.add(openItem);
popup.addSeparator();
popup.add(decompileItem);
popup.addSeparator();
popup.add(exitItem);
// 5. 创建托盘图标
//trayIcon = new TrayIcon(roundedImage, "轴创工具箱", popup);
//trayIcon.setImageAutoSize(true);
// 6. 添加事件监听
//addTrayEventListeners();
//try {
// systemTray.add(trayIcon);
//} catch (AWTException ex) {
// logger.error("添加系统托盘图标失败", ex);
//}
} }
// 基础菜单项创建方法(解决方法不存在问题) // 基础菜单项创建方法(解决方法不存在问题)
@@ -347,6 +323,8 @@ public class MainWindow extends JFrame {
title.setFont(new Font("微软雅黑", Font.BOLD, 28)); title.setFont(new Font("微软雅黑", Font.BOLD, 28));
title.setForeground(new Color(255, 255, 255)); title.setForeground(new Color(255, 255, 255));
JButton settings = new JButton(LoadIcon.loadIcon("settings.png", 32)); JButton settings = new JButton(LoadIcon.loadIcon("settings.png", 32));
settings.putClientProperty(FlatClientProperties.BUTTON_TYPE, FlatClientProperties.BUTTON_TYPE_BORDERLESS); settings.putClientProperty(FlatClientProperties.BUTTON_TYPE, FlatClientProperties.BUTTON_TYPE_BORDERLESS);
settings.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); settings.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));