「JNA」intとDWORDのマッピングについて
JNA で Windows API を呼び出す際の int と DWORD のマッピングについてメモしておきます。
DWORD については以下のドキュメントに記載の通り、32bit の符号なし int (0 ~ 4294967295) になります。
A 32-bit unsigned integer. The range is 0 through 4294967295 decimal.
This type is declared in IntSafe.h as follows:
typedef unsigned long DWORD;
一方、Java の int については 32bit 符号ありになります。
System.out.println(Integer.MAX_VALUE); //2147483647 System.out.println(Integer.MIN_VALUE); //-2147483648
DWORD に対して int をマッピングしても JNA は特に怒らないのですが、上記の通りなので、このマッピングでは Windows API 側から 2147483647 を超える DWORD の値を受け取った場合、Java で結果がおかしなことになります。例えば以下のような感じ。
・GetFileSize関数で 4G バイトのファイルサイズを取得するサンプル(GetFileSize関数の戻り値を int にマッピング)
package com.example.test; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.platform.win32.WinDef.DWORDByReference; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.platform.win32.WinNT.HANDLE; public class DWORDTest { public interface Kernel32 extends Library { Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class); HANDLE CreateFileW(String lpFileName, int dwDesiredAccess, int dwShareMode, WinNT.SECURITY_ATTRIBUTES lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, HANDLE hTemplateFile); int GetFileSize(HANDLE hFile, DWORDByReference lpFileSizeHigh); boolean CloseHandle(HANDLE h); } public static void main(String[] args) { HANDLE hFile = null; try { hFile = Kernel32.INSTANCE.CreateFileW("4G.txt", WinNT.GENERIC_READ, 0, null, WinNT.OPEN_EXISTING, WinNT.FILE_ATTRIBUTE_NORMAL, null); int length = Kernel32.INSTANCE.GetFileSize(hFile, null); System.out.println("result : " + length); } finally { if(hFile != null) Kernel32.INSTANCE.CloseHandle(hFile); } } }
・実行結果
result : -1
上記の問題に対する対応は、JNA で DWORD に対応するクラスが用意されているので、こいつを使ってやるだけで大丈夫みたいです。
・JNA の DWORD クラスを使ってマッピングしたサンプル
package com.example.test; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.platform.win32.WinDef.DWORD; import com.sun.jna.platform.win32.WinDef.DWORDByReference; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.platform.win32.WinNT.HANDLE; public class DWORDTest { public interface Kernel32 extends Library { Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class); HANDLE CreateFileW(String lpFileName, int dwDesiredAccess, int dwShareMode, WinNT.SECURITY_ATTRIBUTES lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, HANDLE hTemplateFile); DWORD GetFileSize(HANDLE hFile, DWORDByReference lpFileSizeHigh); boolean CloseHandle(HANDLE h); } public static void main(String[] args) { HANDLE hFile = null; try { hFile = Kernel32.INSTANCE.CreateFileW("4G.txt", WinNT.GENERIC_READ, 0, null, WinNT.OPEN_EXISTING, WinNT.FILE_ATTRIBUTE_NORMAL, null); DWORD length = Kernel32.INSTANCE.GetFileSize(hFile, null); System.out.println("result : " + length.longValue()); } finally { if (hFile != null) Kernel32.INSTANCE.CloseHandle(hFile); } } }
・実行結果
result : 4294967295
無事、Java の int の範囲を超える結果も受け取れてますね。
長々書くほどの内容でもないですが、以上になります。
[環境情報]
Windows 10
Java SE 8 Update 92
JNA 4.2.2