深入解析JNA—模擬C語言結構體
深入解析JNA—模擬C語言結構體
前言
前幾天寫《JNA--JNI終結者》一文介紹JNA框架。寫完之後才發現,忘了寫比較有難度的C語言Struct的模擬了。
今天就補上這篇文章,介紹Struct。
不寫怎樣模擬C語言結構體,就不能算是真正解決了呼叫動態連結庫的問題。
C語言的結構體用得實在是太廣泛了。
首先說明一點,本文中大量把模擬Struct的類寫作為介面的內部類。
這不是JNA規定的,而是一個程式設計習慣。
因為這些結構體(Structure類的子類),一般沒有重用的價值,因此寫成內部類比較方便。自然,你也可以把結構體寫成一般的類。
例3使用JNA呼叫使用Struct的C 函式
C語言開發
繼續使用例2中的那個VSC++的dll專案。
增加一個結構和使用該結構的函式。
標頭檔案增加如下:
#define MYLIBAPIextern"C"__declspec( dllexport )
struct UserStruct{
long id;
wchar_t*name;
int age;
};
MYLIBAPI void sayUser(UserStruct* pUserStruct);
JNA程式
對應的Java程式中,在例2的介面
/*
* 定義一個類,模擬C語言的結構
* */
publicstaticclass UserStruct extends Structure{
public NativeLong id;
public WString name;
publicintage;
}
publicvoid sayUser(UserStruct.ByReference struct);
Java中的呼叫程式碼:
UserStruct userStruct=new UserStruct ();
userStruct.id=new NativeLong(100);
userStruct.age=30;
userStruct.name=new WString("沈東良");
TestDll1.INSTANCE.sayUser(userStruct);
Struct 說明
現在,我們就在Java中實現了對C語言的結構的模擬。
這裡,我們繼承了Structure類,用這個類來模擬C語言的結構。
必須注意,Structure子類中的公共欄位的順序,必須與C語言中的結構的順序一致。否則會報錯!
因為,Java呼叫dll中的C函式,實際上就是一段記憶體作為函式的引數傳遞給dll。
Dll以為這個引數就是C語言傳過來的引數。
同時,C語言的結構是一個嚴格的規範,它定義了記憶體的次序。因此,JNA中模擬的結構的變數順序絕對不能錯。
如,一個Struct有2個int變數。Int a, int b
如果JNA中的次序和C中的次序相反,那麼不會報錯,但是得到的結果是相反的!
例4使用JNA呼叫使用巢狀Struct陣列的C函式
如果C語言中的結構體是複雜的巢狀的結構體,該怎麼辦呢?
繼續在上面例3的基礎上擴充。
C語言開發
標頭檔案增加如下:
struct CompanyStruct{
long id;
wchar_t*name;
UserStructusers[100];
int count;
};
MYLIBAPI void sayCompany(CompanyStruct* pCompanyStruct);
原始檔:
void sayCompany(CompanyStruct* pCompanyStruct){
std::wcout.imbue(std::locale("chs"));
std::wcout<<L"ID:"<<pCompanyStruct->id<<std::endl;
std::wcout<<L"公司名稱:"<<pCompanyStruct->name<<std::endl;
std::wcout<<L"員工總數:"<<pCompanyStruct->count<<std::endl;
for(int i=0;i<pCompanyStruct->count;i++){
sayUser(&pCompanyStruct->users[i]);
}
}
JNA程式
Java程式中,在原來的介面上加上如下程式碼:
publicstaticclass CompanyStruct extends Structure{
public NativeLong id;
public WStringname;
public UserStruct.ByValue[] users=new UserStruct.ByValue[100];
publicintcount;
}
publicvoid sayCompany(CompanyStruct pCompanyStruct);
對原來的UserStruct類進行改寫:
/*
* 定義一個類,模擬C語言的結構
* */
publicstaticclass UserStruct extends Structure{
publicstaticclass ByReference extends UserStruct implements Structure.ByReference { }
publicstaticclass ByValue extends UserStruct implements Structure.ByValue
{ }
public NativeLong id;
public WString name;
publicintage;
}
呼叫JNA程式:
CompanyStruct companyStruct=new CompanyStruct();
companyStruct.id=new NativeLong(1);
companyStruct.name=new WString("Google");
companyStruct.count=9;
UserStruct.ByValue userStructValue=new UserStruct.ByValue();
userStructValue.id=new NativeLong(100);
userStructValue.age=30;
userStructValue.name=new WString("沈東良");
for(int i=0;i<companyStruct.count;i++){
companyStruct.users[i]=userStructValue;
}
TestDll1.INSTANCE.sayCompany(companyStruct);
說明
可以看到,程式正確輸出了。
讀者也許會有一些疑問。
1,為什麼我們要給UserStruct這個結構新增2個內部類呢?
看Structure類的API說明,我們知道,這個類內部有2個介面:
|
|
|
|
這2個內部介面是標記,內部什麼都沒有。
在執行時,JNA的執行框架會使用反射檢視你是否實現了這2個介面,然後進行特定的處理。
(這種技術在java標註Annotation之前很流行。現在可以使用執行時Annotation實現同樣的效果。
JNA專案據說1999年就啟動了,使用這樣的老技術不足為奇。只是很奇怪,為什麼國內都沒怎麼聽說過。我也是最近偶然在國外的網站上發現它的。一試之下,愛不釋手,令我又對Java的桌面應用信心百倍!
)
介面,那麼JNA認為你的Struct是一個指標。指向C語言的結構體。
介面,那麼JNA認為你的Struct是值型別,就是C語言的結構體。
因此,在例3中,我沒有實現這2個介面。
2,C語言中,結構體內部必須進行陣列定義。Java中最好也這樣做。
C語言的結構體是一段連續的記憶體,記憶體的大小是編譯時確定的。
因此,陣列必須定義。否則編譯不會成功。
對應的Java類中,我們也應該在類定義時為陣列定義。儘管實際上在使用時再賦值也可以。
但是,強烈不建議你這樣做。
如,上面
public UserStruct.ByValue[] users=new UserStruct.ByValue[100];
定義100個元素的陣列,如果你不再類內部定義。
而在使用時定義,如果你沒有正確賦值,沒有定義為100個元素,就會出錯。
從表面上看,CompanyStruct類佔用的記憶體是:
NativeLongid
WStringname;
public UserStruct.ByValue[] users=new UserStruct.ByValue[100];
publicintcount;
這4個元素佔用的記憶體的總和。
由於Java的陣列是一個物件,users中實際儲存的也應該是一個引用,也就是指標,32bit。
那麼CompanyStruct類佔用的記憶體就比對應的C結構體:
struct CompanyStruct{
long id;
wchar_t*name;
UserStructusers[100];
int count;
};
小很多。記憶體少用很多。
我在例3的說明中曾經說過:
“
Java呼叫dll中的C函式,實際上就是一段記憶體作為函式的引數傳遞給dll。
Dll以為這個引數就是C語言傳過來的引數。
”
那麼,JNA怎麼還能正確呼叫C函式呢。
事實上,在呼叫C函式時,JNA會把UserStruct類的例項的所有資料全部*100倍。(我的例子裡陣列是100個,你的例子當然可以有不一樣的數值)
這樣,Java中的結構體的記憶體量就和C語言的CompanyStruct結構體佔據相同大小和結構的記憶體,從而正確呼叫C函式。
例5使用JNA呼叫使用巢狀Struct的指標的陣列的C函式
現在給大家看看最複雜的Struct的例子。
Struct中巢狀的是一個結構體的指標的陣列。
C語言程式碼
struct CompanyStruct2{
long id;
wchar_t*name;
UserStruct*users[100];
// UserStructusers[100];
相關推薦
貼上JNA—JNI終結者、深入解析JNA—模擬C語言結構體兩篇文章的完整原始碼
原文 http://blog.csdn.net/shendl/archive/2008/12/26/3599854.aspx 貼上 JNA—JNI 終結者 、 深入解析 JNA— 模擬 C 語言結構體 兩篇文章的完整原始碼 C 語言
深入解析JNA—模擬C語言結構體
深入解析JNA—模擬C語言結構體 前言 前幾天寫《JNA--JNI終結者》一文介紹JNA框架。寫完之後才發現,忘了寫比較有難度的C語言Struct的模擬了。 今天就補上這篇文章,介紹Stru
深入解析JNA—模擬C語言結構體(2)
原文 http://blog.csdn.net/shendl/archive/2008/12/26/3599849.aspx 2 , C 語言中,結構體內部必須進行陣列定義。 Java 中最好也這樣做。 C 語言的結構體是一段連續的記憶體,記憶體的大小是編譯
貼上 JNA—JNI 終結者 、 深入解析 JNA— 模擬 C 語言結構體 兩篇文章的完整原始碼 (續)...
原文 http://blog.csdn.net/shendl/archive/2008/12/26/3599854.aspx /** * @param args */ public static void main(String[] args
使用 JNA 模擬C語言結構體的完整原始碼
Java原始檔: 請先把JNA.jar匯入classpath。 package com.google.p.shendl.jna.net; import com.google.p.shendl.jna.net.TestDll1Service.TestDll1.CompanyStruct; import co
JNA—模擬C語言結構體
前言 前幾天寫《JNA--JNI終結者》一文介紹JNA框架。寫完之後才發現,忘了寫比較有難度的C語言Struct的模擬了。 今天就補上這篇文章,介紹Struct。 不寫怎樣模擬C語言結構體,就不能算是真正解決了呼叫動態連結庫的問題。 C語言的結構體用得實在是太廣
C語言結構體佔用空間記憶體大小解析
結構體的資料型別的有點我們就不囉嗦了,直接來看相同資料結構體的幾種書寫的格式吧。 格式一: 01.struct tagPhone 02.{ 03. char A; 04. int B; 05. short C; 06.
C語言結構體struct的語法解析
本節內容需要結合視訊講解才能更容易理解,視訊播放地址如下: 用java開發編譯器 本節,我們著重研究結構體定義,也就是struct 這種變數定義,C語言編譯器是如何解析的,本節我們要解析的結構體定義如下: struct tag { int x;
在C語言結構體中添加成員函數
我們 pau 打印 log print class 控制 stdio.h 語言 我們在使用C語言的結構體時,經常都是只定義幾個成員變量,而學過面向對象的人應該知道,我們定義類時,不只是定義了成員變量,還定義了成員方法,而類的結構和結構體非常的相似,所以,為什麽不想想如何
c語言結構體大小 sizeof(struct A)
成員 print ble ret 意義 cnblogs () pan color 1,數據類型自身對齊 數據類型的起始地址為其大小的整數倍 2,結構體的自身對齊 結構體的自身對齊值為其中最大的成員大小 3,指定對齊 可以使用關鍵詞#pragma pack(1) 來指定結構體
C語言----結構體---結構體與函數
urn 全局變量 月的天數 [] strong ret 例子 c語言 += 結構作為參數的函數 整個結構可以作為參數傳入函數 這時是在函數中新建了一個結構變量,並復制調用這個結構的值(重點,只是把值傳入函數,而函數外面真正的變量並沒有改變,與數組不同) 函數也可以返
C語言結構體數組內帶字符數組初始化和賦值
指定 char 字符數 全局 種類 def 變量 指針 變量定義 1.首先定義結構體數組: typedef struct BleAndTspRmtCmd{ char terminal[3]; char note[3]; char rmtCmd[10]; char cmdP
C語言結構體作業
數列 class 系統 過程 函數功能 文字 inf not 類型 一、PTA實驗作業 題目1:6-3 結構體數組中查找指定編號人員 1. 本題PTA提交列表 2. 設計思路 定義一個結構體指針*p for i=0 to i=7 如果std+i的編號與輸入的編號
C語言結構體所占內存大小
oat 接下來 面試題 語言 將不 位置 idt str 返回 用一道面試題題引入struct str1 { char a; int b; float c; double d; };char類型占用
C語言結構體在內存中的存儲情況探究------內存對齊
.com ret size \n str 技術 urn 存儲 typedef 條件(先看一下各個基本類型都占幾個字節): void size_(){ printf("char類型:%d\n", sizeof(char)); printf("in
c語言結構體定義的幾種形式
sdn 方法 tps statistic htm def courier ng- target 轉自https://blog.csdn.net/ziguo2010/article/details/79897327 1、最常用定義方式:定義結構體data,
C語言結構體字節對齊簡單計算方法
最大的 一個數 數據類型 double short 位操作 結構體字節對齊 根據 struct 1.在C語言裏面每一種數據類型都有字節對齊比如在32位操作系統下:整型的自身對齊數就是 4 字節,字符型就是 1 字節,double就是 8 字節。 但是結構體的計算方式就和普通
C語言結構體訓練
結構體大小和記憶體結構 1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<string.h> 4 #include<stdlib.h> 5 6 //結構體需要根據資料型別進
C語言結構體中冒號(位域)用法
位域出現的原因是由於某些資訊的儲存表示只需要幾個bit位就可以表示而不需要一個完整的位元組,同時也是為了節省儲存空間和方便處理。 typedef struct bit_struct { int &n
C語言小節篇1:關於C語言結構體引用變數的問題
這個問題以前沒注意過,今天和同事討論後,查詢相關資料,想搞懂一個問題: 關於結構體引用變數的符號 . 和 -> 的區別。 目前得到的結論是: 變數引用 . 直針引用&n