◆哀丁・四方山話 第06話
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について
- JNR GitHub 様
- torutkの日記 様
- 今日もプログラミング 様
- Sina Weibo 様
- Oracle JNR ドキュメント 様
●64bit DLLを作成する件について
●その他、有用なサイト様
哀丁・四方山話一覧 へ戻る
(画像URL:illust-AC 様:wayo さん、acworks さん iconka.com 様)