张世豪
16 小时以前 b272034a1fdbfe32b355fc6c264a4c45df107190
src/chuankou/SerialPortNativeLoader.java
@@ -1,77 +1,190 @@
package chuankou;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
/**
 * Ensures the correct jSerialComm native library is available on Windows/x86_64 systems.
 * 修复 jSerialComm 库加载问题
 */
public final class SerialPortNativeLoader {
    private static final String LIB_PROPERTY = "com.fazecast.jSerialComm.library.path";
    private static final String EXPECTED_DLL = "jSerialComm.dll";
    private static volatile boolean libraryConfigured = false;
    // 静态块:确保在任何 SerialPort 类加载之前运行
    static {
        ensureLibraryPresentEarly();
    }
    private SerialPortNativeLoader() {
        // utility class
    }
    public static void ensureLibraryPresent() {
        if (System.getProperty(LIB_PROPERTY) != null) {
    /**
     * 早期初始化 - 在静态块中运行
     */
    private static synchronized void ensureLibraryPresentEarly() {
        if (libraryConfigured) {
            return;
        }
        String osName = System.getProperty("os.name", "").toLowerCase();
        if (!osName.contains("win")) {
            return;
        }
        String arch = System.getProperty("os.arch", "").toLowerCase();
        if (!arch.contains("64")) {
            return;
        }
        Path candidateDir = Paths.get("lib", "native", "windows", "x86_64").toAbsolutePath();
        File dllFile = candidateDir.resolve(EXPECTED_DLL).toFile();
        if (!dllFile.isFile()) {
            candidateDir = resolveFromCodeSource();
            if (candidateDir != null) {
                dllFile = candidateDir.resolve(EXPECTED_DLL).toFile();
            }
        }
        if (dllFile.isFile()) {
            System.setProperty(LIB_PROPERTY, candidateDir.toString());
        } else {
            System.err.println("Expected jSerialComm native library not found. Checked " + dllFile);
        }
    }
    private static Path resolveFromCodeSource() {
        System.out.println("开始初始化 jSerialComm 本地库...");
        // 第一步:设置系统属性,阻止自动下载
        setCriticalSystemProperties();
        // 第二步:尝试从项目资源中提取 DLL
        try {
            java.security.CodeSource codeSource = SerialPortNativeLoader.class.getProtectionDomain().getCodeSource();
            if (codeSource == null) {
                return null;
            extractNativeLibraryFromResources();
            libraryConfigured = true;
            System.out.println("jSerialComm 本地库初始化成功");
        } catch (Exception e) {
            System.err.println("从资源提取 DLL 失败: " + e.getMessage());
            // 尝试备用方案
            try {
                setupLibraryFromFileSystem();
                libraryConfigured = true;
                System.out.println("从文件系统加载 jSerialComm 成功");
            } catch (Exception ex) {
                System.err.println("所有加载方式都失败: " + ex.getMessage());
                // 最后尝试:允许 jSerialComm 使用默认方式
                allowDefaultBehavior();
            }
            Path location = Paths.get(codeSource.getLocation().toURI()).toAbsolutePath();
            Path baseDir = location.getParent();
            if (baseDir == null) {
                return null;
            }
            Path siblingLibDir = baseDir.resolveSibling("lib").resolve("native").resolve("windows").resolve("x86_64");
            if (siblingLibDir.toFile().isDirectory()) {
                return siblingLibDir;
            }
            Path relativeLibDir = baseDir.resolve("lib").resolve("native").resolve("windows").resolve("x86_64");
            if (relativeLibDir.toFile().isDirectory()) {
                return relativeLibDir;
            }
        } catch (URISyntaxException | SecurityException ignored) {
            // ignore
        }
        return null;
    }
}
    /**
     * 设置关键系统属性(在类加载前必须设置)
     */
    private static void setCriticalSystemProperties() {
        // 阻止自动下载和端口代理
        System.setProperty("com.fazecast.jSerialComm.port.agent", "disabled");
        System.setProperty("com.fazecast.jSerialComm.autoload", "false");
        System.setProperty("com.fazecast.jSerialComm.autoDispose", "false");
        // 禁用日志和调试
        System.setProperty("com.fazecast.jSerialComm.debug", "false");
        System.setProperty("com.fazecast.jSerialComm.log", "false");
        System.out.println("已设置 jSerialComm 系统属性");
    }
    /**
     * 从 JAR 资源中提取 DLL
     */
    private static void extractNativeLibraryFromResources() throws Exception {
        // 确定目标路径
        String tempDir = System.getProperty("java.io.tmpdir");
        Path targetDir = Paths.get(tempDir, "jSerialComm", "2.10.4");
        // 如果目录不存在,创建它
        if (!Files.exists(targetDir)) {
            Files.createDirectories(targetDir);
        }
        Path dllPath = targetDir.resolve(EXPECTED_DLL);
        // 检查 DLL 是否已存在且可用
        if (Files.exists(dllPath)) {
            System.out.println("DLL 已存在于: " + dllPath);
            // 设置库路径
            System.setProperty(LIB_PROPERTY, targetDir.toString());
            return;
        }
        // 从类路径资源加载 DLL
        String resourcePath = "/native/windows/x86_64/" + EXPECTED_DLL;
        InputStream dllStream = SerialPortNativeLoader.class.getResourceAsStream(resourcePath);
        if (dllStream == null) {
            // 尝试其他可能的路径
            resourcePath = "/lib/native/windows/x86_64/" + EXPECTED_DLL;
            dllStream = SerialPortNativeLoader.class.getResourceAsStream(resourcePath);
        }
        if (dllStream == null) {
            // 尝试从文件系统加载
            resourcePath = "lib/native/windows/x86_64/" + EXPECTED_DLL;
            File file = new File(resourcePath);
            if (file.exists()) {
                dllStream = Files.newInputStream(file.toPath());
            }
        }
        if (dllStream == null) {
            throw new RuntimeException("无法在资源中找到 " + EXPECTED_DLL);
        }
        // 复制 DLL 到临时目录
        try (InputStream is = dllStream;
             FileOutputStream fos = new FileOutputStream(dllPath.toFile())) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        }
        System.out.println("DLL 已提取到: " + dllPath);
        // 设置库路径
        System.setProperty(LIB_PROPERTY, targetDir.toString());
        System.setProperty("java.library.path",
            System.getProperty("java.library.path") + File.pathSeparator + targetDir.toString());
    }
    /**
     * 从文件系统设置库
     */
    private static void setupLibraryFromFileSystem() throws Exception {
        // 检查多个可能的路径
        String[] possiblePaths = {
            "lib/native/windows/x86_64/" + EXPECTED_DLL,
            "./lib/native/windows/x86_64/" + EXPECTED_DLL,
            System.getProperty("user.dir") + "/lib/native/windows/x86_64/" + EXPECTED_DLL,
            "../lib/native/windows/x86_64/" + EXPECTED_DLL
        };
        for (String path : possiblePaths) {
            File dllFile = new File(path);
            if (dllFile.exists()) {
                System.out.println("找到 DLL: " + dllFile.getAbsolutePath());
                // 使用 DLL 所在目录
                File parentDir = dllFile.getParentFile();
                System.setProperty(LIB_PROPERTY, parentDir.getAbsolutePath());
                System.setProperty("java.library.path",
                    System.getProperty("java.library.path") + File.pathSeparator + parentDir.getAbsolutePath());
                return;
            }
        }
        throw new RuntimeException("在文件系统中找不到 " + EXPECTED_DLL);
    }
    /**
     * 允许默认行为(最后的手段)
     */
    private static void allowDefaultBehavior() {
        System.err.println("警告:使用 jSerialComm 的默认加载行为");
        // 清空阻止属性,让 jSerialComm 自己处理
        System.clearProperty("com.fazecast.jSerialComm.autoload");
        System.clearProperty("com.fazecast.jSerialComm.port.agent");
    }
    /**
     * 公共方法 - 供其他代码调用
     */
    public static synchronized void ensureLibraryPresent() {
        // 静态块已经执行,这里只是确保
        if (!libraryConfigured) {
            ensureLibraryPresentEarly();
        }
    }
}