JNA 中的无效内存访问
Invalid memory access in JNA
使用 JNA 时,我在调用 QLConnect
方法时收到无效内存访问错误。
这是我映射 DLL 的接口:
import com.sun.jna.Native;
import com.sun.jna.win32.StdCallLibrary;
public interface QuikLimit extends StdCallLibrary {
QuikLimit INSTANCE = (QuikLimit) Native.load(new File("").getAbsolutePath() + "\QuikLimit.dll", QuikLimit.class);
int QLConnect(String userName, String userPassword, byte[] desc);
//more methods from dll
}
我有另一个 class 常量:
public class Const {
public static final int DESC_SIZE = 1024;
public enum Status {
//enum values
int code;
private Status(int code) {
this.code = code;
}
public int getCode() {
return code;
}
};
}
这里是代码中出现的地方:
@Component
public class ConnectionManager {
@Value("${quik.login}")
private String login;
@Value("${quik.password}")
private String password;
private Logger logger = LoggerFactory.getLogger(this.getClass());
private boolean connected;
public synchronized BaseResponse connect() {
BaseResponse result = new BaseResponse();
byte[] desc = new byte[Const.DESC_SIZE];
if (connected) {
//logic if connected
logger.info("call QLConnect");
int retVal = QuikLimit.INSTANCE.QLConnect(login, password, desc);
if (retVal == Const.Status.QL_ACTIVE.getCode()) {
//logic from response
} else if (retVal == Const.Status.QL_CONNECTEDNOTACTIVE.getCode()) {
//logic from response
}
result.setCode(retVal);
result.setDescription(Util.descToStr(desc));
QuikLimit.INSTANCE.QLDisconnect();
return result;
}
}
我知道 1 假设立即落在 dll 中的类型不匹配这一事实上。但是头文件里明明写着return类型是int:
typedef __int32 ql_long;
ql_long _stdcall QLConnect(const char* lpszUserName, const char* lpszUserPassword, char* lpszError);
lpszUserName 是指向包含用户名的 ASCIIZ 字符串的指针。
pszUserPassword 是指向包含用户密码的 ASCIIZ 字符串的指针
密码。
lpszDesc 是指向缓冲区的指针,如果发生错误,
符合其描述。最小缓冲区大小为 512 字节。
还有什么可能导致错误?
当您收到无效内存访问错误时,首先要检查的是您的类型映射,您正确地从查看 int
return 值开始。事实证明,其他参数的 String
映射在 *nix 系统上运行良好,但在 Windows.
上需要一些特殊处理
默认Windows使用UTF-16编码,JNA的默认类型映射under-the-hood尝试将JavaString
对象转换为UTF-16编码的原生字符串(wchar*
).这适用于 Windows API DLL。
但是,您的自定义 DLL 指定用户名和密码参数应采用 ASCIIZ 格式(null-terminated C 字符串)。
您可以强制 JNA 使用 ASCII String
类型映射器,方法是在加载 DLL 时将 W32APIOptions.ASCII_OPTIONS
作为第三个参数,例如
QuikLimit INSTANCE = (QuikLimit) Native.load(new File("").getAbsolutePath() + "\QuikLimit.dll", QuikLimit.class, W32APIOptions.ASCII_OPTIONS);
还有其他更细粒度的方法只处理字符串类型映射,不尝试也进行函数映射(如果任何映射函数以 W 或 A 结尾,这可能会中断!)。您可以深入研究 W32APIOptions
class 以了解如何创建您自己的要传递的自定义选项。
使用 JNA 时,我在调用 QLConnect
方法时收到无效内存访问错误。
这是我映射 DLL 的接口:
import com.sun.jna.Native;
import com.sun.jna.win32.StdCallLibrary;
public interface QuikLimit extends StdCallLibrary {
QuikLimit INSTANCE = (QuikLimit) Native.load(new File("").getAbsolutePath() + "\QuikLimit.dll", QuikLimit.class);
int QLConnect(String userName, String userPassword, byte[] desc);
//more methods from dll
}
我有另一个 class 常量:
public class Const {
public static final int DESC_SIZE = 1024;
public enum Status {
//enum values
int code;
private Status(int code) {
this.code = code;
}
public int getCode() {
return code;
}
};
}
这里是代码中出现的地方:
@Component
public class ConnectionManager {
@Value("${quik.login}")
private String login;
@Value("${quik.password}")
private String password;
private Logger logger = LoggerFactory.getLogger(this.getClass());
private boolean connected;
public synchronized BaseResponse connect() {
BaseResponse result = new BaseResponse();
byte[] desc = new byte[Const.DESC_SIZE];
if (connected) {
//logic if connected
logger.info("call QLConnect");
int retVal = QuikLimit.INSTANCE.QLConnect(login, password, desc);
if (retVal == Const.Status.QL_ACTIVE.getCode()) {
//logic from response
} else if (retVal == Const.Status.QL_CONNECTEDNOTACTIVE.getCode()) {
//logic from response
}
result.setCode(retVal);
result.setDescription(Util.descToStr(desc));
QuikLimit.INSTANCE.QLDisconnect();
return result;
}
}
我知道 1 假设立即落在 dll 中的类型不匹配这一事实上。但是头文件里明明写着return类型是int:
typedef __int32 ql_long;
ql_long _stdcall QLConnect(const char* lpszUserName, const char* lpszUserPassword, char* lpszError);
lpszUserName 是指向包含用户名的 ASCIIZ 字符串的指针。
pszUserPassword 是指向包含用户密码的 ASCIIZ 字符串的指针 密码。
lpszDesc 是指向缓冲区的指针,如果发生错误, 符合其描述。最小缓冲区大小为 512 字节。
还有什么可能导致错误?
当您收到无效内存访问错误时,首先要检查的是您的类型映射,您正确地从查看 int
return 值开始。事实证明,其他参数的 String
映射在 *nix 系统上运行良好,但在 Windows.
默认Windows使用UTF-16编码,JNA的默认类型映射under-the-hood尝试将JavaString
对象转换为UTF-16编码的原生字符串(wchar*
).这适用于 Windows API DLL。
但是,您的自定义 DLL 指定用户名和密码参数应采用 ASCIIZ 格式(null-terminated C 字符串)。
您可以强制 JNA 使用 ASCII String
类型映射器,方法是在加载 DLL 时将 W32APIOptions.ASCII_OPTIONS
作为第三个参数,例如
QuikLimit INSTANCE = (QuikLimit) Native.load(new File("").getAbsolutePath() + "\QuikLimit.dll", QuikLimit.class, W32APIOptions.ASCII_OPTIONS);
还有其他更细粒度的方法只处理字符串类型映射,不尝试也进行函数映射(如果任何映射函数以 W 或 A 结尾,这可能会中断!)。您可以深入研究 W32APIOptions
class 以了解如何创建您自己的要传递的自定义选项。