1. 程式人生 > >[譯文] 使用JNA來簡化對原生代碼的訪問[三]

[譯文] 使用JNA來簡化對原生代碼的訪問[三]

原文:Simplify Native Code Access with JNA

作者:Sanjay Dasgupta

檢測結構的記憶體對齊問題

這裡的情況是問題存在於細節中,故不能簡單地、非侵入性地做出結論說,某個特別的錯誤是因為記憶體對齊不匹配造成的,而是要通過下面這些必定乏味的討論來找出一種方法。

相關的程式碼(如下所示)在JTwain.java中,可以通過搜尋“Memory Alignment Problem Demo”來找到位置。該程式碼建立了一個TW_IDENTITY(一個Java的結構代替)例項,並把它傳遞給TWAIN執行時,TWAIN接著與使用者互動以選擇一個源裝置,TW_IDENTITY例項在被從Java傳遞給TWAIN時是未初始化的,但在返回時填充了關於選中裝置的資訊,程式碼最後的print()和dump()顯示結構的各個部分以便在檢測問題時有所幫助。

//起始: 記憶體對齊問題演示

TW_IDENTITY srcID = new TW_IDENTITY(Structure.ALIGN_DEFAULT);

stat = SelectSource(g_AppID, srcID);

if (stat != TWRC_SUCCESS) {

  //... (基於清晰的目的而刪除的一些行) ...

}

System.out.printf("ProtocolMajor: %02x%n", srcID.ProtocolMajor);

System.out.printf("ProtocolMinor: %02x%n", srcID.ProtocolMinor);

System.out.printf("SupportedGroups: %04x%n", srcID.SupportedGroups);

System.out.printf("Manufacturer: %s%n", new String(srcID.Manufacturer, 0, 34));

dump(srcID);

// 終止:記憶體對齊問題演示

下面顯示的printf()語句的輸出明顯示意了有記憶體對齊問題存在:

ProtocolMajor: 01

ProtocolMinor: 09

SupportedGroups: 694d0000

Manufacturer: crosoft                         Tw

頭兩個值:ProtocolMajor和ProtocolMinor是正確的,但是接下來的兩個則明顯是有問題的,在之前的TWAIN呼叫中,Java程式碼議定SupportedGroups的值為0x0003,因此應該返回同一個值才對,還有Manufacturer的值看上去明顯像似“Microsoft”的頭兩個字母被砍掉了。

現在讓我們來看看下面展示的dump()的輸出,“dump”顯示結構在從原生代碼接收到時的內容,還沒有被JNA分離成單個的成員值。

000:  11 04 00 00 01 00 00 00 0d 00 01 00 32 36 20 4a

000:  .    .    .    .    .    .    .    .    .    .    .    .   2   6        J   

016:  75 6e 65 20 32 30 30 30 00 00 00 00 00 00 00 00

016:  u   n   e        2   0   0   0   .    .    .    .    .    .    .    .    

032:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00

032:  .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    

048:  09 00 03 00 00 00 4d 69 63 72 6f 73 6f 66 74 00

048:  .    .    .    .    .    .    M  i   c    r   o   s   o  f    t   .    

064:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

064:  .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    .    

080:  00 00 00 00 00 00 00 00 54 77 61 69 6e 20 44 61

080:  .    .    .    .    .    .    .    .    T   w  a   i    n        D  a   

         ... (基於清晰的目的而刪除的其他行) ...

dump的每一行顯示16個位元組的“原始”記憶體的值,每行開始時的數值(在冒號之前)是該行從結構開始的位置算起的偏移值,每組16個位元組列印兩次——首先是16進位制的整數,然後是ASCII字元。顏色(手工配上的)用以分隔相鄰的結構成員,有下劃線的部分則是內嵌在結構TW_IDENTITY(參見Win32Twain.java)中的結構TW_VERSION,每個成員在結構的記憶體空間中的位置和所佔有的空間取決於每個成員被宣告的順序和大小。

從上面的轉儲內容看來,很明顯原生代碼返回的資訊是正確的(應該可以想到PC的Inter處理器是小端位元組序的(little-endian)),具體來說,那就是在轉儲記憶體中的SupportedGroups和Manufacturer的值也都是正確的。

Ÿ           ProtocolMajor(從偏移量46開始的塗成紫色的2個位元組)=0x01

Ÿ           ProtocolMinor(從偏移量48開始的塗成青色的兩個位元組)=0x09

Ÿ           SupportedGroups(從偏移量50開始的塗成紫色的4個位元組)=0x0003

Ÿ           Manufacturer(從偏移量54開始的塗成青色的34個位元組)=“Microsoft”(使用值為0的位元組填充至34個位元組)

比較printf()語句列印的值和轉儲記憶體中的值就可以看出來,JNA所理解的結構成員的位置從SupportedGroups開始不可思議地後移了兩個位元組,這是記憶體對齊問題的典型症狀。

對齊錯誤的出現是因為原生代碼把結構成員無任何插入間隔地串在一起,然而JNA程式碼卻期待在倍數於成員長度的記憶體偏移量位置上找到它們。因此,原生代碼把SupportedGroups放在偏移量為50的地方,但是JNA卻在偏移量為52(SupportedGroupds的長度4的倍數)的地方開始查詢。緊隨在SupportedGroups之後的結構成員也被往後推了兩個位元組,導致了前面顯示的Manufacturer值的變化,你現在應該也能夠解釋“Tw”如何會悄然出現在Manufacturer值的尾部。

最後簡短地說一點關於指標的另一個方面的題外話:dump()的程式碼展示了使用Structure.getPointer()來獲取指向結構開始之處的指標的做法,可以把結構當成是位元組陣列(C程式設計師的void*),使用由getPointer()返回的com.sun.jna.Pointer物件來訪問它。

重現結構對齊錯誤

檔案JTwain.java實際上包含了有記憶體對齊錯誤的程式碼,因此如果希望的話,讀者可以就此問題做進一步的研究,不過TWAIN演示程式仍能夠正常工作,因為它沒有用到結構中的值。

若要重現記憶體對齊錯誤的話,可按照後面“執行示例程式碼”一節中的描述來編譯程式,然後執行JTwainDemo.bat。你應該會看到下面圖3中的標題為“JTwain Demo”的視窗,在選單欄中選擇“File”->“Select Source…”,如圖中所示,一個列出了已安裝的TWAIN裝置的題為“Select Source”的視窗會彈出來,選擇任一個TWAIN裝置,然後點選“Select”按鈕,這會執行有對齊錯誤的程式碼,並會在命令視窗中顯示結構TW-VERSION的內容。



 

圖3:執行JTwain演示程式

需要注意的是,你看到的TW_VERSION結構的內容可能會與前面例子展示的值有所不同(除非你已經安裝了同樣的TWAIN裝置),不過你應該能夠看到是同樣型別的記憶體對齊問題的跡象。

如果標題為“Select Source”的彈出視窗中沒有顯示任何的TWAIN裝置的話,那麼你應該下載並安裝TWAIN developer tookit,該工具包模擬一個影象源(前面圖3中的“Select Source”視窗中的第一項),返回一個TWAIN標誌的影象。

防止結構對齊錯誤

一些本地庫有著不同的記憶體對齊風格(因為編譯器和編譯器選項存在差異的緣故),因此,既然JNA通常在不選擇重新編譯原生代碼的情況下使用,故它可以方便地設定使用的對齊策略。

可以通過呼叫Structure.setAlignType(int alignType)來設定擴充套件了Structure的Java類的成員的對齊策略,對齊方式有四種選項,如下表中的描述。

對齊規範

JNA描述

ALIGN_DEFAULT

使用平臺預設的對齊

ALIGN_GNUC

對32位的x86 linux/gcc有效,對齊欄位大小,最大為4個位元組

ALIGN_MSVC

對w32/msvc有效,根據欄位大小來對齊

ALIGN_NONE

不對齊,所有欄位都用1個位元組的最小分界線

前面展示的dump()輸出表明,TWAIN的原生代碼使用了沒有特別對齊的策略(前面表中的ALIGN_NONE),不過既然這也不是JNA的預設設定,那麼所有代替C結構的Java類都有一個預設的構造方法,該方法把對齊方式設定為ALIGN_NONE(參見Win32Twain.java)。下面的程式碼是結構TW_INDENTITY的帶有預設構造方法的Java類的簡略形式。

public class TW_IDENTITY extends Structure {

  public TW_IDENTITY() {

    setAlignType(Structure.ALIGN_NONE);

  }

  public int Id;

  public TW_VERSION Version = new TW_VERSION();

  public short ProtocolMajor;

  public short ProtocolMinor;

    . . .

}

一般來說是無法知道任何具體的本地庫所使用的對齊策略的,因此,如果DLL的文件沒有說明這部分資訊的話,則就需要做一些實驗來確定要使用的正確的對齊設定。

執行示例程式碼

按照如下步驟來執行本文中描述的示例程式碼:

Ÿ           下載包含了示例程式碼的壓縮檔案,並把它解壓到某個目錄(例如samples)中。

Ÿ           開啟一個命令視窗,然後使用“CD”命令進入samples\code目錄。

Ÿ           執行批處理檔案build.bat,編譯所有的程式碼(並且只要執行一次就可以了),類檔案被放置在名為samples\bin的目錄下。

Ÿ           若要執行某個程式的話,就執行有著相同名字(例如LockWorkStation.bat,、BeepMorse.bat、GetLogicalDrives.bat、GetSystemTime.bat、GetVolumeInformation.bat或者JTwainDemo.bat)的批處理檔案。

示例的壓縮檔案中包含了jna.jar,所以不需要再下載其他任何東西,前面列出的批處理檔案也已經指定了類路徑,所以不需要做任何修改就可以編譯和執行示例程式碼。

資源

Ÿ           本文的示例程式碼

Ÿ           C字串

Sanjay Dasgupta自1996年以來一直在使用Java(在多年使用了多種其他的語言之後),他是一個獨立顧問,現在住在印度的加爾各答。


相關推薦

[譯文] 使用JNA簡化原生訪問[]

原文:Simplify Native Code Access with JNA 作者:Sanjay Dasgupta 檢測結構的記憶體對齊問題 這裡的情況是問題存在於細節中,故不能簡單地、非侵入性地做出結論說,某個特別的錯誤是因為記憶體對齊不匹配造成的,而

居然還有人用3個空格

p2c id3 wix com fan nvm g3d edi poc %E7%BB%9D%E5%A4%A7%E9%83%A8%E5%88%86VR%E5%BC%80%E5%8F%91%E8%80%85%E4%BD%BF%E7%94%A8Unity%EF%BC%8C%E4%

使用JNA,讓java調用原生

hub object 重要 ive 環境 lib comm ray 執行 JNA定義: JNA:java Native Access,是SUN公司開發的基於JNI的

js 原生象的方法

image this element eve 查詢 bsp AS html標簽 () 1. id : document.getElementById(‘id‘) 2. 標簽 : document.getElementsByTagName(‘標簽‘) //獲得的是一個標簽

WebViewJavascriptBridge 使用 js調iOS原生

創建 smi creat color web var key urn cti js代碼和原生ios代碼進行交互使用WebViewJavascriptBridge非常簡化了我們的操作特別是在ios這邊 js 掉用ios原生代碼時要註意的幾個事項: 1、js和ios定義好相互調

用gruntcss進行壓縮

盤符 就會 targe mta ack images http www spa 1.先安裝Node.js環境 Grunt和 Grunt 插件是通過 npm 安裝並管理的,npm是 Node.js 的包管理器。Node.js的下載鏈接 安裝完後進行驗證 2.安裝grun

拿什麽心情閱讀我的(程序員的必備心理技能)

概念 術語 挖掘 偏好設置 而在 set 以及 理解 集中 原文首發於我的微信公眾號:GeekArtT . 閱讀源代碼的開始階段,最好從感興趣、自己有直觀感受且有相對豐富準確的文檔的項目開始。如同最開始閱讀數學證明,最好從淺顯易懂的教材開始,之後再開始最前沿的pape

使用Lombok簡化你的

ima equal .com targe alt pro lsa data 技術分享 http://www.cnblogs.com/ywqbj/p/5711691.html mavem    <dependency> <groupId>or

Oracle03——遊標、異常、存儲過程、存儲函數、觸發器和Java訪問Oracle

height 微軟 數值 getc statement 數據類型 put print .exe 作者: kent鵬 轉載請註明出處: http://www.cnblogs.com/xieyupeng/p/7476717.html 1.遊標(光標)Cursor 在寫

Rquest練習

thead path ade styles -- odin name com script 1.代碼練習 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%><%Strin

python裝飾器實現異常出現進行監控

args lin sha lines 監控腳本 一秒 readline utf 發送 異常,不應該存在,但是我們有時候會遇到這樣的情況,比如我們監控服務器的時候,每一秒去采集一次信息,那麽有一秒沒有采集到我們想要的信息,但是下一秒采集到了, 而

使用OCLint和SonariOS分析和質量管理

環境 測試 版本 ann true onf brew Coding 模板 OCLint 是一個強大的靜態代碼分析工具,可以用來提高代碼質量,查找潛在的bug,主要針對c,c++和Objective-c的靜態分析。 Sonar 是一個用於代碼質量管理的開放平臺。通過插件機制,

JS原生實現導航高亮

meta 樣式 添加 代碼 所有 wid 因此 margin position 一 實現原理 根據當前頁面滾動條的高度判斷當前頁面應當與導航欄中哪個導航相關聯,並對相應的導航設置高亮樣式。 二 代碼解析 先簡單寫一個頁面頂端的導航欄:<nav> <ul&

Java加密的兩種方式,防止反編譯

java加密使用Virbox Protector對Java項目加密有兩種方式,一種是對War包加密,一種是對Jar包加密。Virbox Protector支持這兩種文件格式加密,可以加密用於解析class文件的java.exe,並且可以實現項目源碼綁定制定設備,防止部署到客戶服務器的項目被整體拷貝。兩種加密方

javascript原生取單選框的值

checked undefine efi asc doc element 選擇 elements 好用 網上搜索到的,項目中正好用到,先記下來,以後直接從自己的博客上復制粘貼!!! //取單選框選擇中的值,傳入單選框的name function getR

jquery即點即改+php原生展示

blur value fun connect sta 表示 utf-8 user 展示 show.php代碼頁面 <meta charset="UTF-8"> <?php //鏈接數據庫 $link = mysqli_connect(‘127.0.0.1

投稿007期|令人震驚到發指的PyObject設計之美

member enable 找到 投稿 釋放 素數 程序開發 應用 剖析 前言 最近在重溫經典漫畫《SlamDunk》的全國大賽篇,其中的一個情形可以很好的詮釋虎軀一震這個狀態——當櫻木看到流川楓一次高難度投籃時內心的感受:“經過兩萬次射球練習後,櫻木首次明白到流川楓這一球

學點Groovy理解build.gradle

var 進行 sch ima 再看 基本數據 output 就是 介紹 在寫這篇博客時,搜索參考了很多資料,網上對於 Groovy 介紹的博客已經特別多了,所以也就沒準備再詳細的去介紹 Groovy,本來也就計劃寫一些自己認為較重要的點。後來發現了 Groovy 的官方文檔

SVO 特征分析

mon 特征 HERE tar 變化 文件 優化 需要 poi   SVO稀疏圖像對齊之後使用特征對齊,即通過地圖向當前幀投影,並使用逆向組合光流以稀疏圖像對齊的結果為初始值,得到更精確的特征位置。   主要涉及文件: reprojector.cpp matcher.cpp

原生同步到遠端github上

1.在本地資料夾下建立.gitignore檔案,將github下的github/gitignore/Node.gitignore檔案複製到.gitignore中; 2.執行命令git add . 3.git commit -m "init" 4.建立遠端倉庫 5.執行git remote add or