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 以了解如何创建您自己的要传递的自定义选项。