Java FFM的TUN虚拟网卡封装
This commit is contained in:
@@ -0,0 +1,492 @@
|
||||
package org.kne.cloud.network.tun;
|
||||
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
import org.kne.cloud.network.tun.WindowsInterfaceConverter.GUID;
|
||||
|
||||
/**
|
||||
* Windows DNS 设置管理类
|
||||
* 支持获取、设置和修改网络接口的 DNS 配置
|
||||
*/
|
||||
public class WindowsDNSManager {
|
||||
|
||||
// 内存布局
|
||||
public static final AddressLayout POINTER = ValueLayout.ADDRESS;
|
||||
public static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT;
|
||||
public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG;
|
||||
public static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE;
|
||||
public static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT;
|
||||
|
||||
// DNS 设置常量
|
||||
public static final int DNS_SETTINGS_VERSION1 = 0x0001;
|
||||
public static final int DNS_INTERFACE_SETTINGS_VERSION1 = 0x0001;
|
||||
public static final int DNS_INTERFACE_SETTINGS_VERSION2 = 0x0002;
|
||||
|
||||
// DNS 设置标志
|
||||
public static final long DNS_SETTING_IPV6 = 0x0001L;
|
||||
public static final long DNS_SETTING_NAMESERVER = 0x0002L;
|
||||
public static final long DNS_SETTING_SEARCHLIST = 0x0004L;
|
||||
public static final long DNS_SETTING_REGISTRATION_ENABLED = 0x0008L;
|
||||
public static final long DNS_SETTING_REGISTER_ADAPTER_NAME = 0x0010L;
|
||||
public static final long DNS_SETTING_DOMAIN = 0x0020L;
|
||||
public static final long DNS_SETTING_HOSTNAME = 0x0040L;
|
||||
public static final long DNS_SETTINGS_ENABLE_LLMNR = 0x0080L;
|
||||
public static final long DNS_SETTINGS_QUERY_ADAPTER_NAME = 0x0100L;
|
||||
public static final long DNS_SETTING_PROFILE_NAMESERVER = 0x0200L;
|
||||
public static final long DNS_SETTING_DISABLE_UNCONSTRAINED_QUERIES = 0x0400L;
|
||||
public static final long DNS_SETTING_SUPPLEMENTAL_SEARCH_LIST = 0x0800L;
|
||||
|
||||
// DNS_SETTINGS 结构体布局 (通用 DNS 设置)
|
||||
public static final GroupLayout DNS_SETTINGS_LAYOUT = MemoryLayout.structLayout(
|
||||
DWORD.withName("Version"), // ULONG Version
|
||||
MemoryLayout.paddingLayout(4),
|
||||
DWORD64.withName("Flags"), // ULONG64 Flags
|
||||
POINTER.withName("Hostname"), // PWSTR Hostname
|
||||
POINTER.withName("Domain"), // PWSTR Domain
|
||||
POINTER.withName("SearchList") // PWSTR SearchList
|
||||
);
|
||||
|
||||
// DNS_INTERFACE_SETTINGS 结构体布局 (接口特定 DNS 设置 V1)
|
||||
public static final GroupLayout DNS_INTERFACE_SETTINGS_LAYOUT = MemoryLayout.structLayout(
|
||||
DWORD.withName("Version"), // ULONG Version
|
||||
MemoryLayout.paddingLayout(4),
|
||||
DWORD64.withName("Flags"), // ULONG64 Flags
|
||||
POINTER.withName("Domain"), // PWSTR Domain
|
||||
POINTER.withName("NameServer"), // PWSTR NameServer
|
||||
POINTER.withName("SearchList"), // PWSTR SearchList
|
||||
DWORD.withName("RegistrationEnabled"), // ULONG RegistrationEnabled
|
||||
DWORD.withName("RegisterAdapterName"), // ULONG RegisterAdapterName
|
||||
DWORD.withName("EnableLLMNR"), // ULONG EnableLLMNR
|
||||
DWORD.withName("QueryAdapterName"), // ULONG QueryAdapterName
|
||||
POINTER.withName("ProfileNameServer") // PWSTR ProfileNameServer
|
||||
);
|
||||
|
||||
// DNS_INTERFACE_SETTINGS_EX 结构体布局 (扩展版本)
|
||||
public static final GroupLayout DNS_INTERFACE_SETTINGS_EX_LAYOUT = MemoryLayout.structLayout(
|
||||
DNS_INTERFACE_SETTINGS_LAYOUT.withName("SettingsV1"),
|
||||
DWORD.withName("DisableUnconstrainedQueries"), // ULONG DisableUnconstrainedQueries
|
||||
MemoryLayout.paddingLayout(4),
|
||||
POINTER.withName("SupplementalSearchList") // PWSTR SupplementalSearchList
|
||||
);
|
||||
|
||||
private static final SymbolLookup LOOKUP;
|
||||
private static final Linker LINKER = Linker.nativeLinker();
|
||||
|
||||
// 方法句柄
|
||||
private static final MethodHandle GET_INTERFACE_DNS_SETTINGS;
|
||||
private static final MethodHandle FREE_INTERFACE_DNS_SETTINGS;
|
||||
private static final MethodHandle SET_INTERFACE_DNS_SETTINGS;
|
||||
private static final MethodHandle FREE_DNS_SETTINGS;
|
||||
private static final MethodHandle GET_DNS_SETTINGS;
|
||||
|
||||
static {
|
||||
try {
|
||||
// 加载 dnsapi.dll
|
||||
System.loadLibrary("dnsapi");
|
||||
LOOKUP = SymbolLookup.loaderLookup();
|
||||
|
||||
// GetInterfaceDnsSettings 函数
|
||||
// DWORD GetInterfaceDnsSettings(GUID *Interface, DNS_INTERFACE_SETTINGS *Settings);
|
||||
FunctionDescriptor getInterfaceDnsSettingsDesc = FunctionDescriptor.of(
|
||||
DWORD, // 返回值: DWORD
|
||||
POINTER, // Interface: GUID*
|
||||
POINTER // Settings: DNS_INTERFACE_SETTINGS*
|
||||
);
|
||||
|
||||
// SetInterfaceDnsSettings 函数
|
||||
// DWORD SetInterfaceDnsSettings(GUID *Interface, DNS_INTERFACE_SETTINGS *Settings);
|
||||
FunctionDescriptor setInterfaceDnsSettingsDesc = FunctionDescriptor.of(
|
||||
DWORD, // 返回值: DWORD
|
||||
POINTER, // Interface: GUID*
|
||||
POINTER // Settings: DNS_INTERFACE_SETTINGS*
|
||||
);
|
||||
|
||||
FunctionDescriptor freeInterfaceDnsSettingsDesc = FunctionDescriptor.ofVoid(
|
||||
POINTER
|
||||
);
|
||||
|
||||
// FreeDnsSettings 函数
|
||||
// VOID FreeDnsSettings(DNS_SETTINGS *Settings);
|
||||
FunctionDescriptor freeDnsSettingsDesc = FunctionDescriptor.ofVoid(
|
||||
POINTER // Settings: DNS_SETTINGS*
|
||||
);
|
||||
|
||||
// GetDnsSettings 函数
|
||||
// DWORD GetDnsSettings(DNS_SETTINGS *Settings);
|
||||
FunctionDescriptor getDnsSettingsDesc = FunctionDescriptor.of(
|
||||
DWORD, // 返回值: DWORD
|
||||
POINTER // Settings: DNS_SETTINGS*
|
||||
);
|
||||
|
||||
// 获取方法句柄
|
||||
GET_INTERFACE_DNS_SETTINGS = LINKER.downcallHandle(
|
||||
LOOKUP.find("GetInterfaceDnsSettings").orElseThrow(),
|
||||
getInterfaceDnsSettingsDesc
|
||||
);
|
||||
|
||||
SET_INTERFACE_DNS_SETTINGS = LINKER.downcallHandle(
|
||||
LOOKUP.find("SetInterfaceDnsSettings").orElseThrow(),
|
||||
setInterfaceDnsSettingsDesc
|
||||
);
|
||||
|
||||
FREE_DNS_SETTINGS = LINKER.downcallHandle(
|
||||
LOOKUP.find("FreeDnsSettings").orElseThrow(),
|
||||
freeInterfaceDnsSettingsDesc
|
||||
);
|
||||
|
||||
FREE_INTERFACE_DNS_SETTINGS = LINKER.downcallHandle(
|
||||
LOOKUP.find("FreeInterfaceDnsSettings").orElseThrow(),
|
||||
freeDnsSettingsDesc
|
||||
);
|
||||
|
||||
GET_DNS_SETTINGS = LINKER.downcallHandle(
|
||||
LOOKUP.find("GetDnsSettings").orElseThrow(),
|
||||
getDnsSettingsDesc
|
||||
);
|
||||
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Failed to initialize Windows DNS manager", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DNS 设置信息类 (Java 对象版本)
|
||||
*/
|
||||
public static class DnsInterfaceSettings {
|
||||
private int version;
|
||||
private long flags;
|
||||
private String domain;
|
||||
private List<String> nameServers;
|
||||
private List<String> searchList;
|
||||
private boolean registrationEnabled;
|
||||
private boolean registerAdapterName;
|
||||
private boolean enableLLMNR;
|
||||
private boolean queryAdapterName;
|
||||
private String profileNameServer;
|
||||
private boolean disableUnconstrainedQueries;
|
||||
private List<String> supplementalSearchList;
|
||||
|
||||
public DnsInterfaceSettings() {
|
||||
this.version = DNS_INTERFACE_SETTINGS_VERSION1;
|
||||
this.flags = 0;
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public int getVersion() { return version; }
|
||||
public void setVersion(int version) { this.version = version; }
|
||||
|
||||
public long getFlags() { return flags; }
|
||||
public void setFlags(long flags) { this.flags = flags; }
|
||||
|
||||
public String getDomain() { return domain; }
|
||||
public void setDomain(String domain) { this.domain = domain; }
|
||||
|
||||
public List<String> getNameServers() { return nameServers; }
|
||||
public void setNameServers(List<String> nameServers) { this.nameServers = nameServers; }
|
||||
|
||||
public List<String> getSearchList() { return searchList; }
|
||||
public void setSearchList(List<String> searchList) { this.searchList = searchList; }
|
||||
|
||||
public boolean isRegistrationEnabled() { return registrationEnabled; }
|
||||
public void setRegistrationEnabled(boolean registrationEnabled) {
|
||||
this.registrationEnabled = registrationEnabled;
|
||||
}
|
||||
|
||||
public boolean isRegisterAdapterName() { return registerAdapterName; }
|
||||
public void setRegisterAdapterName(boolean registerAdapterName) {
|
||||
this.registerAdapterName = registerAdapterName;
|
||||
}
|
||||
|
||||
public boolean isEnableLLMNR() { return enableLLMNR; }
|
||||
public void setEnableLLMNR(boolean enableLLMNR) { this.enableLLMNR = enableLLMNR; }
|
||||
|
||||
public boolean isQueryAdapterName() { return queryAdapterName; }
|
||||
public void setQueryAdapterName(boolean queryAdapterName) {
|
||||
this.queryAdapterName = queryAdapterName;
|
||||
}
|
||||
|
||||
public String getProfileNameServer() { return profileNameServer; }
|
||||
public void setProfileNameServer(String profileNameServer) {
|
||||
this.profileNameServer = profileNameServer;
|
||||
}
|
||||
|
||||
public boolean isDisableUnconstrainedQueries() { return disableUnconstrainedQueries; }
|
||||
public void setDisableUnconstrainedQueries(boolean disableUnconstrainedQueries) {
|
||||
this.disableUnconstrainedQueries = disableUnconstrainedQueries;
|
||||
}
|
||||
|
||||
public List<String> getSupplementalSearchList() { return supplementalSearchList; }
|
||||
public void setSupplementalSearchList(List<String> supplementalSearchList) {
|
||||
this.supplementalSearchList = supplementalSearchList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("DNS Interface Settings:\n");
|
||||
sb.append(" Version: ").append(version).append("\n");
|
||||
sb.append(" Flags: 0x").append(Long.toHexString(flags)).append("\n");
|
||||
sb.append(" Domain: ").append(domain != null ? domain : "(null)").append("\n");
|
||||
sb.append(" Name Servers: ").append(nameServers).append("\n");
|
||||
sb.append(" Search List: ").append(searchList).append("\n");
|
||||
sb.append(" Registration Enabled: ").append(registrationEnabled).append("\n");
|
||||
sb.append(" Register Adapter Name: ").append(registerAdapterName).append("\n");
|
||||
sb.append(" Enable LLMNR: ").append(enableLLMNR).append("\n");
|
||||
sb.append(" Query Adapter Name: ").append(queryAdapterName).append("\n");
|
||||
sb.append(" Profile Name Server: ").append(profileNameServer).append("\n");
|
||||
sb.append(" Disable Unconstrained Queries: ").append(disableUnconstrainedQueries).append("\n");
|
||||
sb.append(" Supplemental Search List: ").append(supplementalSearchList);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从网卡获取 DNS 设置
|
||||
* @param interfaceGuid 网卡的 GUID
|
||||
* @return DNS 设置对象
|
||||
*/
|
||||
public static DnsInterfaceSettings getInterfaceDnsSettings(GUID interfaceGuid,DnsInterfaceSettings oldSettings) {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
// 创建 GUID 内存段
|
||||
MemorySegment guidSegment = interfaceGuid.toMemorySegment( arena);
|
||||
|
||||
// 分配 DNS_INTERFACE_SETTINGS 结构体 (版本1)
|
||||
MemorySegment dnsSettings = createDnsInterfaceSettingsMemory(oldSettings, arena);
|
||||
|
||||
// 设置版本
|
||||
dnsSettings.set(DWORD, 0, DNS_INTERFACE_SETTINGS_VERSION1);
|
||||
|
||||
// 调用 GetInterfaceDnsSettings
|
||||
int result = (int) GET_INTERFACE_DNS_SETTINGS.invokeExact(
|
||||
guidSegment,
|
||||
dnsSettings
|
||||
);
|
||||
|
||||
if (result != 0) { // NO_ERROR = 0
|
||||
throw new WindowsAPIException("GetInterfaceDnsSettings failed", result);
|
||||
}
|
||||
DnsInterfaceSettings resultv=parseDnsInterfaceSettings(dnsSettings, arena);
|
||||
FREE_INTERFACE_DNS_SETTINGS.invokeExact(dnsSettings);
|
||||
// 解析结构体为 Java 对象
|
||||
return resultv;
|
||||
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Error getting interface DNS settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用 DNS 设置到网卡
|
||||
* @param interfaceGuid 网卡的 GUID
|
||||
* @param settings DNS 设置对象
|
||||
* @return 操作结果 (0 = 成功)
|
||||
*/
|
||||
public static void setInterfaceDnsSettings(GUID interfaceGuid, DnsInterfaceSettings settings) {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
// 创建 GUID 内存段
|
||||
MemorySegment guidSegment = interfaceGuid.toMemorySegment( arena);
|
||||
|
||||
// 创建 DNS_INTERFACE_SETTINGS 结构体
|
||||
MemorySegment dnsSettings = createDnsInterfaceSettingsMemory(settings, arena);
|
||||
|
||||
// 调用 SetInterfaceDnsSettings
|
||||
int result= (int) SET_INTERFACE_DNS_SETTINGS.invokeExact(
|
||||
guidSegment,
|
||||
dnsSettings
|
||||
);
|
||||
|
||||
if (result != 0) { // NO_ERROR = 0
|
||||
throw new WindowsAPIException("SetInterfaceDnsSettings failed", result);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Error setting interface DNS settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 DNS_INTERFACE_SETTINGS 结构体为 Java 对象
|
||||
*/
|
||||
private static DnsInterfaceSettings parseDnsInterfaceSettings(MemorySegment dnsSettings, Arena arena) {
|
||||
DnsInterfaceSettings settings = new DnsInterfaceSettings();
|
||||
|
||||
// 读取基本字段
|
||||
int version = dnsSettings.get(DWORD, 0);
|
||||
settings.setVersion(version);
|
||||
|
||||
long flags = dnsSettings.get(DWORD64, 8);
|
||||
settings.setFlags(flags);
|
||||
|
||||
// 读取字符串字段
|
||||
MemorySegment domainPtr = dnsSettings.get(POINTER, 16);
|
||||
if (!domainPtr.equals(MemorySegment.NULL)) {
|
||||
String domain = readWideString(domainPtr, arena);
|
||||
settings.setDomain(domain);
|
||||
}else {
|
||||
settings.setDomain(null);
|
||||
}
|
||||
|
||||
// 读取 NameServer (可能是多个服务器,用空格分隔)
|
||||
MemorySegment nameServerPtr = dnsSettings.get(POINTER, 24);
|
||||
if (!nameServerPtr.equals(MemorySegment.NULL)) {
|
||||
String nameServerStr = readWideString(nameServerPtr, arena);
|
||||
if (nameServerStr != null && !nameServerStr.isEmpty()) {
|
||||
String[] servers = nameServerStr.split("\\s+");
|
||||
settings.setNameServers(Arrays.asList(servers));
|
||||
}
|
||||
}else {
|
||||
settings.setNameServers(null);
|
||||
}
|
||||
|
||||
// 读取 SearchList (可能是多个域名,用空格分隔)
|
||||
MemorySegment searchListPtr = dnsSettings.get(POINTER, 32);
|
||||
if (!searchListPtr.equals(MemorySegment.NULL)) {
|
||||
String searchListStr = readWideString(searchListPtr, arena);
|
||||
if (searchListStr != null && !searchListStr.isEmpty()) {
|
||||
String[] domains = searchListStr.split("\\s+");
|
||||
settings.setSearchList(Arrays.asList(domains));
|
||||
}
|
||||
}else {
|
||||
settings.setSearchList(null);
|
||||
}
|
||||
|
||||
// 读取布尔值字段
|
||||
int registrationEnabled = dnsSettings.get(DWORD, 40);
|
||||
settings.setRegistrationEnabled(registrationEnabled != 0);
|
||||
|
||||
int registerAdapterName = dnsSettings.get(DWORD, 44);
|
||||
settings.setRegisterAdapterName(registerAdapterName != 0);
|
||||
|
||||
int enableLLMNR = dnsSettings.get(DWORD, 48);
|
||||
settings.setEnableLLMNR(enableLLMNR != 0);
|
||||
|
||||
int queryAdapterName = dnsSettings.get(DWORD, 52);
|
||||
settings.setQueryAdapterName(queryAdapterName != 0);
|
||||
|
||||
// 读取 ProfileNameServer
|
||||
MemorySegment profileNameServerPtr = dnsSettings.get(POINTER, 56);
|
||||
if (!profileNameServerPtr.equals(MemorySegment.NULL)) {
|
||||
String profileNameServer = readWideString(profileNameServerPtr, arena);
|
||||
settings.setProfileNameServer(profileNameServer);
|
||||
}else {
|
||||
settings.setProfileNameServer(null);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 DNS_INTERFACE_SETTINGS 结构体的内存表示
|
||||
*/
|
||||
private static MemorySegment createDnsInterfaceSettingsMemory(DnsInterfaceSettings settings, Arena arena) {
|
||||
// 分配结构体内存
|
||||
MemorySegment dnsSettings = arena.allocate(DNS_INTERFACE_SETTINGS_LAYOUT);
|
||||
|
||||
// 设置版本和标志
|
||||
dnsSettings.set(DWORD, 0, settings.getVersion());
|
||||
dnsSettings.set(DWORD64, 8, settings.getFlags());
|
||||
|
||||
// 设置 Domain
|
||||
if (settings.getDomain() != null) {
|
||||
MemorySegment domainStr = toWideString(settings.getDomain(), arena);
|
||||
dnsSettings.set(POINTER, 16, domainStr);
|
||||
} else {
|
||||
dnsSettings.set(POINTER, 16, MemorySegment.NULL);
|
||||
}
|
||||
|
||||
// 设置 NameServer (多个服务器用空格分隔)
|
||||
if (settings.getNameServers()!=null) {
|
||||
String nameServerStr = String.join(" ", settings.getNameServers());
|
||||
MemorySegment nameServerSegment = toWideString(nameServerStr, arena);
|
||||
dnsSettings.set(POINTER, 24, nameServerSegment);
|
||||
} else {
|
||||
dnsSettings.set(POINTER, 24, MemorySegment.NULL);
|
||||
}
|
||||
|
||||
// 设置 SearchList (多个域名用空格分隔)
|
||||
if (settings.getSearchList()!=null) {
|
||||
String searchListStr = String.join(" ", settings.getSearchList());
|
||||
MemorySegment searchListSegment = toWideString(searchListStr, arena);
|
||||
dnsSettings.set(POINTER, 32, searchListSegment);
|
||||
} else {
|
||||
dnsSettings.set(POINTER, 32, MemorySegment.NULL);
|
||||
}
|
||||
|
||||
// 设置布尔值字段
|
||||
dnsSettings.set(DWORD, 40, settings.isRegistrationEnabled() ? 1 : 0);
|
||||
dnsSettings.set(DWORD, 44, settings.isRegisterAdapterName() ? 1 : 0);
|
||||
dnsSettings.set(DWORD, 48, settings.isEnableLLMNR() ? 1 : 0);
|
||||
dnsSettings.set(DWORD, 52, settings.isQueryAdapterName() ? 1 : 0);
|
||||
|
||||
// 设置 ProfileNameServer
|
||||
if (settings.getProfileNameServer() != null ) {
|
||||
MemorySegment profileNameServerSegment = toWideString(settings.getProfileNameServer(), arena);
|
||||
dnsSettings.set(POINTER, 56, profileNameServerSegment);
|
||||
} else {
|
||||
dnsSettings.set(POINTER, 56, MemorySegment.NULL);
|
||||
}
|
||||
|
||||
return dnsSettings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将 Java String 转换为 Windows 宽字符串 (UTF-16 LE)
|
||||
*/
|
||||
private static MemorySegment toWideString(String str, Arena arena) {
|
||||
if (str == null ) {
|
||||
return MemorySegment.NULL;
|
||||
}
|
||||
|
||||
byte[] utf16Bytes = str.getBytes(java.nio.charset.StandardCharsets.UTF_16LE);
|
||||
MemorySegment segment = arena.allocate(utf16Bytes.length + 2);
|
||||
segment.copyFrom(MemorySegment.ofArray(utf16Bytes));
|
||||
|
||||
// 添加 null 终止符
|
||||
segment.set(ValueLayout.JAVA_BYTE, utf16Bytes.length, (byte) 0);
|
||||
segment.set(ValueLayout.JAVA_BYTE, utf16Bytes.length + 1, (byte) 0);
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 Windows 宽字符串
|
||||
*/
|
||||
private static String readWideString(MemorySegment strPtr, Arena arena) {
|
||||
if (strPtr.equals(MemorySegment.NULL)) {
|
||||
return null;
|
||||
}
|
||||
strPtr= strPtr.reinterpret(0xffffffffL);
|
||||
//return strPtr.getString(0,Charset.forName("UTF-16"));
|
||||
try {
|
||||
// 计算字符串长度 (找到 null 终止符)
|
||||
long length = 0;
|
||||
while (true) {
|
||||
short ch = strPtr.get(ValueLayout.JAVA_SHORT, length * 2);
|
||||
if (ch == 0) {
|
||||
break;
|
||||
}
|
||||
length++;
|
||||
}
|
||||
|
||||
// 读取字符串数据
|
||||
byte[] bytes = new byte[(int) (length * 2)];
|
||||
MemorySegment.copy(strPtr, 0, MemorySegment.ofArray(bytes), 0, bytes.length);
|
||||
|
||||
return new String(bytes, java.nio.charset.StandardCharsets.UTF_16LE);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user