LabVIEWForum.de
DLL-Einbindung, Struct mit verschiedenen Datentypen als Pointer übergeben - 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: DLL-Einbindung, Struct mit verschiedenen Datentypen als Pointer übergeben (/Thread-DLL-Einbindung-Struct-mit-verschiedenen-Datentypen-als-Pointer-uebergeben)



DLL-Einbindung, Struct mit verschiedenen Datentypen als Pointer übergeben - hawk72 - 06.05.2012 21:02

Hallo Labview-Gemeinde,

ich habe hier ein "kleines" Problem mit der Einbindung einer DLL-Datei, welche einen struct aus verschiedenen Datentypen als Pointer übergeben haben möchte. Habe mir schon die ähnlichen Beispiele hier angeschaut und auch einige Werte, die ohne Umwege über Pointer, durch die DLL ausgegeben werden können, erfolgreich auslesen können (Status, Dll-Version, Anzahl Geräte).
Es macht z.Z. einfach nicht klick und ich stochere doch etwas im Dunkeln.

Mich interessieren hauptsächlich die Daten des t_DeviceData structs.
t_DeviceInfo wäre auch hilfreich aber eher zum Verständniss bzw. zum Gerätecheck

Nachfolgend habe ich die vorhandenen Daten eingefügt:

// --------------------------------------
// Struktur fuer Gereate-Infos (USB-HID)
// --------------------------------------
typedef struct
{
DWORD idx; // USB-Geraete-Index
int open; // Zustand -> Open=1 / Close=0
char vid_pid[256]; // VID/PID-String
char dev_info[256]; // GeraeteInfo-String
char sn_info[256]; // S/N-String
int hw_info; // HW-Info (5xxx)
unsigned char hw_var; // HW-Variante
int fw_vers; // FW-Version
} t_DeviceInfo;

// --------------------------------------
// Struktur fuer Messwerte eines Geraetes
// --------------------------------------
typedef struct
{
unsigned char me; // Masseinheit (0...x) - siehe oben
float brutto; // Brutto-Sring
float netto; // Netto-Sring
float tara; // Tara-Sring
float min; // Min-Sring
float max; // Max-Sring
unsigned char dec; // Anzahl Nachkommastellen
unsigned char status; // Status-Byte
} t_DeviceData;

//---

// --------------------------------------
// Funktions-Prototypen der DLL
// --------------------------------------
extern "C" DLL_API int Init(void);
extern "C" DLL_API int Search(t_DeviceInfo *p_dev_info);
extern "C" DLL_API int Open(int list_idx);
extern "C" DLL_API int Close(int list_idx);
extern "C" DLL_API int ReadData(int list_idx, t_DeviceData *dev_dat);
extern "C" DLL_API int Null(int list_idx);
extern "C" DLL_API int GrossNet(int list_idx);
extern "C" DLL_API int ChangeUnit(int list_idx);
extern "C" DLL_API int ChangeRange(int list_idx);
extern "C" DLL_API int Reset(int list_idx);
extern "C" DLL_API int DLLVersion(void);

//---
____________________________________________________________

Das Lesen der aktuellen Messwerte erfolgt über die Funktion:

int ReadData(int list_idx, t_DeviceData *dev_dat);

list_idx - ist eine int16 Wert mit der Nr. des Meßgerätes (hier 1)
t_DeviceDate - ist die Struktur (siehe struct oben)
*dev_dat - der Pointer

Ich scheitere einfach an der Übergabe der Struktur als Pointer.
Die Structur habe ich in einem Cluster abgelegt bestehend aus:

(U8,double,double,double,double,double,U8,U8)

Vielleicht kann mir jemand einen Tip geben ob dies soweit korrekt ist und
wie die Sache mit dem Pointer zu realisieren ist.

Bis jetzt konnte ich alle Fragen bei meinen Labview Experimenten, auch dank des Forums hier, selber losen.
Aber die Sache mit der DLL ist schon frustrierend ... deshalb auch mein erster Post hier ;o)

Vielen Dank schonmal im Voraus...

Norbert


RE: DLL-Einbindung, Struct mit verschiedenen Datentypen als Pointer übergeben - rolfk - 07.05.2012 11:21

(06.05.2012 21:02 )hawk72 schrieb:  Hallo Labview-Gemeinde,

ich habe hier ein "kleines" Problem mit der Einbindung einer DLL-Datei, welche einen struct aus verschiedenen Datentypen als Pointer übergeben haben möchte. Habe mir schon die ähnlichen Beispiele hier angeschaut und auch einige Werte, die ohne Umwege über Pointer, durch die DLL ausgegeben werden können, erfolgreich auslesen können (Status, Dll-Version, Anzahl Geräte).
Es macht z.Z. einfach nicht klick und ich stochere doch etwas im Dunkeln.

Mich interessieren hauptsächlich die Daten des t_DeviceData structs.
t_DeviceInfo wäre auch hilfreich aber eher zum Verständniss bzw. zum Gerätecheck

Nachfolgend habe ich die vorhandenen Daten eingefügt:

// --------------------------------------
// Struktur fuer Gereate-Infos (USB-HID)
// --------------------------------------
typedef struct
{
DWORD idx; // USB-Geraete-Index
int open; // Zustand -> Open=1 / Close=0
char vid_pid[256]; // VID/PID-String
char dev_info[256]; // GeraeteInfo-String
char sn_info[256]; // S/N-String
int hw_info; // HW-Info (5xxx)
unsigned char hw_var; // HW-Variante
int fw_vers; // FW-Version
} t_DeviceInfo;

// --------------------------------------
// Struktur fuer Messwerte eines Geraetes
// --------------------------------------
typedef struct
{
unsigned char me; // Masseinheit (0...x) - siehe oben
float brutto; // Brutto-Sring
float netto; // Netto-Sring
float tara; // Tara-Sring
float min; // Min-Sring
float max; // Max-Sring
unsigned char dec; // Anzahl Nachkommastellen
unsigned char status; // Status-Byte
} t_DeviceData;

//---

// --------------------------------------
// Funktions-Prototypen der DLL
// --------------------------------------
extern "C" DLL_API int Init(void);
extern "C" DLL_API int Search(t_DeviceInfo *p_dev_info);
extern "C" DLL_API int Open(int list_idx);
extern "C" DLL_API int Close(int list_idx);
extern "C" DLL_API int ReadData(int list_idx, t_DeviceData *dev_dat);
extern "C" DLL_API int Null(int list_idx);
extern "C" DLL_API int GrossNet(int list_idx);
extern "C" DLL_API int ChangeUnit(int list_idx);
extern "C" DLL_API int ChangeRange(int list_idx);
extern "C" DLL_API int Reset(int list_idx);
extern "C" DLL_API int DLLVersion(void);

//---
____________________________________________________________

Das Lesen der aktuellen Messwerte erfolgt über die Funktion:

int ReadData(int list_idx, t_DeviceData *dev_dat);

list_idx - ist eine int16 Wert mit der Nr. des Meßgerätes (hier 1)
Ich gehe mal davon aus dass es sich hier NICHT um eine Windows 3.1 DLL handelt und dann ist int doch echt ein 32 Bit Integer. Sorry!

Zitat:t_DeviceDate - ist die Struktur (siehe struct oben)
*dev_dat - der Pointer

Ich scheitere einfach an der Übergabe der Struktur als Pointer.
Die Structur habe ich in einem Cluster abgelegt bestehend aus:

(U8,double,double,double,double,double,U8,U8)

Vielleicht kann mir jemand einen Tip geben ob dies soweit korrekt ist und
wie die Sache mit dem Pointer zu realisieren ist.

Ganz einfach den Parameter als Adapt To Type konfigurieren und den Cluster anschliessen.

Aber!!! LabVIEW Cluster sind immer Byte Packed in 32 Bit Windows. Deine Struktur ist wahrscheinlich default aligned was bei MS Visual C 8 Byte ist. Das heisst die Struktur sollte zwischen Deinem ersten U8 und dem ersten double auch noch 7 dummy filler chars haben.

Und nein, die DeviceInfo Struktur ist leider wesentlich weniger einfach zu machen, obwohl auch das geht.


RE: DLL-Einbindung, Struct mit verschiedenen Datentypen als Pointer übergeben - hawk72 - 08.05.2012 09:41

Danke erstmal für die Antwort,

ich habe gestern die Sache nochmal durchgetestet, untestützt durch einen C++ Spezi.
Leider sind wir zu keinem Ergebniss gekommen da die
"DeviceInfo" struktur als array[16] zwingend angelegt werden muss und nun hat es eher dort "geklemmt".
Die DeviceData Struktur greift dann auf das in der DeviceInfo Struktur hinterlegte Gerät zu.

"Die Deklaration der Datenstruktur/des Datenarrays für die max. 16 zu verwaltenden Geräte sieht folgendermaßen aus.

t_DeviceInfo myDeviceInfo[16];

Diese muss vom aufrufenden Programm intern angelegt werden. Der Zeiger auf diese Datenstruktur bzw. das Datenarray wird jeweils übergeben."


Wir haben nun verschiedenste Möglichkeiten durchgetestet um die
Deviceinfo - Struktur in LV abzubilden aber ohne Erfolg

DWORD idx; // USB-Geraete-Index
int open; // Zustand -> Open=1 / Close=0
char vid_pid[256]; // VID/PID-String
char dev_info[256]; // GeraeteInfo-String
char sn_info[256]; // S/N-String
int hw_info; // HW-Info (5xxx)
unsigned char hw_var; // HW-Variante
int fw_vers; // FW-Version

Angefangen haben wir mit einem Array mit Cluster (int32,int32,(String array[256],String array[256],String array[256],String array[256]),int32,U8,Int32))

Wobei der C-Spezi sich bei der DWORD und Char Sache nicht ganz sicher war.

Müßten es nicht 3 dummy filler chars sein beim U8 der DeviceData Struktur und auch hier?

Sind wir auf dem richtigen Weg oder müßten wir die String-Array eventuell als Stringcluster umsetzen?

Grüße Norbert


RE: DLL-Einbindung, Struct mit verschiedenen Datentypen als Pointer übergeben - rolfk - 08.05.2012 10:23

(08.05.2012 09:41 )hawk72 schrieb:  Danke erstmal für die Antwort,

ich habe gestern die Sache nochmal durchgetestet, untestützt durch einen C++ Spezi.
Leider sind wir zu keinem Ergebniss gekommen da die
"DeviceInfo" struktur als array[16] zwingend angelegt werden muss und nun hat es eher dort "geklemmt".
Die DeviceData Struktur greift dann auf das in der DeviceInfo Struktur hinterlegte Gerät zu.

"Die Deklaration der Datenstruktur/des Datenarrays für die max. 16 zu verwaltenden Geräte sieht folgendermaßen aus.

t_DeviceInfo myDeviceInfo[16];

Diese muss vom aufrufenden Programm intern angelegt werden. Der Zeiger auf diese Datenstruktur bzw. das Datenarray wird jeweils übergeben."


Wir haben nun verschiedenste Möglichkeiten durchgetestet um die
Deviceinfo - Struktur in LV abzubilden aber ohne Erfolg

DWORD idx; // USB-Geraete-Index
int open; // Zustand -> Open=1 / Close=0
char vid_pid[256]; // VID/PID-String
char dev_info[256]; // GeraeteInfo-String
char sn_info[256]; // S/N-String
int hw_info; // HW-Info (5xxx)
unsigned char hw_var; // HW-Variante
int fw_vers; // FW-Version

Angefangen haben wir mit einem Array mit Cluster (int32,int32,(String array[256],String array[256],String array[256],String array[256]),int32,U8,Int32))

Wobei der C-Spezi sich bei der DWORD und Char Sache nicht ganz sicher war.

Müßten es nicht 3 dummy filler chars sein beim U8 der DeviceData Struktur und auch hier?

Sind wir auf dem richtigen Weg oder müßten wir die String-Array eventuell als Stringcluster umsetzen?

Grüße Norbert

Also DWORD ist ein unsigned long was unter Windwos immer ein unsigned 32 Bit integer ist.
Ob dort drei Fillerbytes kommen oder nicht ist nicht sicher zu sagen ohne die komplete Header Datei zu sehen, und eventuel sogar das Makefile.

Microsoft Visual C benützt default 8 Byte Alignment was heissen würde, dass das letzte int tatsächlich auf 4 Byte aligned würde, aber das kann man mit #pragma pack(x) Preprocessordirektiven im Code ändern und das Defaultalignment lässt sich auch als Commandlineparameter zum C Compiler abänderen, was dann im Makefile oder Projekt sichtbar wäre.

Zu guter letzt Dein C Spezialist sollte wissen, dass fixed-size Arrays innerhalb einer Struktur inlined sind und absolut nicht dasselbe als ein Arraypointer. Und LabVIEW kennt keine entsprechenden fixed-size Arrays (nun es kennt sie schon aber nur für die FPGA Umgebung) also kann man nicht einfach einen LabVIEW String in einen Cluster setzen und hoffen dass das schon gut geht. Das ginge nicht einmal wenn die Struktur dort einen Stringpointer erwartet, denn LabVIEW String ist nicht gleich C String Pointer.

Anstelle davon muss man den fixed-size Array (String) durch einen Cluster mit entsprechender Anzahl kompatibler Elemente nachbilden, also in diesem Fall je 256 U8 Integern.

Persönliche erachte ich es einfacher um auszurechnen wieviele Bytes diese Struktur belegt (alignment Fillerbytes nicht vergessen), dann mit der Anzahl der Strukturelemente im Array multiplizieren und dies dann als Bytearray anzulegen und so an die Funktion zu übergeben. Nach dem Aufruf kann man dann die Daten aus dem Array herausklauben. Mühselig ja, und die beste Variante ist dann auch, um eine Wrapper DLL in C zu schreiben die das Ganze von diesen C Strukturen in LabVIEW freundlichere Datentypen umsetzt. Vorausgesezt dass man genug C kennt, um eine solche Wrapper DLL zu machen ist das die einfachste und auf lange Frist am besten unterhaltbare Variante. Und wenn man nicht genug C Kenntnisse hat um eine solche Wrapper DLL zu machen ist man eigentlich schon zum vorneherein zum Scheitern verurteilt um das auch ohne Wrapper DLL ganz im LabVIEW Diagramm zu machen.


RE: DLL-Einbindung, Struct mit verschiedenen Datentypen als Pointer übergeben - hawk72 - 11.05.2012 14:41

Rolf, danke für Deine Hinweise.

Das Ganze wurde mit #pragma pack(1) kompiliert also Byte allignment.
Letztendlich konnte das Problem nach Rücksprache und mit Unterstützung
des Hersteller gelöst werden.
Die Lösung beruht auf Deinem Vorschlag

"Persönliche erachte ich es einfacher um auszurechnen wieviele Bytes diese Struktur belegt (alignment Fillerbytes nicht vergessen), dann mit der Anzahl der Strukturelemente im Array multiplizieren und dies dann als Bytearray anzulegen und so an die Funktion zu übergeben. Nach dem Aufruf kann man dann die Daten aus dem Array herausklauben"

Ein Problem bei unserer Versuchen war wohl auch das Verhalten der DLL, einen Fehler zu produzieren nachdem Sie mit den falschen Übergabeparametern aufgerufen wurden. Weitere Versuche bringen dann meist nicht viel und der Rechner muß neu gestartet werden damit ein beschreibungskonformes Verhalten gegeben ist.

Gruß Norbert