2011-01-14 19 views
16

PHẦN 1

Tôi đang phát triển một ứng dụng Java sẽ được phát hành dưới dạng một cái bình. Chương trình này phụ thuộc vào các thư viện bên ngoài C++ được gọi là JNI. Để tải chúng, tôi sử dụng phương pháp System.load với đường dẫn tuyệt đối và hoạt động tốt.Java - Tải dll theo đường dẫn tương đối và ẩn chúng trong một cái bình

Tuy nhiên, tôi thực sự muốn "ẩn" chúng bên trong JAR, vì vậy tôi đã tạo một gói để thu thập chúng. Điều này buộc tôi phải tải một đường dẫn tương đối - đường dẫn gói. Bằng cách tiếp cận này, tôi cho phép người dùng chạy JAR trong bất kỳ thư mục nào, mà không phải lo lắng về việc liên kết các tệp DLL hoặc chán với một quá trình cài đặt trước đó.

này ném ngoại lệ dự kiến:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Expecting an absolute path of the library

Làm thế nào tôi có thể làm việc này?

PHẦN 2

Cách tiếp cận của sao chép các file DLL vào một thư mục (giải thích dưới đây) chỉ hoạt động khi tôi chạy nó dưới môi trường thực. Chạy một JAR xuất khẩu, những chương trình DLL cũng được tạo nhưng tải một JNI ném ngoại lệ tiếp theo:

Exception in thread "main" java.lang.reflect.InvocationTargetException

at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56) 
Caused by: java.lang.UnsatisfiedLinkError: C:\Users\Supertreta\Desktop\nm files\temp\jniBin.dll: Can't find dependent libraries at java.lang.ClassLoader$NativeLibrary.load(Native Method) 

tôi chạy phương pháp nạp này:

public static void loadBinaries(){ 
     String os = System.getProperty("os.name").toLowerCase(); 

     if(os.indexOf("win") >= 0){ 
      ArrayList<String> bins = new ArrayList<String>(){{ 
       add("/nm/metadata/bin/dependence1.dll"); 
       add("/nm/metadata/bin/dependence2.dll"); 
       add("/nm/metadata/bin/dependence3.dll"); 
       add("/nm/metadata/bin/dependence4.dll"); 
       add("/nm/metadata/bin/jniBin.dll"); 
      }}; 

      File f = null; 
      for(String bin : bins){ 
       InputStream in = FileManager.class.getResourceAsStream(bin); 
       byte[] buffer = new byte[1024]; 
       int read = -1; 
       try { 
        String[] temp = bin.split("/"); 
        f = new File(TEMP_FOLDER, temp[temp.length-1]);  
        FileOutputStream fos = new FileOutputStream(f); 

        while((read = in.read(buffer)) != -1) { 
         fos.write(buffer, 0, read); 
        } 
        fos.close(); 
        in.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 

      System.load(f.getAbsolutePath()); 
     } 
    } 

Tôi nghĩ rằng đây có thể là một vấn đề đặc quyền truy cập , nhưng không biết cách giải quyết nó. Bạn nghĩ sao?

+0

Tại sao bạn muốn ẩn chúng? –

+0

Để cung cấp một bình sạch – supertreta

Trả lời

21

Tôi không tin rằng bạn có thể tải DLL trực tiếp từ JAR. Bạn phải thực hiện bước trung gian để sao chép DLL ra khỏi JAR. Các mã sau đây nên làm điều đó:

public static void loadJarDll(String name) throws IOException { 
    InputStream in = MyClass.class.getResourceAsStream(name); 
    byte[] buffer = new byte[1024]; 
    int read = -1; 
    File temp = File.createTempFile(name, ""); 
    FileOutputStream fos = new FileOutputStream(temp); 

    while((read = in.read(buffer)) != -1) { 
     fos.write(buffer, 0, read); 
    } 
    fos.close(); 
    in.close(); 

    System.load(temp.getAbsolutePath()); 
} 
+0

Điều này dường như là một cách nhanh chóng và dễ dàng để giải quyết vấn đề này. Tôi đã thử nó nhưng có một vấn đề: phương thức createTempFile gắn thêm một số vào tên của tệp, tức là lib "hello.dll" trở thành "hello.dll4975093656535427331", ngay cả với các tham số này (tên, "").Dll JNI chính của tôi cũng phụ thuộc vào các dll khác nên tôi phải biết về tên. Bạn có biết làm thế nào tôi có thể đối phó với điều này? – supertreta

+0

Có, chỉ cần thay đổi cách 'File temp' được khởi tạo:' File temp = new File (tệp mới (System.getProperty ("java.io.tmpdir")), name) '; –

+0

Bây giờ tôi sao chép tất cả các tập tin dll nhưng khi tôi tải một JNI tôi nhận được ngoại lệ tiếp theo: Ngoại lệ trong thread "main" java.lang.UnsatisfiedLinkError: C: \ Documents and Settings \ Administrator \ Local Settings \ Temp \ hello.dll : Ứng dụng này đã không khởi động được do cấu hình ứng dụng không chính xác. Cài đặt lại các ứng dụng có thể khắc phục vấn đề này. Tôi đã xác nhận rằng tất cả các thư viện nằm trong thư mục tạm thời ... – supertreta

2

Về cơ bản, thao tác này sẽ hoạt động. Vì đây là cách JNA làm điều đó, chỉ cần tải xuống và nghiên cứu mã. Bạn thậm chí có một số gợi ý để làm cho nền tảng này độc lập ...

EDIT

JNA mang mã gốc của nó cùng trong jar, giải nén nhị phân chính xác tại thời gian chạy tải und nó. Điều này có thể là một mô hình tốt để làm theo (nếu tôi có câu hỏi của bạn chính xác).

+3

Xin lỗi, bạn đã bỏ lỡ điều gì đó về câu trả lời của bạn? Với "điều này" bạn đề cập đến cái gì? thanks – supertreta

-1

Bạn cần phải xếp hạng bộ nạp lớp với vị trí của DLL - nhưng nó có thể được nạp mà không cần giải nén nó khỏi bình. Một cái gì đó đơn giản trước khi cuộc gọi tải được thực hiện là đủ. Trong lớp học chính của bạn thêm:

static { 
    System.loadLibrary("resource/path/to/foo"); // no .dll or .so extension! 
} 

Đáng chú ý, về cơ bản, tôi có cùng một số issue with JNA and OSGi's handling of how the DLLs are loaded.

+0

Tôi nghĩ rằng tôi chạy nó đúng cách. Và điều này hoạt động tốt khi tôi gỡ lỗi nó trên nhật thực. Vấn đề xuất hiện khi tôi xuất một lọ và chạy nó. Tôi đã cập nhật câu trả lời bằng phương pháp tải của mình. Cảm ơn! – supertreta

+0

@supertreta: Tôi đặt cược bạn không đóng gói DLL vào JAR của bạn sau đó. Hãy thử đổi tên tệp JAR của bạn thành tệp zip và khám phá nó. –

+0

Tôi có thể thấy lọ tạo các dll trên thư mục. Tôi xác nhận nó đã xóa chúng trước khi chạy nó. Tôi đã kiểm tra bây giờ chuyển đổi nó trong một zip, và họ đang có. – supertreta