DLL-Funktion: Unsupported Argument


#1

Hallo zusammen,

ich habe ein Problem mit dem Aufruf einer DLL-Funktion aus einem Java-Programm heraus.
Laut Dokumentation erwartet die DLL für die Funktion BCSetTextA folgende Parameter:

  • t_BarCode *pBarCode
  • LPCSTR szText
  • LONG nLen

Ich verwende JNA.

Mein Interface sieht wie folgt aus:

{
	tecitdll INSTANCE = (tecitdll) Native.loadLibrary((Platform.isWindows() ? "TBarCode11_x64" : "c"), tecitdll.class);
        void BCLicenseMeA(String a, int b, int c, String d, int e);
        void BCSetTextA(tecitdll pointer, String text, int laenge);
       	void BCSetBCType(tecitdll pointer, String eBC_2OF5IL);
	void BCBitmapToFileA(tecitdll pointer, String pfad, Rectangle rechteck);
}```


Die DLL habe ich so instanziert:
```tecitdll barcode = tecitdll.INSTANCE;```


und diesen Barcode übergebe ich als ersten Parameter:
```barcode.BCSetTextA(barcode, strBarcode, strBarcode.length());

Der Aufruf beschert mir dann diese Fehlermeldung:

Unsupported argument type com.sun.proxy.$Proxy0 at parameter 0 of function BCSetTextA

Die Dokumentation zur DLL findet ihr hier:
TEC-IT TBarCode Library 11: Barcode Data

Ich hoffe auf baldige Hilfe

Vielen Dank im Voraus


#2

Hab’s nur überflogen, aber … du übergibst da ja die library-instanz - also das Ding, mit dem du von Java aus auf die Library zugreifen willst.

Der Doku nach sollte das erste Objekt, das diesen Funktionen übergeben wird, ein Objekt vom typ t_BarCode * sein - also ein Pointer auf eine t_BarCode struct. So ein Objekt wird wohl mit ERRCODE _stdcall BCAlloc ( t_BarCode ** ppBarCode ) allokiert.

Im Pseudocode (bin mit JNA nicht so vertraut) wäre der Ablauf, vermutlich (!) etwa sowas wie

Pointer pointerToBarCode = ... 
PointerToPointer pointerToPointerToBarCode = createFrom(pointerToBarCode);

// Rufe BCAlloc auf, so dass die Struktur im "pointerToBarcode" initialisiert wird
tecitdll.INSTANCE.BCAlloc(pointerToPointerToBarCode);

// Verwende die initialisierte Struktur
tecitdll.INSTANCE.BCSetTextA(pointerToBarCode, "foo", 3);

Websuchen mit Stichworten wie “JNA opaque pointer struct” usw. könnten auch ein paar Ergebnisse bringen. Wenn’s nicht hilft, schau’ ich vielleicht nochmal genauer, wie das bei JNA exakt läuft.


#3

Das Problem ist das du die Funktion BCSetTextA schon einen falschen Header gibst.
dein Header : void BCSetTextA(tecitdll pointer, String text, int laenge);
halbwegs korrekter Header : ERRCODE BCSetTextA(t_BarCode ptr, String szText, int nLen)
Die STRUCT t_BarCode bzw tag_BarCode musst ebenfalls implementieren, sowie den Return ERRCODE :

{
	ERRCODE BCSetTextA(tecitdll.t_BarCode ptr, String szText, int nLen);
	
	long ERRCODE;
	
	public static class t_BarCode extends Structure
	{
		// ...
	}
}```
Ein möglicher Call würde dann in etwa so aussehen :
```tecitdll.t_BarCode struct=new tecitdll.t_BarCode();
String data="DATA";
tecitdll.ERRCODE err=tecitdll.INSTANCE.BCSetTextA(struct, data, data.length());```
Jetzt kommt das Problem : die Doc gibt leider den internen Aufbau der t_BarCode STRUCT nicht her sondern lediglich den hinweis das diese "intern" ist. Dem kannst du alleine aus der Doc kein Java-Mapping erstellen. Und ich bezweifel das die mal eben die Source-Header rausrücken. Ob man mit irgendwelchen Tools aus der fertigen DLL die Header rekonstruieren kann weis ich nicht, grenzt aber auch schon wieder an Lizenz-rechtliche Dinge.

Möglichkeit : den Support anschreiben und um Hilfe bitten, ohne nötige Source oder Doc bzw Tools die aus der DLL nötige infos bauen ist es mit JNA nicht möglich.

#4

Bist du beim letzten sicher? Man braucht den Inhalt der struct ja eigentlich nicht. Das ist ja wohl das, was immer als “opaque pointer” bezeichnet wird: Man bekommt einen Pointer von der Lib, und gibt diesen Pointer an andere Funktionen der Lib, und man weiß, DASS eine struct dahintersteckt (in der interne Daten gespeichert werden), aber man weiß eben nicht, wie die struct aussieht. Einer schnellen Websuche nach sollte es eigentlich möglich sein, das mit JNA anzusteuern. (Wäre schlimm, wenn nicht: Das ist ein sehr gängiges Konzept. Quasi das “private” der C-Programmierer :D)


#5

Bin mir da bzgl der Doc nicht ganz sicher.
Soweit ich die Doc verstehe muss man als erstes BCAlloc() callen, was als Parameter einen Pointer auf einen Pointer auf die struct erhält (ich hasse C, vor allem die Namens-Konventionen und diese Pointer-Krams).
Heißt also, wenn ich das richtig verstanden habe, das man sich erstmal einen Point auf die struct holt, für diese dann erstmal mit malloc(sizeof()) den speicher reserviert (auf den dann der Pointer zeigt) und holt sich dann einen zweiten Pointer der auf den Ersten zeigt und übergibt diesen dann BCAlloc() (warum macht man sowas überhaupt ? maximum verwirrung).

Letzten Endes kommt also (in C) sowas hier bei raus :

malloc(pBarCode, sizeof(pBarCode)) // sry für die Java-syntax ... aber ich weis halt nich wie es richtig geht
ptr** ppBarCode=// Pointer auf pBarCode
BCAlloc(ppBarCode)
BCSetTextA(pBarCode, char* text, sizeof(text))```
PS : müsste mal in "korrektes C/++" übersetzt werden
Und diesen Ablauf dann so in Java nachbauen.

Ob die Impl der struct wirklich nötig ist oder ob man sich so einen passenden Pointer holen kann (nach dem Muster einer Factory-Methode) weis ich nicht, würde aber sagen wenn ich keine Factory habe die mir einen solchen Pointer/Handle liefert brauch ich zumindest fürs Mapping ein entsprechendes Member im Lib-Interface. Oder war das nur wenn man an den Inhalt der struct ran wollte ? Ich weis es nicht mehr, müsste es ausprobieren.

#6

Hm… das mit diesen “Opaquen Pointern” läuft normalerweise (d.h. immer: soweit ich das weiß) etwas anders (alles mir vieeelen Vorbehalten: )

Man weiß erstmal NUR, dass diese struct existiert. Es ist also (sinngemäß, nicht notwendigerweise technisch genau so) so dass man einen Header hat, wo nur eine Typdeklaration drinsteht
typedef struct tag_BarCode t_BarCode;
Man weiß nicht, was in dieser Struct enthalten ist. In irgendeiner (als “Implementierungsdetail” versteckten) C-Datei steht dann die tatsächliche Defintion.

Als Client kann man mit dieser Struct direkt nichts machen. Man kann nur einen “Pointer auf diese Struct” definieren. Aber insbesondere wird sowas wie sizeof(thisStruct) nicht funktionieren, weil man schlicht nicht weiß, wie groß diese Struct ist. Man kann also auch NICHT den Speicher für diese struct selbst malloc’en.
Genau dafür ist ja BCAlloc da:

Man definiert einen Pointer auf so eine struct. Dessen Adresse (also einen Pointer auf einen Pointer) übergibt man an die BCAlloc-Funktion. Die schreibt in den ersten Pointer dann den Pointer auf die struct, die sie intern allokiert hat:

t_BarCode *clientPointer;

// Übergib die Adresse des Pointers (also einen Pointer auf den Pointer) :
BCAlloc(&clientPointer);

// Verwende den nun gefüllten Pointer
BCSetTextA(clientPointer, "foo", 3)

Ausführlicher könnte man auch schreiben:

t_BarCode *clientPointer;

t_BarCode **pointerToClientPointer = &clientPointer;

BCAlloc(pointerToClientPointer);

// Verwende den nun gefüllten Pointer
BCSetTextA(clientPointer, "foo", 3)

Die “BCAlloc”-Funktion würde dann sowas machen wie

void BCAlloc(t_BarCode **pp)
{
    // Die Implementierung KENNT die struct, und
    // kann deswegen malloc und sizeof machen:
    t_BarCode *p = malloc(sizeof(t_BarCode));

    // Schreibe den allokierten Pointer da hin, wo der
    // übergebene Pointer hin zeigt. (Das landet dann
    // im oben angedeuteten "clientPointer")
    *pp = p;
}

Als Benutzer hantiert man NUR mit dem Pointer, und weiß nie, worauf der eigentlich zeigt. Das ist die Objektorientierung der 80er Jahre :smiley:

Und, wie gesagt, das ist vom Muster her nicht unüblich, deswegen sollte JNA das eigentlich auf die Reihe kriegen. Die oben erwähnte Suchanfrage “jna opaque pointer struct” liefert z.B. java - JNA: how to deal with unkown structs? - Stack Overflow oder java - How do i deal with opaque pointers in JNA? - Stack Overflow , aber … ich habe mich mit JNA noch nicht so weit beschäftigt, dass ich direkt den Code für diesen Fall hier herschreiben könnte.


#7

Laut Doc müsste com.sun.jna.Pointer ja grundsätzlich erstmal das Mapping für einen Pointer an sich sein. Also würde in der Klasse zumindest sowas landen wie Pointer t_BarCode;
Und einen Pointer auf diesen Pointer selbst müsste die Methode Pointer.getPointer(long) liefern.
Ergibt dann als Impl :
ERRCODE BCAlloc(Pointer pointer); // ... ERRCODE code=BCAlloc(t_BarCode.getPointer(null));
Oder irgendwie sowas, vielleicht auch was mit LongByReference oder what ever.

JNA ist schon wirklich ne Hausnummer für sich.