日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入解析JNA—模拟C语言结构体

發布時間:2024/1/23 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入解析JNA—模拟C语言结构体 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原帖:http://blog.csdn.net/shendl/article/details/3599849

?

深入解析JNA—模擬C語言結構體

前言

前幾天寫《JNA--JNI終結者》一文介紹JNA框架。寫完之后才發現,忘了寫比較有難度的C語言Struct的模擬了。

今天就補上這篇文章,介紹Struct。

不寫怎樣模擬C語言結構體,就不能算是真正解決了調用動態鏈接庫的問題。

C語言的結構體用得實在是太廣泛了。

?

首先說明一點,本文中大量把模擬Struct的類寫作為接口的內部類。

這不是JNA規定的,而是一個編程習慣。

因為這些結構體(Structure類的子類),一般沒有重用的價值,因此寫成內部類比較方便。自然,你也可以把結構體寫成一般的類。

?

例3?? 使用JNA調用使用Struct的C函數

C語言開發

繼續使用例2中的那個VSC++的dll項目。

增加一個結構和使用該結構的函數。

頭文件增加如下:

?

#define MYLIBAPI extern "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;UserStruct users[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 WString name;public UserStruct.ByValue[] users=new UserStruct.ByValue[100];publicintcount; }public void 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個接口:

static?interface

Structure.ByReference
??????????Tagging interface to indicate the address of an instance of the Structure type is to be used within a
Structure definition rather than nesting the full Structure contents.

static?interface

Structure.ByValue
??????????Tagging interface to indicate the value of an instance of the
Structure type is to be used in function invocations rather than its address.

2個內部接口是標記,內部什么都沒有。

在運行時,JNA的執行框架會使用反射查看你是否實現了這2個接口,然后進行特定的處理。

(這種技術在java標注Annotation之前很流行。現在可以使用運行時Annotation實現同樣的效果。

JNA項目據說1999年就啟動了,使用這樣的老技術不足為奇。只是很奇怪,為什么國內都沒怎么聽說過。我也是最近偶然在國外的網站上發現它的。一試之下,愛不釋手,令我又對Java的桌面應用信心百倍!

?

?

如果你的Struct實現Structure.ByReference接口,那么JNA認為你的Struct是一個指針。指向C語言的結構體。

如果你的Struct實現Structure.ByValue接口,那么JNA認為你的Struct是值類型,就是C語言的結構體。

如果你不實現這2個接口,那么就相當于你實現了Structure.ByReference接口。

因此,在例3中,我沒有實現這2個接口。

2,C語言中,結構體內部必須進行數組定義。Java中最好也這樣做。

C語言的結構體是一段連續的內存,內存的大小是編譯時確定的。

因此,數組必須定義。否則編譯不會成功。

對應的Java類中,我們也應該在類定義時為數組定義。盡管實際上在使用時再賦值也可以。

但是,強烈不建議你這樣做。

如,上面

public UserStruct.ByValue[]users=new UserStruct.ByValue[100];

定義100個元素的數組,如果你不再類內部定義。

而在使用時定義,如果你沒有正確賦值,沒有定義為100個元素,就會出錯。

?

從表面上看,CompanyStruct類占用的內存是:

NativeLong?id

WString? name;

????????????????????

?????????? public UserStruct.ByValue[]users=new UserStruct.ByValue[100];

?????????? publicintcount;

4個元素占用的內存的總和。

?

由于Java的數組是一個對象,users中實際保存的也應該是一個引用,也就是指針,32bit。

?

那么CompanyStruct類占用的內存就比對應的C結構體:

struct CompanyStruct{

??? long id;

?? wchar_t*?name;

?? UserStruct?? users[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];

? // UserStruct??users[100];

?? int count;

?

};

MYLIBAPI void sayCompany2(CompanyStruct2* pCompanyStruct);

?

這里,把剛才使用的UserStruct數組換成UserStruct指針的數組。

JNA代碼

publicstaticclass CompanyStruct2 extends Structure{publicstaticclass ByReference extends CompanyStruct2 implements Structure.ByReference { }public NativeLong id;public WString name;public UserStruct.ByReference[] users=new UserStruct.ByReference[100];publicintcount;}publicvoid sayCompany2(CompanyStruct2.ByReference pCompanyStruct);

測試代碼:

CompanyStruct2.ByReference companyStruct2=new CompanyStruct2.ByReference();companyStruct2.id=new NativeLong(2);companyStruct2.name=new WString("Yahoo");companyStruct2.count=10; UserStruct.ByReference pUserStruct=new UserStruct.ByReference();pUserStruct.id=new NativeLong(90);pUserStruct.age=99;pUserStruct.name=new WString("良少");pUserStruct.write();// TestDll1.INSTANCE.sayUser(pUserStruct);for(int i=0;i<companyStruct2.count;i++){companyStruct2.users[i]=pUserStruct;} TestDll1.INSTANCE.sayCompany2(companyStruct2);

程序說明----Pin鎖住Java對象:

因為是結構體的指針的數組,所以,我們使用了

public UserStruct.ByReference[] users=new UserStruct.ByReference[100];

來對應C語言中的

UserStruct*? users[100];

?

但是,有問題,如果去除

pUserStruct.write();

這一行代碼,就會報錯。

如果去除pUserStruct.write();

但是使用??? TestDll1.INSTANCE.sayUser(pUserStruct);

也不會有問題。

?

這是怎么回事?

?

??? 原來,錯誤的原因就是內存鎖定!

?

java內存鎖定機制和JNI,JNA調用

我們知道,java的內存是GC管理的。它會自動管理JVM使用的堆內存。刪除不再使用的內存,并把Java對象使用的內存自動移動,以防止內存碎片增多。

因此,雖然Java的引用實際上就是指針,但還是和指針不同。Java中不能直接使用指針。因為對象在內存中的地址都不是固定的。說不準什么時候GC就把它給移位了。

?

如果使用JNI,JNA等技術調用C函數。那么就會有問題。因此,C語言使用的是指針。如果想要獲取C函數的返回值。那么我們必須提供一塊內存給C語言訪問。而C語言是不知道你Java的引用的。它只能訪問固定的內存地址。

如果GC把Java對象移來移去,那么C函數就沒辦法和Java交互了。

?

因此,在使用JNI和JNA時,都會把Java對象鎖住。GC不再管理。不刪除,也不移動位置。由此出現的內存碎片,也不管了!

這種技術的術語是PIN。? .NET也有同樣的概念。

?

?

上面TestDll1.INSTANCE.sayUser(pUserStruct);這個調用是JNA調用。這個操作就把pUserStruct這個Java對象鎖住了。

?

4中,嵌套的是結構體,因此也會直接把CompanyStruct類的實例鎖住。

?

但是例5中,嵌套的是結構體的指針的數組。? CompanyStruct2類的實例companyStruct2在執行

TestDll1.INSTANCE.sayCompany2(companyStruct2);

時是被鎖住了,可以companyStruct2users指針數組的成員:

pUserStruct?都沒有被鎖住。

?

怎么辦呢??

難道每一個UserStruct.ByReference 的實例都先調用一遍不需要的

TestDll1.INSTANCE.sayUser(pUserStruct);? 方法?

?

沒事!JNA開發人員早已想到了:

Structure? 類中有方法:

write

public void write()

Writes the fields of the struct to native memory


writeField

public void writeField(String?name)

Write the given field to native memory. The current value in the Java field will be translated into native memory.

Throws:

IllegalArgumentException - if no field exists with the given name

?

??? 這些write方法,會把Java的內存Pin住。?? 就是C語言可以使用。GC不再管理它們。

?

現在只要調用

pUserStruct.write();

java模擬結構體實例給Pin住就可以了。

?

題外話,C#定義了語法,可以使用關鍵字 pin 鎖住.NET對象。

?

結論:

結構體是C語言模擬OOP開發中經常使用的一種數據組織形式。搞定了結構體Struct,我們就可以放心大膽、輕輕松松地把C程序隨便拿來用了!

總結

以上是生活随笔為你收集整理的深入解析JNA—模拟C语言结构体的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。