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

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

ただいまの
回答率

90.35%

  • Java

    15062questions

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

  • C++

    4070questions

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

  • JNI

    29questions

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

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

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 270

yukkuri

score 492

 前提・実現したいこと

現在、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つ目 ドットが表示されない
なし

 該当のソースコード

#include <windows.h>
#include <tchar.h>

#include "jni.h"
#include "org_yukkuri_jyl_base_swing_JylFrame.h"

static HWND hwnd;
HDC hdc;
PAINTSTRUCT ps;

static int R = 255;
static int G = 255;
static int B = 255;

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );

JNIEXPORT void JNICALL Java_org_yukkuri_jyl_base_swing_JylFrame_nInitFrame( JNIEnv *env, jobject obj, jint x, jint y, jint width, jint height )
{
    HINSTANCE hInstance = GetModuleHandle( 0 );

    WNDCLASS wnd; MSG msg;

    wnd.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;        //    縦、横方向にサイズ変更時再描画
    wnd.lpfnWndProc = WndProc;
    wnd.cbClsExtra = wnd.cbWndExtra;
    wnd.hInstance = hInstance;
    wnd.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wnd.hCursor = LoadCursor( NULL, IDC_ARROW );
    wnd.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
    wnd.lpszMenuName = NULL;
    wnd.lpszClassName = ( "STATIC" );

    if( !RegisterClass( &wnd ) ){
        MessageBox( NULL, TEXT( "window option error" ), TEXT( "error" ), MB_ICONSTOP );
        return;
    }

    hwnd = CreateWindow(
        TEXT( "STATIC" ), TEXT( "frame" ),
        WS_OVERLAPPEDWINDOW,
        (int)x, (int)y,
        (int)width, (int)height,
        NULL, NULL,
        hInstance, NULL
    );

    if( hwnd == NULL ){
        MessageBox( NULL, TEXT( "window error" ), TEXT( "error" ), MB_ICONSTOP );
        return;
    }

    ShowWindow( hwnd, SW_SHOW );

//    MessageBox( NULL, TEXT( "window created" ), TEXT( "message" ), MB_ICONINFORMATION );

    while( GetMessage( &msg, NULL, 0, 0 ) ) DispatchMessage( &msg );
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg ){
        case WM_DESTROY:
            PostQuitMessage( 0 );
            return 0;
    }
    return DefWindowProc( hwnd, msg, wParam, lParam );
}

JNIEXPORT void JNICALL Java_yukkuri_jyl_base_swing_JylFrame_testDot( JNIEnv *env, jobject obj, jint x, jint y )
{
    hdc = GetDC( hwnd );

    SetPixel( hdc, (int)x, (int)y, RGB( R, G, B ) );

    ReleaseDC( hwnd, hdc );
}
package org.yukkuri.jyl.base.swing;

public class JylFrame
{
    private String title = "frame";
    private int x = 100; private int y = 100;
    private int width = 640; private int height = 480;

    static
    {
        System.load( System.getProperty( "user.dir" ) + "\\native\\windows\\Frame.dll" );
    }

    public JylFrame()
    {
        nInitFrame( x, y, width, height );

        testDot( 100, 100 );
    }

    private native void nInitFrame( int x, int y, int width, int height );

    private native void testDot( int x, int y );
}

 試したこと

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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

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

    while( GetMessage( &msg, NULL, 0, 0 ) ) DispatchMessage( &msg );

メインウィンドウが閉じられて nInitFrame から制御が戻った際に testDot は呼び出されますが、その時にはメインウィンドウが破棄されているため、処理は実行されません。

この問題に対処するためには以下の 2 通りの手法があります。

 1.シングルスレッドの場合

シングルスレッドの場合、ウィンドウプロシージャをコールバック形式で Java 側を呼び出す形を取る必要があります。実装方法の差異はありますが、この方法を突き詰めていけば、AWT、Swing または SWT のいずれかに行きつくかと思います。

public class JylFrame
{
    private static final int x = 100;
    private static final int y = 100;
    private static final int width = 640;
    private static final int height = 480;
    private static final int WM_MOUSEMOVE = 0x0200;

    static
    {
        System.load( System.getProperty( "user.dir" ) + "\\frame.dll" );
    }

    public static void main(String[] args) {
        JylFrame frame = new JylFrame();
        frame.initFrame();
        frame.run();
    }

    public void initFrame()
    {
        nInitFrame( x, y, width, height );
    }

    public void wndProc(int msg, long wParam, long lParam) {
        System.out.println( "nWndProc(" + msg + ", " + wParam + ", " + lParam + ")" );
        switch( msg ) {
            case WM_MOUSEMOVE:
                int x = (int)( lParam & 0xffff );
                int y = (int)( lParam >> 16 );
                testDot( x, y );
                break;
        }
    }

    private native void run();

    private native void nInitFrame( int x, int y, int width, int height );

    private native void testDot( int x, int y );
}
#include <windows.h>
#include <tchar.h>

#include <jni.h>
#include "JylFrame.h"

static HWND hwnd;
static jobject jylFrame;

const int R = 255;
const int G = 255;
const int B = 255;

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );

JNIEXPORT void JNICALL Java_JylFrame_run( JNIEnv *env, jobject obj ) {
    MSG msg;
    while( GetMessage( &msg, NULL, 0, 0 ) ) {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
}

JNIEXPORT void JNICALL Java_JylFrame_nInitFrame( JNIEnv *env, jobject obj, jint x, jint y, jint width, jint height ) {
    HINSTANCE hInstance = GetModuleHandle( 0 );

    WNDCLASS wnd;

    wnd.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;        //    縦、横方向にサイズ変更時再描画
    wnd.lpfnWndProc = WndProc;
    wnd.cbClsExtra = wnd.cbWndExtra;
    wnd.hInstance = hInstance;
    wnd.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wnd.hCursor = LoadCursor( NULL, IDC_ARROW );
    wnd.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
    wnd.lpszMenuName = NULL;
    wnd.lpszClassName = TEXT( "JylFrame" );

    if( !RegisterClass( &wnd ) ){
        MessageBox( NULL, TEXT( "window option error" ), TEXT( "error" ), MB_ICONSTOP );
        return;
    }

    hwnd = CreateWindow(
        TEXT( "JylFrame" ), TEXT( "frame" ),
        WS_OVERLAPPEDWINDOW,
        (int)x, (int)y,
        (int)width, (int)height,
        NULL, NULL,
        hInstance, NULL
    );

    if( hwnd == NULL ){
        MessageBox( NULL, TEXT( "window error" ), TEXT( "error" ), MB_ICONSTOP );
        return;
    }

    ShowWindow( hwnd, SW_SHOW );

    // Java クラスの参照を保持する。
    jylFrame = (*env)->NewGlobalRef( env, obj );
}

JNIEXPORT void JNICALL Java_JylFrame_testDot( JNIEnv *env, jobject obj, jint x, jint y ) {
    HDC hdc = GetDC( hwnd );

    SetPixel( hdc, (int)x, (int)y, RGB( R, G, B ) );

    ReleaseDC( hwnd, hdc );
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) {
    JavaVM *jvm;
    jsize ct;
    JNIEnv *env;

    switch( msg ){
        case WM_MOUSEMOVE:
            // グローバルな JNIEnv を取得する。要 -ljvm.lib
            JNI_GetCreatedJavaVMs( &jvm, 1, &ct );
            (*jvm)->GetEnv( jvm, (void **)&env, JNI_VERSION_1_8 );

            // nWndProc を取得する。
            jclass jcls = (*env)->FindClass( env, "JylFrame" );
            jmethodID wndproc = (*env)->GetMethodID( env, jcls, "wndProc", "(IJJ)V" );

            (*env)->CallVoidMethod( env, jylFrame, wndproc, msg, (long long)wParam, (long long)lParam );
            break;
        case WM_DESTROY:
            // Java クラスの参照を解放する。
            (*env)->DeleteGlobalRef( env, jylFrame );
            PostQuitMessage( 0 );
            return 0;
    }

    return DefWindowProc( hwnd, msg, wParam, lParam );
}

なお、C 側のコンパイルに必要な引数はかなり増えます。

gcc -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/29 20:15

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

    キャンセル

  • 2018/10/30 01: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 で起動するとコンソールが表示されません。

    キャンセル

  • 2018/11/01 20:22

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

    キャンセル

  • 2018/11/01 23:47 編集

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

    キャンセル

  • 2018/11/04 16:51

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

    キャンセル

  • 2018/11/05 00:13

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

    どのコードでどのようにエラーが発生しているかを記載してください。

    キャンセル

  • 2018/11/05 00:34

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

    キャンセル

  • この投稿は削除されました

  • 2018/11/05 00:37

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

    キャンセル

  • 2018/11/05 00:56 編集

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

    キャンセル

  • 2018/11/05 01:05

    それは原因を特定できていることになりません。ウィンドウプロシージャはメッセージループで処理されるため、必ず run メソッド内での処理となるためです。

    まずは、どこで問題が発生しているかを特定するために、WM_MOUSEMOVE の処理を削除するなりして実行しないようにしてみてください。元々言われていたマウスが入るとエラーになるという話ですので、これで WM_MOUSEMOVE に記載している処理が問題かどうかが判断できます。

    キャンセル

+2

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)
JNIEXPORT void JNICALL Java_yukkuri_jyl_base_swing_JylFrame_testDot( JNIEnv *env, jobject obj, jint x, jint y )

関数名に org がない。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/24 23:05

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

    キャンセル

  • 2018/10/24 23:16

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

    キャンセル

  • 2018/10/24 23:23

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

    キャンセル

  • 2018/10/24 23:41

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

    キャンセル

  • 2018/10/24 23:44

    あるかもしれませんね。

    キャンセル

  • 2018/10/24 23:55

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

    キャンセル

  • 2018/10/25 00:13

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

    キャンセル

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

  • ただいまの回答率 90.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Java

    15062questions

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

  • C++

    4070questions

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

  • JNI

    29questions

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