1. 程式人生 > >深入解析JNA—模擬C語言結構體

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

 

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

前言

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

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

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

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

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

這不是JNA規定的,而是一個程式設計習慣。

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

3使用JNA呼叫使用StructC
函式

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中模擬的結構的變數順序絕對不能錯。

如,一個Struct2int變數。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個介面:

static interface


          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


          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的桌面應用信心百倍!

介面,那麼JNA認為你的Struct是一個指標。指向C語言的結構體。

介面,那麼JNA認為你的Struct是值型別,就是C語言的結構體。

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

2C語言中,結構體內部必須進行陣列定義。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