Win7(64bit)에서 JAVA의 JNI를 이용하여, kernel32.dll로 부터 디스크 사용정보 가져오기

2011. 8. 31. 23:3399. 정리전 - IT/11. Java

----------------------------------
  01. 준비물
----------------------------------
1. JDK : 중요! 최신으로 32bit 자바를 받는다. 64bit 로 받으면 컴파일시 에러남
             http://www.oracle.com/technetwork/java/javase/downloads/index.html


2. 이클립스 (32bit용) - 자바를32bit로 받았기 때문에 (c의 64bit 컴파일 방법이 있지만 난 아무리 해도 잘 안됨)


3. VisualStudio

MICROSOFT_VISUAL_STUDIO_2010_ULTIMATE_[thethingy].5881703.TPB.t

                           

4. 시스템 환경변수에서 path 추가하기
    1)   변수명 : JAVA_HOME
          값       : D:\Program Files (x86)\Java\jdk1.7.0
    2)   path에 추가할 값
          %JAVA_HOME%\bin;%JAVA_HOME%\lib;
          %JAVA_HOME%\include;
          %JAVA_HOME%\include\win32;


----------------------------------
  02. JAVA 파일 만들기
----------------------------------
1. 위치 : D:\WWW\DiskSpace\src\com\donzbox\file\action
2. 파일명 : DiskSpace.java
package com.donzbox.file.action;

import java.io.File;

public class DiskSpace {
    public ClassLoader cl;
    public String dllPath;

    public DiskSpace() {
         /*
            cl = Thread.currentThread().getContextClassLoader();
            if (cl == null) {
                cl = ClassLoader.getSystemClassLoader();
            }
            dllPath = cl.getResource("").getPath().toString();
        */
        dllPath = new File("").getAbsolutePath() + File.separator + "DiskSpace.dll";
       
        System.out.println(dllPath);
        File file = new File(dllPath);
        if (file.isFile()) {
            try {
                System.load(dllPath);
                System.out.println("1) \"" + dllPath + "\" Library is loaded.");
            } catch (Exception e) {
                System.out.println("2) \"" + dllPath + "\" Library loaded is fail.");
            }
        } else {
            System.out.println("3) \"" + dllPath + "\" Library no exist.");
        }
    }
    public native String diskspace(String drv);
    public String getDiskSpace(String drv) throws Exception {
        return diskspace(drv);
    }
}


----------------------------------
  03. JAVAH 컴파일 하기
----------------------------------
1. 위치 : D:\WWW\DiskSpace\src
2. 컴파일명령어 (패키지 컴파일 이므로 패키지의 최상단에서 컴파일 함)
D:\WWW\DiskSpace\src>javah com.donzbox.file.action.DiskSpace

3. 생성된 파일 확인
D:\WWW\DiskSpace\src>dir
2011-09-04  오후 02:20    <DIR>          .
2011-09-04  오후 02:20    <DIR>          ..
2008-11-20  오후 08:16    <DIR>          com
2011-09-04  오후 02:20               568 com_donzbox_file_action_DiskSpace.h
               1개 파일               1,885 바이트
               3개 디렉터리   7,173,541,888 바이트 남음


----------------------------------
  04. c 파일 만들기
----------------------------------
1. 위치 : D:\WWW\DiskSpace\src
2. 파일명 : DiskSpace.c
#include <jni.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "DiskSpace.h"

typedef BOOL (WINAPI *PGETDISKFREESPACEEX)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);

JNIEXPORT jstring JNICALL Java_com_donzbox_file_action_DiskSpace_diskspace(JNIEnv* env, jobject obj, jstring msg) {

    // 읽어온 값을 C 데이터로 변환한다.
    const char *str = (*env)->GetStringUTFChars(env, msg, 0);
    double temp;
    char s1[33 + 1];
    char s2[33 + 1];

    LPCSTR pszDrive = str;
    PGETDISKFREESPACEEX pGetDiskFreeSpaceEx;
    __int64 i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes, i64ReturnResult;
    DWORD dwSectPerClust, dwBytesPerSect, dwFreeClusters, dwTotalClusters;
    BOOL fResult;
    pGetDiskFreeSpaceEx = (PGETDISKFREESPACEEX) GetProcAddress(GetModuleHandle("kernel32.dll"),    "GetDiskFreeSpaceExA");

    if (pGetDiskFreeSpaceEx) {
        fResult = pGetDiskFreeSpaceEx (pszDrive, (PULARGE_INTEGER)&i64FreeBytesToCaller, (PULARGE_INTEGER)&i64TotalBytes, (PULARGE_INTEGER)&i64FreeBytes);
        if(fResult) {
//            printf("Total free bytes = %I64d\n", i64FreeBytes);
            i64ReturnResult = i64FreeBytes;
        }
    } else {
        fResult = GetDiskFreeSpaceA (pszDrive, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters);
        if(fResult) {
//            printf("Total free bytes = %I64d\n", dwFreeClusters*dwSectPerClust*dwBytesPerSect);
            i64ReturnResult = dwFreeClusters*dwSectPerClust*dwBytesPerSect;
        }
    }
    temp = i64TotalBytes / 1024 / 1024 / 1024;
    ltoa(temp, s1, 10);
    temp = i64ReturnResult / 1024 / 1024 / 1024;
    ltoa(temp, s2, 10);

    (*env)->ReleaseStringUTFChars(env, msg, str);
    return (*env)->NewStringUTF(env, strcat(strcat(s1,"|"),s2));
}


----------------------------------
  05. JNI 컴파일 하기
----------------------------------
1. 위치 : D:\WWW\DiskSpace\src
2. 이름 맞추기 (아래와 같이 작성된 c파일과 같은 이름으로 javah로 생성한 파일의 이름을 변경한다)
D:\WWW\DiskSpace\src>rename com_donzbox_file_action_DiskSpace.h DiskSpace.h

D:\WWW\DiskSpace\src>dir
2011-09-04  오후 03:17    <DIR>          .
2011-09-04  오후 03:17    <DIR>          ..
2008-11-20  오후 08:16    <DIR>          com
2007-01-29  오후 06:23             1,654 DiskSpace.c
2011-09-04  오후 02:20               568 DiskSpace.h
               2개 파일               3,539 바이트
               3개 디렉터리   7,173,050,368 바이트 남음

3. c 컴파일 (따옴표에 주의할 것 : 스페이스가 들어간거 따옴표에 넣어주면 패스로 인식)
다음과 같이 c의 라이브러리가 전부 적용되어있는 프롬프트를 연다.




※ " 따옴표 주의 " ※

D:\WWW\DiskSpace\src>cl -I"D:\Program Files (x86)\Java\jdk1.7.0\include" -I"D:\Program Files (x86)\Java\jdk1.7.0\include\win32" -LD DiskSpace.c

Microsoft (R) C/C++ Optimizing Compiler Version 16.00.30319.01 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

DiskSpace.c
Microsoft (R) Incremental Linker Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:DiskSpace.dll
/dll
/implib:DiskSpace.lib
DiskSpace.obj
   Creating library DiskSpace.lib and object DiskSpace.exp

D:\WWW\DiskSpace\src>dir
2011-09-04  오후 03:52    <DIR>          .
2011-09-04  오후 03:52    <DIR>          ..
2008-11-20  오후 08:16    <DIR>          com
2007-01-29  오후 06:23             1,654 DiskSpace.c
2011-09-04  오후 03:52            50,688 DiskSpace.dll
2011-09-04  오후 03:52               716 DiskSpace.exp
2011-09-04  오후 02:20               568 DiskSpace.h
2011-09-04  오후 03:52             1,926 DiskSpace.lib
2011-09-04  오후 03:52             2,167 DiskSpace.obj
               6개 파일              63,644 바이트
               3개 디렉터리   7,172,358,144 바이트 남음


--------------------------------------------------------------------
  06. 생성된 DLL을 JAVA에서 불러 동작확인
-------------------------------------------------------------------- 
1. DLL 복사 위치 : D:\WWW\DiskSpace (이클립스에서 실행시 이곳을 봅니다)
D:\WWW\DiskSpace>dir
2011-09-04  오후 07:26    <DIR>          .
2011-09-04  오후 07:26    <DIR>          ..
2008-11-20  오후 08:16               299 .classpath
2008-11-20  오후 08:16               385 .project
2010-07-31  오후 03:12    <DIR>          .settings
2011-08-26  오후 09:21    <DIR>          bin
2011-09-04  오후 07:26            50,688 DiskSpace.dll
2011-09-04  오후 07:26    <DIR>          src
               4개 파일              56,013 바이트
               4개 디렉터리   7,289,778,176 바이트 남음

2. DLL 복사 위치 : D:\WWW\DiskSpace\bin (커맨드 모드에서 실행시 이곳을 봅니다)
D:\WWW\DiskSpace\bin>dir
2011-09-04  오후 10:40    <DIR>          .
2011-09-04  오후 10:40    <DIR>          ..
2011-09-04  오후 10:21    <DIR>          com
2011-09-04  오후 08:15            50,688 DiskSpace.dll
               1개 파일              50,688 바이트
               3개 디렉터리   7,290,695,680 바이트 남음

3. 위치 : D:\WWW\DiskSpace\src\com\donzbox\file\action
    파일명 : DiskSpaceTest.java
package com.donzbox.file.action;

import java.text.DecimalFormat;

public class DiskSpaceTest {

    private static final String DRIVE_C = "C:";
    private static final String DRIVE_D = "D:";
    private static DiskSpace ds = new DiskSpace();

    public static void main (String [] args) throws Exception {
       
        String diskSize = ds.getDiskSpace(DRIVE_C);
        System.out.println(diskSize);
        System.out.println(DRIVE_C + " Total : " + diskSize.split("\\|")[0] + "GB");
        System.out.println(DRIVE_C + " Free  : " + diskSize.split("\\|")[1] + "GB\r\n");

        diskSize = ds.getDiskSpace(DRIVE_D);
        System.out.println(DRIVE_D + " Total : " + diskSize.split("\\|")[0] + "GB");
        System.out.println(DRIVE_D + " Free  : " + diskSize.split("\\|")[1] + "GB\r\n");
    }
}
--------------------
  커맨드 모드로 실행
--------------------
위치 : D:\WWW\DiskSpace\bin
실행 : java com.donzbox.file.action.DiskSpaceOutput

--------------------
  결과
--------------------
D:\WWW\DiskSpace\DiskSpace.dll
29|6
C: Total : 29GB
C: Free  : 6GB

D: Total : 249GB
D: Free  : 6GB


▶ 참고사항 ◀
----------------------------------------
  WAS에서 JNI 2중 로드 방지법
----------------------------------------
1. dll을 부르는 클레스 파일의 dll파일 로딩시 직접경로로 System.load(DLL_FILE); 로드

2. dll을 부르는 클레스를 jar로 만들어 프로젝트의 최상단 디렉토리에 위치시킨다.

3. tomcat의 bin디렉토리의 setclasspath.bat의 set CLASSPATH= 를 찾아
   set CLASSPATH=%JAVA_HOME%\lib\tools.jar;C:\EZF_0219\DonzBox.net\DiskSpace.jar;
   요롷게 추가해 준다.

4. dll 파일도 프로젝트의 최상단에 위치시킨다.(tomcat의 리로드시 재로딩 방지)

5. jar로 만든 원본 클레스의 이름을 바꾼다.

6. 이름이 바뀜으로서 연결되어 있는 클레스 파일들을 jar로 만든 원본 클레스의 이름과 같이 소스를 수정

7. 프로젝트 속성의 라이브러리 추가에서 jar로 만든 파일을 추가

 


▶ 64bit 운영체제에서 64bit로 컴파일 ◀
----------------------------------------
  윈도우8 64bit에 VisualStudio2012 에서 적용하여 보았다
----------------------------------------
01. JDK 를 64bit 용으로 인스톨, VisualStudio2012(32, 64bit 구분없음) 인스톨

02. 상기의 c컴파일시
    


03. 64bit로 환경설정된 창을 열고 상기와 똑같이
    

04.  출력된 dll 을 classes 로 이동