# Funktionsaufruf einer C++-Funktion mittels JNA



## raumichi (24. Februar 2010)

Hallo,

ich hoffe ich bin hier richtig und mir kann jemand helfen.

Ich will einen Kartenleser ansprechen (Krankenversichertenkarte). Bei dem Kartenleser ist eine API dabei, die drei Funktionen zur Verfügung stellt:

CT_init, CT_data und CT_close. 

Die init Funktion und die Close-Funktion laufen problemlos, nur das Aufrufen der CT_data Funktion will mir nicht gelingen.

Definiert sind die Funktionen in der API wie folgt: 

CT_init ( unsigned short ctn , unsigned short ctn)
CT_close ( unsigned short ctn)
CT_data (unsigned short ctn, unsigned char *dad, unsigned char *sad,
unsigned short lenc, unsigned char *command, unsigned short* lenr, unsigned char *response)

Ich habe die DLL mittels JNA eingebunden und folgende Klasse definiert:

```
package gbws.bw_tm.control;

import java.nio.ByteBuffer;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.ptr.ShortByReference;


public class CtTMKartenleser {

   public interface IKVKarteLibrary extends Library {
      IKVKarteLibrary INSTANCE = (IKVKarteLibrary)
         Native.loadLibrary("ctdeutin" , IKVKarteLibrary.class);
    
      int CT_init(char shA, char shB);
      int CT_close(char shA);
      int CT_data(char shCtn, ByteBuffer dad, ByteBuffer sad, char shLenc, ByteBuffer sCommand, ShortByReference lenr, ByteBuffer sResponse);
   }

}
```

So und nun will ich die Funktionen aufrufen.

CT_init und CT_close funktioniert, nur leider CT_data nicht.


```
final char chCtn = 0x00000001; 
         final char chPn  = 0x00000001;
            // Instanz des Kartenlesers erzeugen            
         IKVKarteLibrary lib  = IKVKarteLibrary.INSTANCE;
         // erster Schritt Kartenleser initialisieren
         int iInit = lib.CT_init(chCtn, chPn);

         // Datenfelder für den Zugriff auf CT_DATA #
         byte[] btDad = {1};
         byte[] btSad = {2};
         char chLenc = 0X00000005;     
         char chLenr = 256;
         short shLenr = 256;
         // Befehl
         byte[] resetICC = {0x20, 0x12, 0x01, 0x01, 0x00,0x00,  0x00, 0x00, 0x00, 0x00 };
        
        // Rückgabebereich         
         byte[] hsp = new byte[11];
         for (int i = 0; i < 10; i++) {
            hsp[i] = resetICC[i];
         }  
         byte[] rsp = new byte[501];
         for (int i = 0; i < rsp.length; i++) {
            rsp[i] = 0;
         }         
         int iErg = 0; 
               
         
         ByteBuffer bbin  = ByteBuffer.wrap(hsp);
         ByteBuffer bbout = ByteBuffer.wrap(rsp);
         ByteBuffer bbDad = ByteBuffer.wrap(btDad);
         ByteBuffer bbSad = ByteBuffer.wrap(btSad); 
         ShortByReference p1 = new ShortByReference(); 
         p1.setValue((short)chLenr); 


         // Nun sind 4 Befehle an das Kartenterminal abzusetzen:
         iErg = lib.CT_data(chCtn , bbDad, bbSad, chLenc, bbin, p1, bbout);
```

Als Rückgabewert sollte idealerweise 0 zurückkomme . ich bekomme allerdings: "202731264"

Aus einem C++Programm, aus dem der Zugrif funktioniert, schaut das so aus:


```
int ctn=1;
unsigned char ct_dad=1;
unsigned char sad=2;
unsigned short lenr=256;
unsigned char commandcode[11];
unsigned char responsearray[501];
commandcode[0]=0x20;
commandcode[1]=0x12;
commandcode[2]=0x01;
commandcode[3]=0x01;
commandcode[4]=0x00;
commandcode[5]=0x00;
commandcode[6]=0x00;
commandcode[7]=0x00;
commandcode[8]=0x00;
commandcode[9]=0x00;
commandcode[10]=0x00;			
			
// Request ICC zum CT schicken
rc=CT_data(ctn,&ct_dad,&sad,5,&commandcode[0],&lenr,&responsearray[0]);
```


Kann mir da jemand helfen, wo mein Denkfehler ist? Leider bin ich noch neu in JAVA und C++ kann ich kaum.

Danke


----------



## miffi (1. März 2010)

Howdie.

Prinzipiell zu JNA:
Ich habe inzwischen einige Projekte damit durchgeführt, und solche Zahlen-Geschichten haben meistens die gleiche Ursache -  Unsigned C-Variablen in signed Java-Variablen zu speichern. Vielleicht versuchst du mal den Rückgabewert im nächstgrößeren Datentyp zu speichern (in dem Fall Long), damit das Vorzeichen-Bit (das erste Bit der Variable) nicht als gesetzt interpretiert wird. Wobei es natürlich seltsam ist, dass ausgerechnet 202731264 als Ergebnis zurückkommt....

Falls was bei der Übergabe falsch läuft: 
Versuch doch mal statt dem ByteBuffer den ByteByReference-Datentyp zu verwenden. Warum nimmst du überhaupt einen Buffer? Hast du schon mal versucht, direkt das Byte-Array zu verwenden? Das mache ich meistens bei Char-Pointern. Ich muss gestehen, mit dem ByteBuffer hab ich das noch nie ausprobiert...
Für Mappings auf 16Bit C-Datentypen habe ich auch immer Shorts verwendet, nicht Chars. Hast du eine Möglichkeit, die Bibliothek zu debuggen und zu schauen, ob die gewünschten Werte ankommen? Dann könntest du den Fehler zumindest einschränken.

Ohne die Bibiothek selbst zu haben kann ich dir momentan leider nur bedingt helfen.... Sorry.

Viel Erfolg + Gruß
miffi


----------



## raumichi (2. März 2010)

Hallo,

Vielen Dank erstmal für deine Hilfe!!

Ich hab nun die Funktion geändert:


```
long CT_data(short shCtn, ByteBuffer dad, ByteBuffer sad, short shLenc, byte[] sCommand, Pointer lenr, byte[] sResponse);
```


```
final short shCtn = 1;
         byte[] btDad = {1};
         byte[] btSad = {2};
         short shLenc = 5;     
         short shLenr = 256 ;
         final ByteBuffer bbDad = ByteBuffer.wrap(btDad);
         final ByteBuffer bbSad = ByteBuffer.wrap(btSad); 
         ShortByReference p1 = new ShortByReference(); 
         p1.setValue(shLenr); 
         Pointer pLenr = p1.getPointer();  
        
         
        
         iErg = lib.CT_data(shCtn , bbDad, bbSad, shLenc, hsp, pLenr, rsp);
```

Leider mit einem ähnlichen Ergebnis: "797154176660410112".

Debuggen kann ich die Biliothek leider nicht. Ich kann dir gerne die .dll zuschicken?


----------



## Thomas Darimont (2. März 2010)

Hallo,



> Ich kann dir gerne die .dll zuschicken?


Ohne das Kartenlesegerät wird das doch nichts nützen, oder?

Gruß Tom


----------



## raumichi (2. März 2010)

Hallo,


weiß ich nicht, ob ihr Profis damit nicht sogar was anfangen könntet.

Ich selbst kann mit .dll und Kartenleser die Lösung nicht finden. 

Gruß


Michi


----------



## miffi (2. März 2010)

Hi Michi.

Da die Long-Variable auch mit so seltsamen Werten gefüllt wird, kommt mir eine Frage:
Ist der Rückgabewert der Funktion _CT_data_ nicht zufällig ein Pointer? Vielleicht liest du da grad nur Speicheradressen aus. In deinem ersten Post stehen gar keine Rückgabewerte bei der API.

Wie Thomas schon gesagt hat - ob es sinnvoll ist, die DLL ohne Lesegerät zu testen, ist fraglich... 
Oder gibt es einen brauchbaren Rückgabewert, falls kein Lesegerät gefunden wurde? Dann könnt ich mir das schon mal anschauen.

Gruß

miffi


----------



## raumichi (2. März 2010)

Hi,

ich bin nun total verwirrt, wenn ich es als pointer definiere kommt ein zumindest besseres Ergebnis raus. Es läuft zwar immer noch nicht korrekt, aber das Ergebnis zeigt nun eine "0" (steht eigentlich für fehlerfrei) an. Leider stehen aber keine Daten in dem Response-Bereich.


```
ShortByReference p2 = new ShortByReference(); 
p2 = lib.CT_data(shCtn , btDad, btSad, (short) 5, hsp, p1, rsp);
shErg = p2.getValue();
```

Kann mir jemand vielleicht ein C++-Programm schreiben, dass die Funktion

```
CT_data 
(unsigned short ctn, unsigned char * dad, unsigned char * sad, unsigned short lenc, unsigned char * command, unsigned short * lenr, unsigned char * response);
```
allgemein zur Verfügung stellt und die übergebenen Bereiche an die Konsole übergibt. Dann würde ich mir ´ne .dll draus machen und schauen welcher Parameter falsch ankommt.

Grüsse und nochmals vielen Dank!


----------



## deepthroat (2. März 2010)

Hi.

Laut CT-API Referenz ist der Rückgabetyp ein "signed char". (IS8)

Man könnte schon eine C Datei erstellen welche diese Funktione zur Verfügung stellt, nur müßtest du dann auch wirklich konkret sagen wie die Funktionen aussehen (Rückgabetyp, calling convention?). Zeig mal die Headerdatei wo die Funktionen deklariert sind.

Gruß


----------



## raumichi (2. März 2010)

Hier, ich hoffe das ist die richtige:



```
/*****************************************************************************
@doc            INT EXT
******************************************************************************
* $ProjectName:  $
* $ProjectRevision:  $
*-----------------------------------------------------------------------------
* $Source: z:/pr/ctapi/sw/ct/rcs/ct.h $
* $Revision: 3 $
*-----------------------------------------------------------------------------
* $Author: tbruendl $
*-----------------------------------------------------------------------------
* History: see EOF
*-----------------------------------------------------------------------------
*
* Copyright © 2009 HID Global
******************************************************************************/

#ifndef _INC_CT
   #define _INC_CT

/*****************************************************************************/
/**  CT-API return codes according CT-API 1.1                               **/
/*****************************************************************************/
/*
** @consts CT API Error Codes | The CT API functions return following error codes.
*/
#define    OK              0           /* @cnst Function call was successful        */
#define    ERR_INVALID    -1           /* @cnst Invalid parameter or value          */
#define    ERR_CT         -8           /* @cnst CT error (CT not in operation)      */
#define    ERR_TRANS      -10          /* @cnst Non-eliminable transmission error   */
#define    ERR_MEMORY     -11          /* @cnst Memory assignment error in HTSI     */
#define    ERR_HTSI       -128         /* @cnst HTSI error                          */

#define SAD_HOST         0x02
#define SAD_REMOTE_HOST  0x05
#define SAD_ICC1         0x00
#define SAD_CT           0x01

#define DAD_HOST         0x02
#define DAD_REMOTE_HOST  0x05
#define DAD_ICC1         0x00
#define DAD_CT           0x01





#ifdef __cplusplus
    extern "C" {
#endif


char  _stdcall CTDEUTICM_close             (
                          unsigned short ctn
                          );

char  _stdcall CTDEUTICM_init        (
                           unsigned short ctn,
                           unsigned short pn
                    );
char _stdcall CTDEUTICM_data (
                             unsigned short ctn,
                             unsigned char * dad,
                             unsigned char * sad,
                             unsigned short lenc,
                             unsigned char * command,
                             unsigned short * lenr,
                             unsigned char * response
                             );

char  _stdcall CT_close             (
                          unsigned short ctn
                          );

char  _stdcall CT_init        (
                           unsigned short ctn,
                           unsigned short pn
                    );
char  _stdcall CT_data (
                             unsigned short ctn,
                             unsigned char * dad,
                             unsigned char * sad,
                             unsigned short lenc,
                             unsigned char * command,
                             unsigned short * lenr,
                             unsigned char * response
                             );


#ifdef __cplusplus
   }
#endif



#endif /* _INC_CT */
```


----------



## deepthroat (2. März 2010)

Hi.

Die Datei ist etwas merkwürdig. Es wird stdcall verwendet, allerdings konnte JNA die Funktionen in der DLL finden obwohl nur von Library  und nicht von StdCallLibrary
 geerbt wurde... (evtl. solltest du mal StdCallLibrary versuchen...)

Dann wird der Rückgabetyp als char spezifiziert und nicht als signed char wie laut Spezifikation.

Außerdem wird kein declspec import bzw. export der Funktionen zur Nutzung als DLL durchgeführt. Das ist schon etwas merkwürdig.

Ich hab mal schnell einen Testtreiber und DLL geschrieben. Wenn du die DLL erstellst mußt du die ct_EXPORTS Variable definieren - beim Kompilieren des ct_test.c Programmes nicht.

Gruß


----------



## raumichi (3. März 2010)

Hallo,


echt super deine Hilfe

Ich hab nun deine Sourcen kompiliert und eine .dll daraus gemacht.
Bekomm nun aber leider beim Ausführen folgenden Fehler:

java.lang.UnsatisfiedLinkError: Error looking up function 'CT_init': Die angegebene Prozedur wurde nicht gefunden.

	at com.sun.jna.Function.<init>(Function.java:179)


Muss ich beim Kompilieren noch was einstellen? Ich nutze den Dev-Cpp.


----------



## deepthroat (26. März 2010)

Hi.

Sorry, ist mir irgendwie durch die Lappen gegangen das du hier etwas geschrieben hast. Ist das Problem denn inzwischen gelöst?


raumichi hat gesagt.:


> Ich hab nun deine Sourcen kompiliert und eine .dll daraus gemacht.
> Bekomm nun aber leider beim Ausführen folgenden Fehler:
> 
> java.lang.UnsatisfiedLinkError: Error looking up function 'CT_init': Die angegebene Prozedur wurde nicht gefunden.
> ...


Wie gesagt, du mußt nur die ct_EXPORTS Präprozessorvariable beim Erstellen der DLL definieren. Also -Dct_EXPORTS=1 bei den Compileroptionen in den Projekteinstellungen angeben.

Gruß


----------

