虛擬化項目,須要用到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
,本身從頭至尾搭建一個項目骨架。
最後表揚一下潘佳琦,上週基本我有一半的時間都在上課,我能作的就是前一天晚上把任務建好,而後寫一些基礎代碼或示例代碼,而後給潘佳琦講,再讓他去寫。
潘佳琦效率仍是很高的,我記得週一的時候建了一堆任務,我想怎麼着也得寫兩天吧,當我上課回來,發現「噹噹噹」,潘佳琦都給寫完了,代碼也十分的規範。
對小組員的開發效率在心中也有了一個從新的定位。