JNR(JNI)経由で、WindowsのネイティブDLLが呼び出せない

◆哀丁・四方山話 第06話

pinch
JNR(JNI)経由で、WindowsのネイティブDLLが呼び出せない

ジャンル・キーワード

  • Visual Studio 2015
  • Java
  • Visual C++
  • JNI、JNR、JNA

結果は・・・・解決(^o^)

 

何が起こったの?

Windowsのソフトウェアを開発する上で、.NET Framework が提供している高度な機能を利用することは、もはや不可欠です。

今回「PROCESSINGで音声合成を行うには(Windows編)」記事執筆のため、 .NET Framework が提供している「音声合成」機能を、Javaから使いたくなりました。

JavaからWindowsのAPI(ネイティヴ機能)を使うには、CやVisual C++ ( VC++ ) でネイティヴ機能を利用する部分をDLLとしてコーディングし、それをJNI(Java Native Interface)経由でJavaアプリケーションから呼び出すのが一般的です。

ところがJNIはなかなか難解な仕組みで、コーディングの保守性も(ようするに、後からぱっと見て何やっているか理解するのも)落ちてしまいます。

こんな時はJNIではなくJNA(Java Native Access)を使うのが良いです。JNIにくらべて圧倒的に簡単(の・・・筈)で、C++側に余計なコーディングを追加する必要もありません。

またJNAとよく似たものにJNR(Java Native Runtime)があります。JNAとの違いがよくわかりませんが(汗)、こちらも良さそうな感じです。

大した理由はありませんが、今回はJNRを使ってみる事にしました。

完成イメージは以下のような感じです。


JNRは初めてで、しかもVC++には不慣れだけど・・・いける!、俺ならきっとできる・・・と思ったのですが・・・(今から考えると、かなり無謀でしたね:汗)。

どうしてもJavaから VC++ で作ったDLLを 呼び出せません。JNRをJNAに変更してもNGです。

もういろいろな壁に衝突しまくりな「障害物競走」状態になったのですが、整理すると、ライブラリがロードできないという問題につきます。

遭遇した代表的なエラーが・・・これ。
Exception in thread “main” java.lang.UnsatisfiedLinkError:Unable to load library

なんかJNRからDLLが見えないと、のたまっていらっしゃいやがります。

DLLにPATHを通したり、DLLをWindowsのSystem32の下に置いたりもしましたが解決しません。

JNRで LibraryLoader.create() している箇所を絶対パスに変更してもNGです。

¥がいけないのか?、/なのか とか、.dll の拡張子まで指定するんだっけ?・・・といろいろ試行錯誤するも、全てダメ(泣)。

PROCESSINGからブリッジ用.jarを利用すると
A library relies on native code that’s not available. Or only works properly when the sketch is run 64-bit application.
のようなエラーとなります。

 

結論

とにかくDLLが見えない、ロードできないというエラーになった場合、以下の全てをチェックしましょう。

1つでも該当しているものがあると、上記現象に遭遇する可能性があります。

  • DLL、JDK(JRE)、Javaアプリ(EclipseやPROCESSINGなどの開発環境も)のbit数は全て揃える
  • Microsoft Visual C++ 2015 Redistributable をインストールする※
  • DLLを置くパスはWindowsの環境変数 path に指定するか、JVMの引数やEclipthの実行環境定義で指定する
  • DLLを置くパスは、間にスペースを含んだフォルダ階層は避ける
  • DLL側の公開関数には、extern “C” __declspec(dllexport) を付ける

下3つは今までの経験から「こうだろう?」と予想して対応済みでしたが、上2つにハマリました(汗)。

※私はC++のライブラリをVisualStudio2015で作成していたため、 Visual C++ 2015 Redistributableを導入しています。C++をVisualStudio2017や2019で開発している場合は、それに適合したランタイムライブラリーを導入してください。

またC++/CLIで作成するブリッジ用のDLLを用意せず、Javaから直接C#のマネージドコードを呼び出す方法もあります。

そちらについては「JavaからC#のDLLを呼び出す」記事を参照してください。

 

わかった事

bit数に関して

まずは、これですね。
JDK(JRE)、EclipseやPROCESSINGの開発環境、そしてVC++で作成するDLLのbit数は、全て統一されている必要があります。

JNRやJNAは、32bitと64bitで同じライブラリが使えました。

ただしJNRやJNAはJAVA_HOMEの環境変数でbit数を判断しているフシがあるため、JAVA_HOME環境変数は利用するJDKに応じた物に変更しておきましょう。

いろいろと検索したら

が同じ問題に遭遇されていました。大変参考とさせて頂きました。ありがとうございます。

とくにVisual Studio 2015で何も考えずにコンパイルすると、 32bitのモジュールが出来上がる事に気がつくまでに、無駄に時間を消費しました(汗)。

Visual Studio 2015で64bitのDLLやアプリケーションを作成する方法については、下記Microsoftの公式サイトを参考にしてください。

あとどうでも良いですが、もしもJNRとJNAのどちらを使っても良いなら、JNAをオススメします。

なんといっても情報量が違いすぎます。ええ。JNR・・・マイナーなのね(汗)。

ランタイムに関して

上記で64bit(あるいは32bit)に統一しても、まだDLLが見つからねーというエラーになる事があります。

特にPROCESSINGが吐くMSGには惑わされました。

64bit DLL が出来なかったのか・・・と、いろいろ調べたりもしました。ここでも大変苦戦(汗)。

64bitのDLLを使う場合は、Visual Studio 2015 C++ Redistributable(x64) をインストールしていないと、エラーになるようです。

ちなみにDLLのbit数を調べるには、Visual Studioに添付されている dumpbin.exe を使います。

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin などに格納されていますので、コマンドプロンプトから
dumpbin.exe  /headers DLLファイル名
のようにして使います。


上記のように 8864 machine ( x64 ) と表示されていれば、64bitで作成されています。

/headers を /exports にすると、DLLが公開している関数名がわかります。

DLL側の公開関数に extern “C” __declspec(dllexport) を記述していない場合は、@つきの名前になっている事があります。その場合はJNRから期待した名前で呼び出せません。

不安な人はチェックしてみましょう。


参考にさせて頂いたサイト様など

●JNAについて

●JNRについて

●64bit DLLを作成する件について

●その他、有用なサイト様

 


哀丁・四方山話一覧 へ戻る
(画像URL:illust-AC 様:wayo さん、acworks さん iconka.com 様)