プロジェクト

全般

プロフィール

com4j

Windows COM形式ライブラリをJavaから利用するためのライブラリです。

公式サイト

http://com4j.kohsuke.org/index.html

リポジトリは次
https://github.com/kohsuke/com4j

インストール

mavenリポジトリからビルド済みのライブラリを取得します。

2015-08-14時点では、バージョン2.1が最新です。また、tlbimpはargs4jを利用しているので、args4jも取得します。
tlbimp-2.1 -> args4j-2.0.8

com4j-2.1.jar をzip解凍可能なツールで中からdllファイルを2つ抜き出します。
com4j-x86.dll, com4j-amd64.dll

以下が必要なファイル一式です。これを、適切なディレクトリに配置します(例、C:\java\com4j)

com4j-2.1.jar             tlbimp-2.1.jar            args4j-2.0.8.jar
com4j-2.1-javadoc.jar     tlbimp-2.1-javadoc.jar    com4j-x86.dll
com4j-2.1-sources.jar     tlbimp-2.1-sources.jar    com4j-amd64.dll

DLLをあらかじめjarファイルから抜いておく理由

com4jを利用するプログラムを実行する際、com4jのjarファイルと同じディレクトリにネイティブのライブラリ(com4j-x86.dllまたはcom4j-amd64.dll)が存在しないと、jarファイルの中からdllを自動で展開します。しかし、そのディレクトリにアプリケーション実行ユーザーが書き込み権限を持たない場合、dllが展開できず、com4jの初期化がエラーとなります。

NetBeansにおいても、通常の実行、デバッグ実行の際はライブラリ定義にあるcom4jを使用するのでライブラリ定義の場所が書き込み権限のないProgram Files下だとエラーとなります。

エラーメッセージを 表示する

利用方法

com4jは、COMインタフェースの型情報リポジトリ

COMインタフェース型に対応するJavaクラスを生成

COMインタフェースの提供する型に対応するJavaクラスを生成します。型情報は、COMのライブラリ(DLL、OCXなどの)ファイルから抽出します。

C:\work> java -jar C:\java\com4j\tlbimp-2.1.jar -o location -p geolocation %WINDIR%\System32\LocationApi.dll
Generating definitions from LocationApiLib
Unable to handle the type _SYSTEMTIME
  method GetTimestamp
  interface ILocationReport
Unable to handle the type tag_inner_PROPVARIANT
  method GetValue
  interface ILocationReport
    :(中略)
Unable to handle the type tagTLIBATTR
  method RemoteGetLibAttr
  interface ITypeLib
Don't know how to print VT_UI4 as an Java literal
Don't know how to print VT_UI4 as Java program code

生成されたJavaソースファイルは次のとおりです。

ClassFactory.java                IStorage.java
events                           IStream.java
ICivicAddressReport.java         ITypeComp.java
ICivicAddressReportFactory.java  ITypeInfo.java
IDefaultLocation.java            ITypeLib.java
IDispCivicAddressReport.java     LOCATION_DESIRED_ACCURACY.java
IDispLatLongReport.java          LOCATION_REPORT_STATUS.java
IEnumSTATSTG.java                package.html
ILatLongReport.java              tagCALLCONV.java
ILatLongReportFactory.java       tagDESCKIND.java
ILocation.java                   tagFUNCKIND.java
ILocationEvents.java             tagINVOKEKIND.java
ILocationReport.java             tagSYSKIND.java
ILocationReportFactory.java      tagTYPEKIND.java
IRecordInfo.java                 tagVARKIND.java
ISequentialStream.java

メソッドのシグニチャについて

COMで定義されるメソッドは、通常戻り値型がHRESULTで、メソッドの処理結果は引数([out,retval])の1つとして実現します。
Javaでは、メソッドの処理結果を戻り値とすることが一般的です。
そこで、com4jではJavaの型を生成する際に、シグニチャをCOMの元の定義からアレンジして、メソッドの処理結果を戻り値として実現します。

http://com4j.kohsuke.org/runtime-semantics.html

プログラミング(Location APIを使ってGPSの位置を取得)

ILocation COMオブジェクトのインスタンス生成

ILocation location = ClassFactory.createLocation();

生成されたClassFactoryクラスのcreateLocationメソッドを使ってILocationインスタンスを生成します。
createLocationは、COM4J.createInstance(Class<T>, String) を呼んでおり、戻り値はnon-nullで、例外発生時はComExceptionをスローするとあります。

ILocationのrequestPermissionを呼び出し

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

C++/ATLでは、IID_ILocationReport というシンボル(GUID型)が利用可能ですが、生成されたJavaコードにはこのシンボルが存在しなかったので、ILocationReportインタフェースのIIDを調べて(生成されたILatLongReport.java ファイルを見ると次のように書かれているので)、そのGUID文字列を使用します。

@IID("{7FED806D-0EF8-4F07-80AC-36A0BEAE3134}")
public interface ILatLongReport extends com.torutk.location.ILocationReport {

requestPermissionの引数については次のように指定しています。

  • 第1引数はC++/ATLではNULLを指定していたので、Javaでは0を指定
  • 第2引数はC++/ATLではIID[]と配列ですが、生成されたJavaコードではGUIDとなっており、ここでは要素1つだけだったので配列ではなくGUIDを1つ指定
  • 第3引数はC++/ATLでは第2引数の配列の要素数を指定していたので、ここでは1を指定(Javaでは第2引数が配列でないので2以上を指定するとダメかも)
  • 第4引数はC++/ATLではTRUE(整数1のデファイン)を指定していたので、ここでは1を指定

ILcationのgetReportStatusを呼び出し

LOCATION_REPORT_STATUS status = location.getReportStatus(reportType);
if (status != LOCATION_REPORT_STATUS.REPORT_RUNNING) {
    // 位置データの取得ができない状態の対処
}

LOCATION_REPORT_STATUS はenum型で、レポートの状態を判別できます。座標データが取得できるのはREPORT_RUNNINGのときです。

ILocationから位置(緯度経度)情報の取得

ILocationReport report = location.getReport(reportType);
ILatLongReport latLongReport = report.queryInterface(ILatLongReport.class);
if (latLongReport == null) {
    // reportはILatLongReportインタフェースを実装していない
}

ILocationから、IID_ILatLongReportを指定して緯度経度の位置情報を取得します。ILocationインタフェースのGetReportメソッドはILocationReport型で取得されるので、緯度経度を取り出すためにILatLongReport型へQI(QueryInterface)して型変換(ダウンキャストのようなもの)します。
QIが出来ない場合はnullが返されるので、nullチェックをしておきます。

double latitude = latLongReport.getLatitude();
double longitude = latLongReport.getLongitude();

緯度(latitude)、経度(longitude)を取得します。
ILatLongReportにはほかにも高度(altitude)、および誤差(水平方向、高さ方向)を取得するメソッドもあります。

例外処理

生成されたJavaクラス群を利用する際に発生するエラーは、大抵は非チェック例外 ComException で通知されます。