LabVIEWForum.de
Problem bei Aufruf einer dll - Druckversion

+- LabVIEWForum.de (https://www.labviewforum.de)
+-- Forum: LabVIEW (/Forum-LabVIEW)
+--- Forum: LabVIEW Allgemein (/Forum-LabVIEW-Allgemein)
+---- Forum: DLL & externer Code (/Forum-DLL-externer-Code)
+---- Thema: Problem bei Aufruf einer dll (/Thread-Problem-bei-Aufruf-einer-dll)



Problem bei Aufruf einer dll - swoc - 07.09.2022 05:52

Hallo zusammen,

ich habe ein interessantes Phänomen beim Aufruf einer dll - und zwar geht es hier um einen ganz einfachen Aufruf folgender Funktion:

/**
* Get the error message, in case an error happend
*/
LBUS_API const char* __stdcall getErrorMessage(int errorVal);

also ich übergebe der Funktion einen numerischen Wert und bekomme einen String, der den Fehler beschreibt...nichts anderes wie die Umwandlung Fehlercode in Text.

Der definierte Bereich geht von -19 bis 11. Bei manchen Werten bekomme ich jedoch nur ein paar Sonderzeichen 0x03 0x00 0x00 zurück. (siehe z.b. -17, -16)

-19 File too old or Wrong File
-18 devicSTM:STARTe not found
-17 
-16 
-15 File not readable
...

Jetzt stellt sich für mich die Frage, warum es in den meisten Fällen funktioniert und manchmal nicht.

hier ein Screenshot meiner Konfiguration:
[attachment=62357][attachment=62354]

hier der Code in LV:
[attachment=62355]

Vielleicht noch ein zusätzlicher nützlicher Hinweis:
Wenn ich in meiner Knotenkonfiguration errorVal von I32 auf I16 umstelle, dann funktionieren die ganzen negativen Werte nicht mehr, also alles unter 0 liefert dann diese Sonderzeichenkette und von 0 bis 11 verhält es sich wie vorher: mehrheitlich ok, manchmal Sonderzeichen
[attachment=62356]

Ich benutze LV2020 32bit, die dll sollte laut meinem Kollegen auch 32bit sein
Betriebssystem ist Windows 10 Pro, 64bit

Vielen Dank im Voraus für euer Feedback.
Christian


RE: Problem bei Aufruf einer dll - jg - 07.09.2022 09:30

Hallo Christian,

das die Umstellung von I32 auf I16 dann bei negativen "I16" Zahlen Blödsinn liefert, ist logisch. Stell einfach mal die Anzeige eine I16 oder I32, bei dem du Dezimal -1 eingibst, in LabVIEW auf Binary um, dann wirst du feststellen, dass bei I16 ganz viele gesetzte Bits fehlen.

Zur "seltsamen" Rückgabe: Was sagt denn die Doku der DLL dazu? Was soll denn z.B. bei -16 zurückkommen?

Gruß, Jens


RE: Problem bei Aufruf einer dll - swoc - 08.09.2022 06:45

Hallo Jens,

vielen Dank für dein Feedback.

Die Thematik (Probleme im negativen Bereich) mit dem falschen Datentyp (I16 statt I32) hab ich mir eh schon gedacht und ist auch nachvollziehbar.

Die dll ist von uns selber und funktioniert auch in anderen Programmen problemlos, nur eben in LabVIEW leider nicht.

Ich glaube, das liegt einfach an der schlechten Kompatibilität der Datentypen und Speicherverwaltung etc. (zumindest habe ich das so wahrgenommen, wo ich mir die Beiträge im Forum bezüglich Verwendung einer dll durchgelesen habe).

Ich habe mal versucht, den Code in LabVIEW nachzubauen, daraus eine dll zu erstellen und dann zu schauen, wie der Funktionsprototyp aussieht.

Das ganze hat dann folgendes ergeben:
void __cdecl GetErrorMessage(int32_t errorVal, LStrHandle *String);

Bei der ursprünglichen dll sieht das so aus:
LBUS_API const char* __stdcall getErrorMessage(int errorVal);

Ich weiß nicht ob es andere Möglichkeiten gibt, die ursprüngliche dll fehlerfrei zu nutzen, aber ich hab mir gedacht, vielleicht kann man eine Art "Zwischenschnittstelle" oder "Übersetzer-Schnittstelle" einbauen zwischen der ursprünglichen dll, und dem, was man gut in LabVIEW einbinden kann. In dieser Thematik kenn ich mich aber leider gar nicht aus.

Für Tipps bin ich sehr dankbar.

Gruß
Christian


RE: Problem bei Aufruf einer dll - Martin.Henz - 08.09.2022 08:03

Hallo Christian,

bei einer Funktion die als Return Type char * hat ohne dass der Aufrufer einen Puffer und die Puffergröße übergeben muss, gehen bei mir ehrlich gesagt erst mal die Alarmglocken an. Wo kommt denn der String her? Wer muss den Speicher wieder frei geben oder ist es ein pointer auf einen statischen String?

Wenn ich das nicht beantworten kann, dann lasse ich da meine finger weg und wenn ich selbst eine DLL schreibe, dann mache ich so etwas erst gar nicht.
(Den Fall meide ich seit ewigen Zeiten wie die Pest und kann deine Frage(n) daher auch nur bedingt beantworten)

Der Aufrufer stellt den Puffer zur Verfügung und sagt, wie groß der Puffer ist. Übergibt der Aufrufer eine Puffergröße von 0 und/oder einen Null Pointer, dann liefert die Funktion entweder einen Fehler oder sie sagt, wie groß der Puffer sein sollte. Wenn es denn unbedingt sein soll, dass der DLL Funktion den Speicher allokiert, dann bitte nur Optional und nur wenn beschrieben wird, wo der Puffer her kommt. Ein Beispiel für letzteren Fall wäre z.B. FormatMessage

LabVIEW hat seinen eigenen Speichermanager und du hast so gut wie gar keinen Einfluss darauf, wann Speicher allokiert wird und wann er von LabVIEW wieder freigegeben wird. Wenn eine Funktion also ein char * zurück liefert und du diesen als LStrHandle in Labview haben möchtest, dann muss LabVIEW in der Call Library Node den C-String auf den char * zeigt umkopieren, also zunächst einmal dessen Länge feststellen, Speicher in der passenden größe allokieren und den String umkopieren.

Das sollte IMHO auch erst einmal gut funktionieren. Ein CStr endet beim ersten 0x00 Zeichen. Ist die Zeichenkette z.B. in UTF-16 kodiert, dann kann das nicht mehr funktionieren. Es funktioniert auch dann nicht, wenn die aufrufende Funktion den benötigten Speicher dynamisch allokiert und der Aufrufer den Speicher wieder freigeben soll.

Zunächst wären also ein paar Fragen zu klären:
Wo kommt der Speicher her auf den der Pointer zeigt? Und gegebenenfalls auch: Wer und wie wird dieser Speicher wieder freigegeben?
Wie ist der String kodiert?

Weil ihr die DLL ja selbst erstellt, würde ich jetzt einfach den Entwickler der DLL freundlich aber bestimmt darauf hinweisen, dass diese Funktion keine so gute Idee ist und ihn bitten (oder auch mit einer Tasse Kaffee "bestechen") einfach zusätzlich noch folgende Funktion zu implementieren:

const int __stdcall getErrorMessageExt(int errorVal, char *Buffer, int BufferSize);

Die Funktion prüft, ob der übergebene Puffer groß genug ist für den String (und Buffer ungleich NULL ist). Falls das passt, dann wird die Fehlermeldung dort hineinkopiert.
Return Wert: Anzahl der Zeichen im String. Im Fehlerfall ist der Return Wert null.

Das sollte in ein paar Minuten inclusive Doku erledigt sein und spart auf der anderen Seite viele Stunden an Nachfragen und Überlegungen (ganz unabhängig von der Programmiersprache stellt sich das immer). Alleine die Zeit für dieses Postings dauert schon länger als das Implementieren einer solchen Funktion. Es wäre überflüssig wenn es eine solche Funktion bereits geben würde.


RE: Problem bei Aufruf einer dll - swoc - 08.09.2022 12:58

Hi Martin,

vielen Dank für deinen Input.

Ich habe es jetzt so versucht:
in der dll (h-Datei):
/*!
* GetErrorMessage
*/
void __cdecl GetErrorMessage(int32_t errorVal, char String[], int32_t LNge);

in LabVIEW:
[attachment=62367][attachment=62368][attachment=62369]

Schaut soweit gut aus, ich denke jetzt habe ich das Prinzip dahinter verstanden - werde es demnächst auch mit Strukturen/Clustern versuchen.

Danke und Gruß
Christian


RE: Problem bei Aufruf einer dll - Martin.Henz - 08.09.2022 17:01

Hi Christian,

viel Spass mit den Clustern und Strukturen.