「JNA」intとDWORDのマッピングについて

JNA で Windows API を呼び出す際の int と DWORD のマッピングについてメモしておきます。


DWORD については以下のドキュメントに記載の通り、32bit の符号なし int (0 ~ 4294967295) になります。

Windows Data Types (Windows)

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 に対応するクラスが用意されているので、こいつを使ってやるだけで大丈夫みたいです。

W32API.DWORD — Project Kenai

・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