张世豪
4 天以前 df006129448b6f0653b67caed7c061b84af8e1df
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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;
 
/**
 * 修复 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
    }
 
    /**
     * 早期初始化 - 在静态块中运行
     */
    private static synchronized void ensureLibraryPresentEarly() {
        if (libraryConfigured) {
            return;
        }
        
        System.out.println("开始初始化 jSerialComm 本地库...");
        
        // 第一步:设置系统属性,阻止自动下载
        setCriticalSystemProperties();
        
        // 第二步:尝试从项目资源中提取 DLL
        try {
            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();
            }
        }
    }
 
    /**
     * 设置关键系统属性(在类加载前必须设置)
     */
    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();
        }
    }
}