質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JNI

JNI(Java Native Interface)は、Javaプラットフォームにおいて、Javaで記述されたプログラムと、他の言語で書かれたネイティブコードを連携するためのインタフェース仕様である

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

2756閲覧

JNIを用いていて2つエラーが発生する

yukkuri

総合スコア624

JNI

JNI(Java Native Interface)は、Javaプラットフォームにおいて、Javaで記述されたプログラムと、他の言語で書かれたネイティブコードを連携するためのインタフェース仕様である

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/10/24 13:54

前提・実現したいこと

現在、JNIを使用してGUIを作っています。しかし、2つ問題が発生しました。

発生している問題・エラーメッセージ

1つ目 Exceptionのスロー

Exception in thread "main" java.lang.UnsatisfiedLinkError: org.yukkuri.jyl.base.swing.JylF rame.testDot(II)V at org.yukkuri.jyl.base.swing.JylFrame.testDot(Native Method) at org.yukkuri.jyl.base.swing.JylFrame.<init>(JylFrame.java:18) at Test.main(Test.java:7)

2つ目 ドットが表示されない
なし

該当のソースコード

C++(C?)

1#include <windows.h> 2#include <tchar.h> 3 4#include "jni.h" 5#include "org_yukkuri_jyl_base_swing_JylFrame.h" 6 7static HWND hwnd; 8HDC hdc; 9PAINTSTRUCT ps; 10 11static int R = 255; 12static int G = 255; 13static int B = 255; 14 15LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); 16 17JNIEXPORT void JNICALL Java_org_yukkuri_jyl_base_swing_JylFrame_nInitFrame( JNIEnv *env, jobject obj, jint x, jint y, jint width, jint height ) 18{ 19 HINSTANCE hInstance = GetModuleHandle( 0 ); 20 21 WNDCLASS wnd; MSG msg; 22 23 wnd.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // 縦、横方向にサイズ変更時再描画 24 wnd.lpfnWndProc = WndProc; 25 wnd.cbClsExtra = wnd.cbWndExtra; 26 wnd.hInstance = hInstance; 27 wnd.hIcon = LoadIcon( NULL, IDI_APPLICATION ); 28 wnd.hCursor = LoadCursor( NULL, IDC_ARROW ); 29 wnd.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH ); 30 wnd.lpszMenuName = NULL; 31 wnd.lpszClassName = ( "STATIC" ); 32 33 if( !RegisterClass( &wnd ) ){ 34 MessageBox( NULL, TEXT( "window option error" ), TEXT( "error" ), MB_ICONSTOP ); 35 return; 36 } 37 38 hwnd = CreateWindow( 39 TEXT( "STATIC" ), TEXT( "frame" ), 40 WS_OVERLAPPEDWINDOW, 41 (int)x, (int)y, 42 (int)width, (int)height, 43 NULL, NULL, 44 hInstance, NULL 45 ); 46 47 if( hwnd == NULL ){ 48 MessageBox( NULL, TEXT( "window error" ), TEXT( "error" ), MB_ICONSTOP ); 49 return; 50 } 51 52 ShowWindow( hwnd, SW_SHOW ); 53 54// MessageBox( NULL, TEXT( "window created" ), TEXT( "message" ), MB_ICONINFORMATION ); 55 56 while( GetMessage( &msg, NULL, 0, 0 ) ) DispatchMessage( &msg ); 57} 58 59LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) 60{ 61 switch( msg ){ 62 case WM_DESTROY: 63 PostQuitMessage( 0 ); 64 return 0; 65 } 66 return DefWindowProc( hwnd, msg, wParam, lParam ); 67} 68 69JNIEXPORT void JNICALL Java_yukkuri_jyl_base_swing_JylFrame_testDot( JNIEnv *env, jobject obj, jint x, jint y ) 70{ 71 hdc = GetDC( hwnd ); 72 73 SetPixel( hdc, (int)x, (int)y, RGB( R, G, B ) ); 74 75 ReleaseDC( hwnd, hdc ); 76}

Java

1package org.yukkuri.jyl.base.swing; 2 3public class JylFrame 4{ 5 private String title = "frame"; 6 private int x = 100; private int y = 100; 7 private int width = 640; private int height = 480; 8 9 static 10 { 11 System.load( System.getProperty( "user.dir" ) + "\native\windows\Frame.dll" ); 12 } 13 14 public JylFrame() 15 { 16 nInitFrame( x, y, width, height ); 17 18 testDot( 100, 100 ); 19 } 20 21 private native void nInitFrame( int x, int y, int width, int height ); 22 23 private native void testDot( int x, int y ); 24} 25

試したこと

1つ目は、パッケージに問題がある、と書いてあるのが見受けられましたが、パッケージには問題ないようです。
2つ目は、外部から描画関数を呼び出すために、関数ごとにGetDCを使用しています。

補足情報(FW/ツールのバージョンなど)

Windows、MinGW、Java8を使用しています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

回答内容については daisuke7 さんが回答されている内容が完全な回答になっています。testDot が呼び出されない理由は、以下のメッセージループで無限ループとなっているために Java 側に処理が戻っていないことです。

C

1 while( GetMessage( &msg, NULL, 0, 0 ) ) DispatchMessage( &msg ); 2```メインウィンドウが閉じられて nInitFrame から制御が戻った際に testDot は呼び出されますが、その時にはメインウィンドウが破棄されているため、処理は実行されません。 3 4この問題に対処するためには以下の 2 通りの手法があります。 5 6### 1.シングルスレッドの場合 7シングルスレッドの場合、ウィンドウプロシージャをコールバック形式で Java 側を呼び出す形を取る必要があります。実装方法の差異はありますが、この方法を突き詰めていけば、AWT、Swing または SWT のいずれかに行きつくかと思います。 8 9```Java 10public class JylFrame 11{ 12 private static final int x = 100; 13 private static final int y = 100; 14 private static final int width = 640; 15 private static final int height = 480; 16 private static final int WM_MOUSEMOVE = 0x0200; 17 18 static 19 { 20 System.load( System.getProperty( "user.dir" ) + "\frame.dll" ); 21 } 22 23 public static void main(String[] args) { 24 JylFrame frame = new JylFrame(); 25 frame.initFrame(); 26 frame.run(); 27 } 28 29 public void initFrame() 30 { 31 nInitFrame( x, y, width, height ); 32 } 33 34 public void wndProc(int msg, long wParam, long lParam) { 35 System.out.println( "nWndProc(" + msg + ", " + wParam + ", " + lParam + ")" ); 36 switch( msg ) { 37 case WM_MOUSEMOVE: 38 int x = (int)( lParam & 0xffff ); 39 int y = (int)( lParam >> 16 ); 40 testDot( x, y ); 41 break; 42 } 43 } 44 45 private native void run(); 46 47 private native void nInitFrame( int x, int y, int width, int height ); 48 49 private native void testDot( int x, int y ); 50}

C

1#include <windows.h> 2#include <tchar.h> 3 4#include <jni.h> 5#include "JylFrame.h" 6 7static HWND hwnd; 8static jobject jylFrame; 9 10const int R = 255; 11const int G = 255; 12const int B = 255; 13 14LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); 15 16JNIEXPORT void JNICALL Java_JylFrame_run( JNIEnv *env, jobject obj ) { 17 MSG msg; 18 while( GetMessage( &msg, NULL, 0, 0 ) ) { 19 TranslateMessage( &msg ); 20 DispatchMessage( &msg ); 21 } 22} 23 24JNIEXPORT void JNICALL Java_JylFrame_nInitFrame( JNIEnv *env, jobject obj, jint x, jint y, jint width, jint height ) { 25 HINSTANCE hInstance = GetModuleHandle( 0 ); 26 27 WNDCLASS wnd; 28 29 wnd.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // 縦、横方向にサイズ変更時再描画 30 wnd.lpfnWndProc = WndProc; 31 wnd.cbClsExtra = wnd.cbWndExtra; 32 wnd.hInstance = hInstance; 33 wnd.hIcon = LoadIcon( NULL, IDI_APPLICATION ); 34 wnd.hCursor = LoadCursor( NULL, IDC_ARROW ); 35 wnd.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH ); 36 wnd.lpszMenuName = NULL; 37 wnd.lpszClassName = TEXT( "JylFrame" ); 38 39 if( !RegisterClass( &wnd ) ){ 40 MessageBox( NULL, TEXT( "window option error" ), TEXT( "error" ), MB_ICONSTOP ); 41 return; 42 } 43 44 hwnd = CreateWindow( 45 TEXT( "JylFrame" ), TEXT( "frame" ), 46 WS_OVERLAPPEDWINDOW, 47 (int)x, (int)y, 48 (int)width, (int)height, 49 NULL, NULL, 50 hInstance, NULL 51 ); 52 53 if( hwnd == NULL ){ 54 MessageBox( NULL, TEXT( "window error" ), TEXT( "error" ), MB_ICONSTOP ); 55 return; 56 } 57 58 ShowWindow( hwnd, SW_SHOW ); 59 60 // Java クラスの参照を保持する。 61 jylFrame = (*env)->NewGlobalRef( env, obj ); 62} 63 64JNIEXPORT void JNICALL Java_JylFrame_testDot( JNIEnv *env, jobject obj, jint x, jint y ) { 65 HDC hdc = GetDC( hwnd ); 66 67 SetPixel( hdc, (int)x, (int)y, RGB( R, G, B ) ); 68 69 ReleaseDC( hwnd, hdc ); 70} 71 72LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { 73 JavaVM *jvm; 74 jsize ct; 75 JNIEnv *env; 76 77 switch( msg ){ 78 case WM_MOUSEMOVE: 79 // グローバルな JNIEnv を取得する。要 -ljvm.lib 80 JNI_GetCreatedJavaVMs( &jvm, 1, &ct ); 81 (*jvm)->GetEnv( jvm, (void **)&env, JNI_VERSION_1_8 ); 82 83 // nWndProc を取得する。 84 jclass jcls = (*env)->FindClass( env, "JylFrame" ); 85 jmethodID wndproc = (*env)->GetMethodID( env, jcls, "wndProc", "(IJJ)V" ); 86 87 (*env)->CallVoidMethod( env, jylFrame, wndproc, msg, (long long)wParam, (long long)lParam ); 88 break; 89 case WM_DESTROY: 90 // Java クラスの参照を解放する。 91 (*env)->DeleteGlobalRef( env, jylFrame ); 92 PostQuitMessage( 0 ); 93 return 0; 94 } 95 96 return DefWindowProc( hwnd, msg, wParam, lParam ); 97} 98```なお、C 側のコンパイルに必要な引数はかなり増えます。 99```CMD 100gcc -IC:\openjdk\include -IC:\openjdk\include\win32 frame.c -shared -o frame.dll -LC:\openjdk\lib -lgdi32 -ljvm

C ドライブ直下の openjdk フォルダにインストールしていると上記のような指定になります。C:\openjdk 部分は環境に合わせて読み替えてください。JVM のメソッドを直接呼び出しているために -ljvm が必須となります。また、jvm.lib の位置を知らせるために OpenJDK の lib フォルダのパスを -L オプションで指定する必要があります。

このサンプルのビルドに成功すれば、以下のようにマウスの軌跡をたどるアプリが完成します。
サンプル画面

2.マルチスレッドの場合

もう1つマルチスレッドのサンプルも作成しようと思ったのですが、回答に時間がかかるのでやめました。マルチスレッドのサンプルが必要であれば別途コメントしてください。

投稿2018/10/28 18:48

編集2018/10/28 18:50
atata0319

総合スコア881

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yukkuri

2018/10/29 11:15

gcc -Ijavaのパス\include -Ijavaのパス\include\win32 frame.c -shared -o frame.dll -Wl,-subsystem,windows -Ljavaのパス\lib -lgdi32 -ljvm とオプションを設定しましたが、 -ljvm が見つからないと出てしまいます。パスにjvm.libがあることは確認済みです。
atata0319

2018/10/29 16:41

gcc では -l でライブラリファイルを指定するのが標準的なやり方かなと思いますが、VC++ のようにライブラリファイルを -l なしで指定することも可能で、この場合はフルパスで指定することができます。-L も指定する必要はありません。いったん、これで試されてはいかがでしょうか? gcc -IC:\openjdk\include -IC:\openjdk\include\win32 frame.c -shared -o frame.dll -Wl,-subsystem,windows -lgdi32 C:\openjdk\lib\jvm.lib さらに gcc のビット数と jdk のビット数を一致させる必要があります。MinGW が 64 ビットで JDK が 32 ビットであるようなビット数が異なる場合、リンクに失敗します。 なお、DLL プロジェクトでは subsystem の指定はほとんど意味を持ちません。起動する exe ファイル側の設定に影響されるためです。Java をコンソールなしで起動する場合、java.exe ではなく javaw.exe で起動するとコンソールが表示されません。
yukkuri

2018/11/01 11:22

すいません、64bit出揃えたら、以下のエラーが出てしまいまして… AppData\Local\Temp\ccshkb4S.o:frame.c:(.text+0x299): undefined referen ce to 'imp_JNI__GetCreatedJavaVMs@12' (パスはユーザー名、C:を省略させていただいています)
atata0319

2018/11/01 16:29 編集

このメッセージが表示されるということはコンパイラが 32 ビットコンパイラを使用しているか 64 ビットコンパイラを 32 ビットで動作させている(-m32オプションを指定している)のかのどちらかです。64 ビットコンパイラを使用すると通常、関数末尾の @12 が付与されません。gcc もフルパスで指定してみるとかどうでしょうか?
yukkuri

2018/11/04 07:51

ありがとうございます。おかげでフレームを生成まで行きました。しかし、マウスをフレームに入れるとjavaが停止してしまい、思ったように動きません。エラーは出ていません。
atata0319

2018/11/04 15:13

C 側で例外が発生した際にエラーが表示されないのは JNI としては正常な動作となります。その際、カレントディレクトリに Java のログファイルが出力されているかと思いますが、そのファイルからエラー箇所を特定できるのであれば、質問する必要は多分ないと思います。これ以上は C 側のデバッグが必要になります。適当なデバッガを用意してステップ実行してもらうのが早いですね。MinGW で開発されているなら Eclipse CDT を用意するのが早いかもしれません。 https://qiita.com/t-tkd3a/items/39d9f9f64b6707332cbd どのコードでどのようにエラーが発生しているかを記載してください。
yukkuri

2018/11/04 15:34

ログファイルらしきものが見つからなかったのですが、どうしたらいいでしょうか。
atata0319

2018/11/04 15:37

デバッグしてどこに問題があるかを突き止めてください。行ごとに MessegeBox を配置するような手法でも良いです。
yukkuri

2018/11/04 16:00 編集

いろいろ調べてrun関数内で不正なグローバルまたはローカルアクセスがされている、ということでした。(ネイティブメゾットの追跡はしてくれないようです) 断定できたのは、この2行のどこかということです。 jclass jcls = (*env)->FindClass( env, "JylFrame" ); jmethodID wndproc = (*env)->GetMethodID( env, jcls, "wndProc", "(IJJ)V" );
atata0319

2018/11/04 16:05

それは原因を特定できていることになりません。ウィンドウプロシージャはメッセージループで処理されるため、必ず run メソッド内での処理となるためです。 まずは、どこで問題が発生しているかを特定するために、WM_MOUSEMOVE の処理を削除するなりして実行しないようにしてみてください。元々言われていたマウスが入るとエラーになるという話ですので、これで WM_MOUSEMOVE に記載している処理が問題かどうかが判断できます。
guest

0

Exception in thread "main" java.lang.UnsatisfiedLinkError: org.yukkuri.jyl.base.swing.JylFrame.testDot(II)V at org.yukkuri.jyl.base.swing.JylFrame.testDot(Native Method)

C

1JNIEXPORT void JNICALL Java_yukkuri_jyl_base_swing_JylFrame_testDot( JNIEnv *env, jobject obj, jint x, jint y ) 2

関数名に org がない。

投稿2018/10/24 14:00

daisuke7

総合スコア1563

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yukkuri

2018/10/24 14:05

エラーの発生、1つ目は解決しました!ありがとうございます!
daisuke7

2018/10/24 14:16

正直Win32 APIなんてもうほとんど覚えていないのですが、 CreateWindow/ShowWindow/(UpdateWindow)した直後にGetDCとかしても、 まだまともにウィンドウが出てないんじゃないでしょうか? ちょっと時間をおいてから testDot()してみるとか。
yukkuri

2018/10/24 14:23

5秒ほど待機してから呼び出してみましたが無理でした。
daisuke7

2018/10/24 14:41

あ、ネイティブ側でWindowsのメッセージループに入っちゃうから、Java側に制御戻ってこないのでは?
yukkuri

2018/10/24 14:44

あるかもしれませんね。
yukkuri

2018/10/24 14:55

追記:Windows.hのSleepを使用して検証した結果、5秒ほど待機しても無理でした。 また、そのままでは呼び出せないので(int,int)の似たような関数を使用しました。
daisuke7

2018/10/24 15:13

Java 側でスレッド作って、Java側はそのスレッドで動くようにして、元のスレッドはnInitFrame呼び出す(そのままメッセージループに入る)とかしないと駄目な気がします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問