자바 JNI의 원리는 다음과 같다.


-> 자바파일 안에 호출하려고 하는 c함수에 대한 선언문과 호출문, 그리고 dll 로드문을 작성한다.


-> native call을 하려는 c함수에 대한 정의문 및 헤더파일을 작성한다.


-> 만들어진 c파일을 dll로 빌드한다.


-> 빌드한 dll을 자바코드에서 호출해서 만든 c함수를 자바에서 사용할 수 있다.



---------------------------------------------------------------------------------------------------------



1. 자바 프로젝트를 생성한다.



2. 자바 파일에 JNI 호출을 위한 코드를 작성 한다.





public class HelloWorld {


private native void print();


static {

System.loadLibrary("Native");

}


public static void main(String[] args) {

new HelloWorld().print();

}


}


* 여기서 print메서드 선언은 c코드로 구현해야 할 함수 부분에 대한 native 선언문이다.


* SystemloadLibrary 메서드를 사용해 c로 컴파일된 dll파일을 읽는다. (dll파일의 이름은 Native가 됨)



3. 자바코드 컴파일


이클립스의 경우 원래 저장만 해도 자동으로 컴파일이 되므로, 프로젝트폴더/bin/패키지명/ 에 가보면 class파일이


들어져 있을 것이다. 없으면 javac로 만들어주자.


> javac HelloWorld.java




4. 헤더파일 작성


c코드 작성에 앞서 헤더파일부터 만들어줘야 한다. javah를 이용해 만든다.


이 때 javah의 대상은 HelloWorld.class파일이므로 javah명령은 bin/패키지명/ 내에서 수행한다.


> javah HelloWorld


여기까지 아무 이상 없이 실행되었다면 다음 그림처럼 헤더 파일이 만들어질 것이다.





여기서 만약 javah 명령을 수행하는 과정에서 다음과 같은 에러가 발생한다면


Error : Could not find class file for 'HelloWorld'.


다음 글을 참고하자.


글 참고 : http://huammmm1.tistory.com/443



5. C 함수 몸체 코드 작성


이제 헤더파일에 대한 C 함수 몸체를 작성한다. 대충 메모장으로 다음과 같이 입력해주자.


#include<jni.h>

#include "jni_HelloWorld.h"

#include<stdio.h>

JNIEXPORT void JNICALL Java_jni_HelloWorld_print(JNIEnv *env, jobject obj)

{

printf("Hello world!\n");

return;

}


* #include "jni_HelloWorld.h" 부분은 자신이 생성한 헤더파일의 이름으로 놓는다

* 함수명 및 리턴형식, 인자형식은 자신이 생성한 헤더파일 내의 함수 선언문을 참고로 작성하면 된다


그리고 아무 이름으로 c파일로 저장하고, 헤더파일과 동일한 bin/패키지명/에 놔주자.(편의상 HelloWorld.c로 저장)


6. dll파일 생성

이제 만들어진 c파일을 토대로 dll파일을 생성하면 된다. 자바로 JNI를 이용하는 과정 중에 제일 어려운 단계이다.

dll파일을 생성하는 방법은 대부분의 JNI 관련 게시물들이 비주얼 스튜디오에서 dll 프로젝트를 사용해 만드는 방법을

보여주고 있는데, 나의 경우는 비주얼 스튜디오에서 제공하는 cl컴파일러를 사용해 dll파일을 만들었다.

* 하고 나서 느끼는 건데 cl컴파일러를 사용한 방법이 그렇게 어려운 방법이 아니었다

* 환경은 windows로 가정하였으며 다른 환경에 대한 정보는 본 글에서 제공하지 않음



먼저 윈도우 키를 누르고 명령 프롬프트를 검색하자.





그리고 맨 위에 보이는 VS용 개발자 명령 프롬프트를 실행 한다.


비주얼 스튜디오에서는 cl이라는 컴파일러를 사용하는데, 일반 명령 프롬프트에서는 환경변수나 path같은게 잡혀있지


않다. 이에 비해 VS용 개발자 명령 프롬프트에서는 cl과 관련된 기본적인 환경변수 및 path가 셋팅되어있다고 한다.


실행할때는 관리자 권한으로 실행하자. (그렇지 않으면 obj 만들 때 permission denied 문구를 보게 될 수 있다)


실행했으면 cl을 입력해서 컴파일러가 제대로 동작하는지 확인하자.





이제 앞에서 만들어둔 .c파일이 위치한 path로 이동하자. (이 글에서는 bin/패키지명)





이제 다음 명령어를 사용해 dll파일을 만들어본다.


>cl -I"C:\jdk1.7\include" -I"C:\jdk1.7\include\win32" -LD HelloWorld.c -FeNative.dll


-I 옵션은 include할 파일들에 대한 디렉토리를 지정하는데 사용한다.


-LD 옵션은 dynamic-link library를 만들 때 사용한다


-Fe 옵션은 실행 파일의 이름을 지정한 이름으로 바꿀 때 사용한다


그러니까.. 위의 명령을 요약하면 cl컴파일러를 이용해서 HelloWorld.c 파일을 DLL로 만들되, 이 때 파일 이름은


Native.dll로 하고, dll파일을 만들 때 include해야 할 파일들은 -I 옵션 뒤에 명시된 디렉토리들에서 참고하라는 의미


가 된다.



* 여기서 -I는 대문자 아이이다.


* cl에서 l은 소문자 엘이다. (처음에 이거때문에 약간 혼란)


* -I 옵션 뒤의 path는 당연하겠지만 자신이 설치한 jdk의 path로 설정해야 한다


* -Fe 옵션과 Native.dll이 띄어쓰기가 안되어있다. 근데 이게 맞게 한거다. -_- (한 번 더 혼란)


* -I 옵션 뒤는 띄어줘도 된다.


* -I 옵션 뒤를 "" 큰따옴표로 묶은 것은 path에 스페이스가 들어가는 경우에도 전체를 하나의 올바른 path로 인식하게


  하기 위해서이다





잘 만들어진 것 같다. 이제 bin/jni 폴더로 가보면 Native.dll 파일이 생성되어있을 것이다. 





Native.dll 파일을 복사한 후 프로젝트 폴더의 루트에 복사해주자.





이제 이클립스로 돌아와 실행을 시켜보자.





그리고 에러가 발생 한다.


사람에 따라 에러가 발생하지 않을 수 있는데, 내 컴퓨터에 에러가 발생한 이유는 만들어진 dll파일은 32비트인데, 


사용 하는 JVM은 64비트여서 그런 것이다.


이 때 해결방법은 두 가지가 있는데, 첫째는 이클립스에서 JVM을 32비트버전을 사용하도록 바꿔주는 것이고, 


두번째는 dll파일을 64비트로 빌드하는 것이다.


나 같은 경우는 첫 번째 방법이 귀찮아서 두 번째 방법으로 해결했다


* 그리고 dll파일을 64비트로 빌드하는 것은 의외로 어렵지 않았다


* 첫 번째 방법은 다음 글을 참조 : http://huammmm1.tistory.com/445



dll파일을 64비트로 빌드하는 방법은 다시 두 가지로 나뉜다. (시간이 없는 분들은 2번만 보면 된다)



1) vcvarsall.bat 파일을 이용하여 컴파일러를 64bit용으로 체인지해서 사용하는 방법


vs에서는 x86용, x64용, arm용 등 여러 버전의 컴파일러를 제공하고 있다. 그리고 vcvarsall.bat 파일을 사용하면 32비


트나 64비트, 혹은 크로스 컴파일을 위한 커맨드 라인을 configure할 수 있도록 환경 변수를 쉽게 설정할 수 있다.


64bit 플랫폼 위에서의 64bit output을 목적으로 빌드하기 위해서는 vcvarsall.bat의 인수로 amd64를 주면 된다.


먼저 vcvarsall.bat 파일은 다음 위치 경로에 존재한다.


C:\Program Files (x86)\Microsoft Visual Studio version\VC


cd로 해당 디렉토리로 이동한 후 다음과 같이 실행 한다.





이제 아까처럼 cl 컴파일러를 사용해 다시 dll파일을 만들고 프로젝트 루트에 복사 후 실행해보면 실행이 잘 된다.


>cl -I"C:\jdk1.7\include" -I"C:\jdk1.7\include\win32" -LD HelloWorld.c -FeNative.dll





* 첫 번째 방법에 대한 정확하고 자세한 방법은 아래 msdn을 참조


참고자료 : http://msdn.microsoft.com/en-us/library/f2ccy3wt.aspx


참고자료 : http://msdn.microsoft.com/en-us/library/x4d2c09s.aspx



2) x64용 명령 프롬프트를 이용하는 방법


사실 첫 번째 방법으로 직접 바꿀 필요도 없이 비주얼 스튜디오에서 제공하는 x64용 명령 프롬프트를 사용하면 된다.


* 나는 첫 번째 방법을 안 이후 두 번째 방법을 알게 되었다.





이번에는 VS용 x64 네이티브 도구 명령 프롬프트를 실행한다. (혹은 자신이 원하는 플랫폼 및 빌드 output에 따라 적


절한 명령 프롬프트를 선택하면 된다)


그리고 아까와 같이 cl 컴파일러를 사용해 dll파일을 만들면 64비트용으로 잘 만들어지고 프로젝트 루트에 복사 후


실행해보면 출력이 잘 나오는 것을 확인할 수 있다. 끝


>cl -I"C:\jdk1.7\include" -I"C:\jdk1.7\include\win32" -LD HelloWorld.c -FeNative.dll



참고)


자신이 만든 dll파일이 32bit dll인지 64bit dll인지 확인하려면?


관련 글 : http://huammmm1.tistory.com/451








Posted by huammmm1
,