자바 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;
}
먼저 윈도우 키를 누르고 명령 프롬프트를 검색하자.
그리고 맨 위에 보이는 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
'Java > 공개글' 카테고리의 다른 글
자바에서 인라인 어셈블리를 해보자 (2) | 2014.07.22 |
---|---|
Can't load IA 32-bit .dll on a AMD 64-bit platform (1) | 2014.07.22 |
[JNI] javah could not find class file (0) | 2014.07.21 |
자바/이클립스에서의 Carriage Return, Line Feed에 대해서 (0) | 2014.02.28 |
참조형의 캐스팅에 대해서 (0) | 2013.07.13 |