1. 程式人生 > >JNA呼叫DLL

JNA呼叫DLL

一、前言

  Jna呼叫的示範,基本包括了Java->C基本型別的轉換,指標的轉換等。

       比起一直盲目搜尋,第一次用JNA最好靜下心看一遍,看完這篇省很多事。

不過文章是2011年的,可能後面要檢視下有什麼改變。

二、原文

三、翻譯

     大體翻譯。

     從Java呼叫dll,你可以選擇JNI和JNA。(還有其他了.......)

      關於呼叫轉換

  首先定義介面,介面繼承“ “com.sun.jna.Library” 或者 “com.sun.jna.win32.StdCallLibrary”. ”。

     View Code

Java中呼叫如下:

View Code

基本資料轉換

上述方法中,“returnDllVersion”方法返回一個char *(C語言),JNA框架將char *轉換為Java String(Unicode)。JNA提供了大部分的JAVA/C資料型別的轉換,可參見 “ Mapping between Java and Native “.

      注:此段後為自己加的內容,可能有誤。

由於我在資料轉換時涉及到了很多unsigned,signed的問題,之前基礎也不好,對Java/C的資料型別就一併進行了一些學習,記錄在此塊。

      首先可以看出,對於Java來說大多是sighed型別的,除了java char,java char為2位元組,byte才為1位元組相當於C中的char。

      簡單的一個小表,Java的型別首字母大寫無視吧......小表只是自己分析過程的一個記錄......

       

       所以,我對網上的一些對C函式中unsigned int 的有些說轉成Java long或者Java int的做法不是很理解。對於儲存來說,計算機儲存的unsighed int就是32位,轉換為Java long64位讀取不會越界?轉換為Java  int讀取的話如果涉及到最高位符號位的,按照Java int有符號的讀取怎麼會正確?

輸出到共享記憶體

C++:
EXAMPLE_DLL bool readBufferContent(unsigned 
char* outbuf); Java: boolean readBufferContent(Memory memory);

C函式”readBufferContent"將字串輸出到記憶體中,Java呼叫這函式讀取等,如果寫入字串的最大長度已知,則可如下操作:

1.Java請求分配記憶體,以byte為單位

Memory memory = new Memory(100);

2.Java呼叫執行,以memory為引數

ExampleDLL.INSTANCE.readBufferContent(memory);

3.C++寫入,返回

4.Java讀取記憶體,獲得寫入字串

String bufferContent = memory.getString(0);

5.當這塊記憶體的引用為0是,Java垃圾回收機制自動回收此空間。

    int 型別輸出引數:call-by-reference

C++:
EXAMPLE_DLL ResultStruct* consolidate(const short *cardTypes, int *numberOfResults);

Java:
Pointer consolidate(int[] cardTypes, IntByReference numberOfResults);

這個例項演示瞭如下幾個問題:

    1. C中第一個引數為int陣列(short 16位為什麼會是int陣列?),JNA中提供了簡單陣列轉換,參見JNA文件  “Pointers and Arrays “。

2.  第二個變數為輸出變數,Dll把結果寫入numberOfResults變數中,引數型別為int *,傳入int變數地址。JNA提供了豐富的ByReference類來實現地址傳參,如 “com.sun.jna.ptr.IntByReference”.Java中如下可訪問該地址,獲取資料。  

int value = numberResults.getValue();

   3. 返回為結構體指標,對於這個指標不能像IntByReference一樣確定該指標指向的空間大小。

   動態陣列指標

 之前的方法中,C返回陣列指標,陣列元素型別為結構體ResultStruct,JNA不能自動轉換結構體。但是可以將C結構體轉換為Java Object類,結合JNA提供的陣列轉換,來完成結構體陣列的轉換。 

我們要解決2個問題:

  1.    如何為結構體陣列分配和釋放動態記憶體?
  2.    如何構造Java Object類?

由於在函式呼叫前我們無法獲知陣列長度,所以無法確定需要的記憶體大小,所以不能在JNA中分配固定大小的記憶體。C++使用malloc()和new()來分配heap memory,DLL中必須提供單獨的freeMemory()函式來實現記憶體的釋放。   

C++:
void freeMemory(ResultStruct *arr) { if(arr) delete[] arr; }

Java:
void freeMemory(Pointer p);

  由於DLL自身使用了單獨的heap-Manager,Java程式不能釋放這段記憶體,進一步討論可參見   in a C++ forum 。

    後面是結構體的轉換等,過了一遍不翻譯。

Create Java objects from array of C-structs

We map the return type of the function as a com.sun.jna.Pointer in Java. With the knowledge about the C-struct (sequence and data type of member variables), the java program can read and convert the memory content to create adequate objects. The number of elements in the array is also known (variable “numberOfResults”, see above). It is very important, to compute the offset correctly to avoid JVM-crashes for invalid memory access!

C++:
struct ResultStruct {
  unsigned short count;
  char description[40];
  unsigned short average;
};

Java:
public static class ResultStruct {
  public short count;
  public String description;
  public short average;
}

static ResultStruct[] fromArrayPointer(Pointer pointer, int numberResults) {
  ResultStruct[] arr = new ResultStruct[numberResults];
  int offset = 0;
  for (int i = 0; i < numberResults; i++) {
    arr[i] = fromPointer(pointer, offset);
    offset += 44;
  }
  return arr;
 }

static ResultStruct fromPointer(Pointer pointer, int offset) {
  ResultStruct inst = new ResultStruct();
  inst.count = pointer.getShort(offset);
  offset += 2;
  inst.description = pointer.getString(offset);
  offset += 40;
  inst.average = pointer.getShort(offset);
  return inst;
}

Callback functions (Call a Java method from within the C++ DLL)

In places, where C gets a function pointer to call a function, this can be used to call Java methods as well. JNA offers a Callback-interface, documented in chapter “ Callbacks/Closures “. The Callback class inherits form the Callback interface and contains a single method of arbitrary name and with compatible signature.

C++:
EXAMPLE_DLL void setFunction(void (*func)(char **texts, int count));

Java:
void setFunction(ExampleCallback callback);

public interface ExampleCallback extends Callback {
    void receive(Pointer pointerToTexts, int count);
}

Convert a char** into a String-array

Finally, a look at the signature of the “receive” method in the ExampleCallback interface. It gets a string-array in form of  a pointer to a pointer, char **. Because of the differences between C-strings and Java-strings, you should use the method Pointer.getStringArray(int,int) provided by JNA to do the conversion. No care needs to be taken of memory usage, because the memory for the strings has been allocated by the C-DLL and the call happens from inside the DLL, so the C-program is responsible to free the memory when the callback returns. The java-strings copy the memory content and are completly decoupled from the c-strings.

public void receive(Pointer pointerToTexts, int count) {
  String[] texts = pointerToTexts.getStringArray(0, count);
  ...
}

四、小節

  long  WINAPI AutoOpenComPort(long* Port, unsigned char *ComAdr, unsigned char Baud,long *FrmHandle);

      分析下我自己需要的轉換,32位機。

      long *:之前直接用pointer指向4位元組,傳參pointer.應該可以直接用intbyrefrence. 想想其實實質也沒差別啊

      unsigned char *:就是String, byte[]?

      unsigned char:byte.

     理解上是不是所有指標傳參都可以用pointer完成,獲取看看原始碼怎麼實現就知道了。