torutkのブログ

ソフトウェア・エンジニアのブログ

Windows Location APIをJavaから利用する

Windows Sensor and Location APIでGPSデバイスから座標を取得 - torutkの日記 の続きです。
Javaから、Windows Sensor and Location APIを利用する方法を探ります。

Javaからネイティブのライブラリを呼び出す方法

まずは、Java SE Development Kit(JDK)標準の機構であるJNI があります。これは、ネイティブのライブラリを呼び出すためにC/C++でラッパーを記述する必要があり、Javaの開発に加えてC/C++の開発が必要になります。

一方、世の中にはC/C++でラッパーを記述しなくてもJavaだけでネイティブのライブラリを呼び出す便利なライブラリがあります。代表的なものに JNAライブラリ があります。ただし、C言語インタフェースの利用に限られます。

ところで、Location APIは、COMインタフェースとなっています。COMインタフェースを利用するライブラリがいくつか存在しています。代表的なものに com4jライブラリ があります。

JNAも標準ではなくcontribにCOMインタフェース呼び出しの機能があるようです(jna-platform.jar)。

com4j を使ってみる

ビルド済みライブラリ(com4jとtlbimp)をmavenリポジトリより入手します。現時点ではv2.1が最新のようです。

以下に入手とCOMインタフェースの型をJavaで生成するところまでの作業を記述しました。
http://www.torutk.com/projects/swe/wiki/Com4j

COMタイプライブラリから型情報を取得し対応するJavaクラスを生成

利用したいCOMライブラリファイル(DLLやOCXなど)から型情報を取得して、対応するJavaクラスを自動生成します。

com4jのtlbimpライブラリ(実行可能JARファイル)を実行し、対象COMライブラリファイル、生成するJavaクラスのパッケージ名、生成先ディレクトリをコマンドラインオプションで指定します。
(上述URL参照)

COMライブラリの初期化

Visual C++(ATL)では、最初にCOMライブラリの初期化(CoInitializeEx)を、COMを呼び出すスレッドごとに実行するのですが、com4jでは初期化らしきものを見かけないので内部で行われているのかと思います。

  • Visual C++(ATL)
CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);

不要(?)

ILocationオブジェクトのインスタンス生成
  • Visual C++(ATL)
    CComPtr<ILocation> pLocation;
    if (FAILED(pLocation.CoCreateInstance(CLSID_Location))) {
        return 1;
    }
    ILocation location = ClassFactory.createLocation();
権限の取得
  • Visual C++(ATL)
    IID REPORT_TYPES[] = { IID_ILatLongReport };
    if (FAILED(pLocation->RequestPermissions(nullptr, REPORT_TYPES, ARRAYSIZE(REPORT_TYPES), TRUE))) {
        return 1;
    }

ここで、IID型の配列で指定しているIID_ILatLongReportの正体を調べると、locationapi.hに次のように定義されています。

EXTERN_C const IID IID_ILatLongReport;

実行して内容を確認すると、インタフェースILatLongReportのIIDと一致しました。

7FED806D-0EF8-4F07-80AC-36A0BEAE3134

        GUID reportType = new GUID("7FED806D-0EF8-4F07-80AC-36A0BEAE3134"); // IID_ILatLongReport
        location.requestPermissions(0, reportType, 1, 1);

requestPermissionsのシグニチャは、次のように生成されています。

void requestPermissions(int hParent, GUID pReportTypes, int count, int fModal);

第2引数がGUID[]ではなくGUIDとなっているのが微妙です(バグ?)

位置情報のステータスの取得
  • Visual C++(ATL)
	LOCATION_REPORT_STATUS status;
	if (FAILED(pLocation->GetReportStatus(IID_ILatLongReport, &status))) {
		return 1;
	}
LOCATION_REPORT_STATUS status = location.getReportStatus(reportType);
位置情報(ILocationReport)の取得
  • Visual C++(ATL)
    CComPtr<ILocationReport> pLocationReport;
    hr = pLocation->GetReport(IID_ILatLongReport, &pLocationReport);
    if (FAILED(hr)) {
        return 1;
    }
ILocationReport report = location.getReport(reportType);
位置情報(ILocationReport)を緯度経度情報(ILatLongReport)へ
  • Visual C++(ATL)
    CComQIPtr<ILatLongReport> pLatLongReport(pLocationReport);
ILatLongReport latLongReport = report.queryInterface(ILatLongReport.class);
緯度経度情報から緯度経度の値を取得
  • Visual C++(ATL)
    double latitude = 0.0;
    double longitude = 0.0;
    if (FAILED(pLatLongReport->GetLatitude(&latitude))) {
        return 1;
    }
    if (FAILED(pLatLongReport->GetLongitude(&longitude))) {
        return 1;
    }
        double latitude = latLongReport.getLatitude();
        double longitude = latLongReport.getLongitude();