虚拟化项目,须要用到Java
调用原生代码的技术,咱们使用的是开源库JNA
(Java Native Access
)。java
Native
(C/C++
)代码,编译生成动态连接库Dynamic-link library
。数组
在Windows
下常见的.dll
文件。这是咱们项目中用到的动态连接库。数据结构
而在unix
环境下,为.so
文件。这是百度地图的动态连接库。架构
与动态连接库配套的,会有相应的头文件,来声明动态连接库中对外暴露的方法。app
百度地图是直接封装好,给了.so
,可是不给头文件,直接把写好的jar
包给你,直接调用就行。ide
以前也是用过百度地图的SDK
,如今本身手写代码调用动态连接库才明白,原来以前用的都是别人封装好的,现在本身参照头文件手写,感受理解仍是深入了很多。学习
咱们使用JNA
,主要是去调用动态连接库中已经实现的方法,因此要解决的问题就是:如何在Java
代码中调用动态连接库的方法?ui
打开头文件,这个方法要求传输的数据是指针,而Java
是没有指针的,另外一个问题:Java
数据类型与C/C++
的数据类型如何映射?spa
打开JNA
的官方README
,点击Getting Started
。设计
直接看代码,里面的sample
,入门足够了。
Library
。// This is the standard, stable way of mapping, which supports extensive // customization and mapping of Java to native types. public interface CLibrary extends Library { CLibrary INSTANCE = (CLibrary) Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class); void printf(String format, Object... args); }
这是官方README
中给的类型映射。
学习与实践的区别,看着这个表格感受挺简单的,实际用起来真难。
结构体PSA_HOST
:
typedef struct { char name[33]; DWORD context; } PSA_HOST;
映射类HostStruct
:
HostStruct
,继承Structure
,表示这个一个结构体。name
与context
,而且设置访问属性为public
。 getFieldOrder
方法,表示本类中各字段以何顺序映射原生结构体。/** * @author zhangxishuo on 2019-02-16 * 结构体 PSA_HOST */ public class HostStruct extends Structure { public byte[] name = new byte[33]; public int context; @Override protected List<String> getFieldOrder() { return Arrays.asList("name", "context"); } }
注意
const char *
才能映射为String
类型。
而char *
只能映射为byte
数组,而后使用Native.toString()
方法将byte
数组转换为String
。
typedef PSA_STATUS (*LPFN_PSA_ShutdownHost)( PSA_LOGON_HANDLE *handle, IN PSA_HOST *psa_host );
参数中须要PSA_HOST
结构体的指针。
参考了好多篇文章,最经常使用的就是下面这种写法。写静态内部类,内部类实现ByReference
与ByValue
接口,分别表示映射指针,与映射值。
/** * @author zhangxishuo on 2019-02-16 * 结构体 PSA_HOST */ public class HostStruct extends Structure { /** * 结构体指针 */ public static class ByReference extends HostStruct implements Structure.ByReference { } /** * 结构体具体的值 */ public static class ByValue extends HostStruct implements Structure.ByValue { } public byte[] name = new byte[33]; public int context; @Override protected List<String> getFieldOrder() { return Arrays.asList("name", "context"); } }
映射
/** * 关闭计算机 * @param pointerByReference 认证pointer * @param host 主机指针 * @return 参见枚举类PsaStatus */ NativeLong _PSA_ShutdownHost(PointerByReference pointerByReference, HostStruct.ByReference host);
打起精神,重点来了!
开发过程当中,有这样一种复杂的数据结构,嵌套关系比较复杂。
typedef struct { union { DWORD flags; struct { DWORD rev:23; DWORD copy_status:3; DWORD timeout:1; DWORD disconnect:1; DWORD rev1:1; DWORD os_logoned:1; DWORD logoned:1; DWORD online:1; }; }; } PSA_HOST_STATUS_FLAGS;
知识不用就忘,谁还记得C
语言里的联合是啥?
struct { DWORD rev:23; DWORD copy_status:3; DWORD timeout:1; DWORD disconnect:1; DWORD rev1:1; DWORD os_logoned:1; DWORD logoned:1; DWORD online:1; };
DWORD
就是int
,先映射里面的结构体。
把这些属性一写,而后再重写getFieldOrder
方法。
/** * @author zhangxishuo on 2019-02-26 * 计算机状态结构体 */ public class HostStatusStruct extends Structure { /** * 结构体指针 */ public static class ByReference extends HostStatusStruct implements Structure.ByReference { } /** * 结构体具体的值 */ public static class ByValue extends HostStatusStruct implements Structure.ByValue { } public int rev; public int copy_status; public int timeout; public int disconnect; public int rev1; public int os_logoned; public int logoned; public int online; @Override protected List<String> getFieldOrder() { return Arrays.asList("rev", "copy_status", "timeout", "disconnect", "rev1", "os_logoned", "logoned", "online"); } }
union { DWORD flags; struct { DWORD rev:23; DWORD copy_status:3; DWORD timeout:1; DWORD disconnect:1; DWORD rev1:1; DWORD os_logoned:1; DWORD logoned:1; DWORD online:1; }; };
而后再映射联合,编写一个类继承Union
,该类即映射到联合。
/** * 联合 */ public static class UNION extends Union { public int flags; public HostStatusStruct hostStatusStruct; }
最后映射最外层的PSA_HOST_STATUS_FLAGS
。
/** * @author zhangxishuo on 2019-02-26 * 结构体 PSA_HOST_STATUS_FLAGS */ public class HostStatusFlagsStruct extends Structure { /** * 联合 */ public static class UNION extends Union { public int flags; public HostStatusStruct hostStatusStruct; } /** * 结构体指针 */ public static class ByReference extends HostStatusFlagsStruct implements Structure.ByReference { } /** * 结构体具体的值 */ public static class ByValue extends HostStatusFlagsStruct implements Structure.ByValue { } public UNION union; @Override protected List<String> getFieldOrder() { return Collections.singletonList("union"); } }
看上去好像没什么毛病,一切都这么简单吗?固然不是。
一调用,就炸了。
看API
文档的声明,flags
应该是没什么具体含义的,而结构体中应该是咱们想要获取的信息。
那为何flags
有数,而结构体中却没有值呢?
struct TEST_STRUCT { int a, int b };
union TEST_UNION { int a, int b };
重写read
方法,当读取数据时,设置联合的类型为结构体类型。
@Override public void read() { super.read(); union.setType(HostStatusStruct.class); union.read(); }
当时这个问题把我愁坏了,捯饬了一成天才学明白。
一直是这个数字:-1073741824
,这个数字是否是有什么问题?
把-1073741824
转换为32
机的二进制表示:
1100 0000 0000 0000 0000 0000 0000 0000
1073741824
是2
的30
次方。
问题仍是出在这个上:
struct { DWORD rev:23; DWORD copy_status:3; DWORD timeout:1; DWORD disconnect:1; DWORD rev1:1; DWORD os_logoned:1; DWORD logoned:1; DWORD online:1; };
虽然DWORD
就映射为int
,但这里不是简单的映射:
rev:23
表示rev
占32
位。
copy_status:3
表示copy_status
占3
位。
timeout:1
表示timeout
占1
位。
内存是倒着存的,因此数据应该是这样。
原理知道了,那怎么映射呢?怎么映射一个一位的内容呢?
StackOverflow
上一老哥给出了解决方案,先写个int
把全部的都映射过来,而后我想要第几位再从里面扒。
/** * @author zhangxishuo on 2019-02-26 * 计算机状态结构体 */ public class HostStatusStruct extends Structure { /** * 结构体指针 */ public static class ByReference extends HostStatusStruct implements Structure.ByReference { } /** * 结构体具体的值 */ public static class ByValue extends HostStatusStruct implements Structure.ByValue { } public int value; public int getRev() { return value & 0x3FFFFF; } public int getCopyStatus() { return (value >> 23) & 0x3; } public int getTimeout() { return (value >> 26) & 0x1; } public int getDisconnect() { return (value >> 27) & 0x1; } public int getRev1() { return (value >> 28) & 0x1; } public int getOsLogoned() { return (value >> 29) & 0x1; } public int getLogoned() { return (value >> 30) & 0x1; } public int getOnline() { return (value >> 31) & 0x1; } @Override protected List<String> getFieldOrder() { return Collections.singletonList("value"); } }
至此,功能完成。
又过去了忙碌的一周,很高兴咱们的新项目已经完成大半。
感谢潘佳琦与李宜衡在本项目中的支持,第一次用Angular
,好多地方我也不懂,我先学着,而后设计一套架构付诸实践,潘佳琦与李宜衡也都能听从我制定的规范。
起初,我也提出了许多错误的规范,但当我用着用着发现原来那套不行的时候,及时改正,修改架构再从新设计,潘佳琦与李宜衡也在前台经历了大约三次的代码重构。
前台架构变动屡次,感受最后的设计还让人满意,也能让他人快速理解这种设计理念。
争取下一个项目,不使用ng-alain
,本身从头至尾搭建一个项目骨架。
最后表扬一下潘佳琦,上周基本我有一半的时间都在上课,我能作的就是前一天晚上把任务建好,而后写一些基础代码或示例代码,而后给潘佳琦讲,再让他去写。
潘佳琦效率仍是很高的,我记得周一的时候建了一堆任务,我想怎么着也得写两天吧,当我上课回来,发现“当当当”,潘佳琦都给写完了,代码也十分的规范。
对小组员的开发效率在心中也有了一个从新的定位。