27.08.2014, 07:12
(Dieser Beitrag wurde zuletzt bearbeitet: 27.08.2014 07:17 von galilio.)
|
|
|
27.08.2014, 23:09
(Dieser Beitrag wurde zuletzt bearbeitet: 27.08.2014 23:28 von rolfk.)
|
rolfk
LVF-Guru
Beiträge: 2.306
Registriert seit: Jun 2007
alle seit 6.0
1992
EN
2901GG
Niederlande
|
RE: Labview Import / DLL debuggen
(27.08.2014 07:12 )galilio schrieb: @rolfk
danke für deine Antwort.
-->rolfk schrieb:
CEFI_Out_Wrapper scheint nicht verwendet aber den solltest Du auch nicht als Funktionsparameter/Rückgabewert irgendwo verwenden.
sondern wie mache ich das denn ?
LabVIEW Strings sind KEINE C Strings. Du kannst also nicht LabVIEW Strings in einen Cluster stopfen und hoffen dass die C String Pointer automagisch in LabVIEW Strings umgewandelt werden. Du musst also einen Cluster machen wo anstelle des C String Pointers ein Integer ist. Dieser muss 32 bit sein wenn Du in LabVIEW 32 Bit bist und 64 Bit wenn Du in LabVIEW 64 Bit bist. Danach musst Du nachdem die Funktion den Cluster zurückgegeben hat jeden Pointer einzeln in einen LabVIEW String kopieren. Dies geschieht durch Aufruf von LabVIEW Manager Funktionen wie in diesem Thread erklärt (in Englisch).
Und wenn die Strings nicht irgendwie statisch in der DLL liegen sondern mit malloc oder ähnlichem angelegt werden musst Du danach diese auch wieder deallozieren. Das darfst Du nicht mit LabVIEW Funktionen machen sondern dafür musst Du eine weitere Funktion exportieren die mit alle Pointer mit den entsprechenden Funktionen in Deiner Library dealloziert.
Wenn Du diese Struktur über den Funktionsrückgabewert übergibst musst Du ausserdem auch noch den Cluster vom Rückgabepointer in eine LabVIEW Cluster kopieren. Wenn Du das hingegen über einen Funktionsparameter machst kannst Du diesen in der Call Library Node als Adapt to Type konfigurieren und dann eine LabVIEW Cluster mit der richtigen Datenstruktur daran verbinden. Dann füllt die Funktion diese ein.
Ausserdem hast Du dann noch das Problem von Alignment. Das heisst dass der C Compiler normalerweise (default Einstellungen) alle Elemente in einer struct auf eine Adresse legt die ein Vielfaches des kleineren Wertes der beiden Grössen Elementgrösse und Defaultalignment (Visual C benützt hier default 8) ist.
Hier ein Beispiel:
Code:
struct
{
char val;
char *string;
}
wird in 32 Bit im Speicher durch den C Compiler so angelegt, bei default Einstellungen:
Code:
struct
{
char val;
char filler[3]; // The following pointer uses 4 Bytes so it must start on a multiple of 4 bytes from the start of the structure.
char *string;
}
und in 64 Bit:
Code:
struct
{
char val;
char filler[7]; // The following pointer uses 8 Bytes so it must start on a multiple of 8 bytes from the start of the structure.
char *string;
}
LabVIEW verwendet aber unter Windows für die eigenen Datenstrukturen das sogenannte Packed Format. D.h. es fügt keine Filler in die Cluster ein. Das ist kein Fehler, denn das Alignment bei C Compilern ist mittels Compiler Optionen einstellbar. D.h. wenn LabVIEW das Visual C Default Alignment verwenden würde wäre das Interfacing zu externen Libraries die mit einem anderen Alignment kompiliert wurden fast unmöglich. So muss man nur selber daran denken dass man bei den Clustern die man an externe Funktionen übergibt das entsprechende Alignment durch extra Dummyelemente garantiert.
BOOL ist nicht gleich bool. Das erste ist ein Windows API Datentyp der tatasächlich ein 32 Bit Integer ist. Das zweite ist ein C11 Datentyp (nur in C++ und allerneusten C Compilern unterstützt). C11 spezifiziert keinen bestimmte Datengrösse sondern überlässt dies den Compilerbauern. Praktischerweise verwendet zumindest Visual C hier ein Byte, aber anderen Compilern ist es freigestellt etwas anderes zu verwenden.
EFILib ist eigentlich ein Object-Pointer. Die Grösse von einem Pointer ist abhängig vom Memorymodell. Konfiguriere diese Parameter deshalb als Pointer sized Integer und verwende im LabVIEW Diagramm einen U64.
|
|
|
04.09.2015, 17:10
(Dieser Beitrag wurde zuletzt bearbeitet: 13.09.2015 10:09 von galilio.)
|
galilio
LVF-Gelegenheitsschreiber
Beiträge: 175
Registriert seit: Mar 2014
2015
2013
EN
Schweiz
|
RE: Labview Import / DLL debuggen
Hallo Rolf,
Zurück zum meinen ersten Beitrag im bezug auf das Dummy Projekt "http://www.labviewforum.de/Thread-Labview-Import-DLL-debuggen".
Wenn ich dieses Dummy Projekt in Labview importiere dann funktioniert.
Bei diesem Import stört mich dieses Funktion:
Code:
DLLIMPORT Wrapper createWrapper(double a, double b)
{
Wrapper wrapper = {static_cast<void*>(new MyClass(a,b)) };
return wrapper;
}
Wie soll ich die Rückgabe dieses Funktion auf Labview Seite initialisieren?
Was ist mit der anderen Funktinoen:
Code:
DLLIMPORT void destoryWrapper(Wrapper LV_ref)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
delete myClass;
}
DLLIMPORT double Add(Wrapper LV_ref, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
return myClass->Add(a, b);
}
DLLIMPORT double Mult(Wrapper LV_ref, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
return myClass->Mult(a, b);
}
DLLIMPORT double Subst(Wrapper LV_ref, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
return myClass->Subst(a, b);
}
DLLIMPORT double Divid(Wrapper LV_ref, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
return myClass->Divid(a, b);
}
Danke nochmal
Danke in voraus
|
|
|
14.09.2015, 18:11
|
galilio
LVF-Gelegenheitsschreiber
Beiträge: 175
Registriert seit: Mar 2014
2015
2013
EN
Schweiz
|
RE: Labview Import / DLL debuggen
Hi,
ich stehe wirklich auf dem schlauch und weiss es nicht wie es weiter geht
|
|
|
16.09.2015, 10:50
(Dieser Beitrag wurde zuletzt bearbeitet: 16.09.2015 11:11 von rolfk.)
|
rolfk
LVF-Guru
Beiträge: 2.306
Registriert seit: Jun 2007
alle seit 6.0
1992
EN
2901GG
Niederlande
|
RE: Labview Import / DLL debuggen
(04.09.2015 17:10 )galilio schrieb: Hallo Rolf,
Zurück zum meinen ersten Beitrag im bezug auf das Dummy Projekt "http://www.labviewforum.de/Thread-Labview-Import-DLL-debuggen".
Wenn ich dieses Dummy Projekt in Labview importiere dann funktioniert.
Bei diesem Import stört mich dieses Funktion:
Code:
DLLIMPORT Wrapper createWrapper(double a, double b)
{
Wrapper wrapper = {static_cast<void*>(new MyClass(a,b)) };
return wrapper;
}
Wie soll ich die Rückgabe dieses Funktion auf Labview Seite initialisieren?
Was ist mit der anderen Funktinoen:
Code:
DLLIMPORT void destoryWrapper(Wrapper LV_ref)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
delete myClass;
}
DLLIMPORT double Add(Wrapper LV_ref, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
return myClass->Add(a, b);
}
DLLIMPORT double Mult(Wrapper LV_ref, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
return myClass->Mult(a, b);
}
DLLIMPORT double Subst(Wrapper LV_ref, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
return myClass->Subst(a, b);
}
DLLIMPORT double Divid(Wrapper LV_ref, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(LV_ref.MYClass);
return myClass->Divid(a, b);
}
Wrapper ist ein C++ Objekt. Als solches ist es für Standard C ein Datenpointer.
Wenn Du dies in LabVIEW jeweils als pointersized Integer konfigurierst sollte es funktionieren. Das hindert einen LabVIEW Programmierer aber nicht mit diesem Integer irgendwelche Kapriolen zu machen wie etwa irgendeinen Wert hinzuzuaddieren oder etwas anderes sinnloses. Dann zeigt der Pointer aber nicht mehr auf das Objekt und beim nächsten Aufruf einer DLL Funktion mit diesem modifizierten "Pointer" krachts gewaltig.
Also pointersized Integer in der Call Library Configuration ist die Lösung aber als VI Library für den Konsum von Durchsnchitt LabVIEW Benützern ist das unzureichend.
Der ganze Klimbim mit static_cast<> erscheint mir übrigens sinnlos. Da kannst Du auch gleich das eigentlich C Objekt als Pointer verwenden statt da ein sinnloses Wrapperobjekt dazwischen zu schalten.
Ein Wrapperobjekt würde nur Sinn machen wenn Du da auch wirklich Wrapperfunktionen definierst etwa um zwischen LabVIEW Datentypen nach C++ und zurück zu konvertieren aber das kannst Du auch in den extern C Wrapperfunktionen direkt machen, die Du ja für den LabVIEW Aufruf erstellen solltest.
Warum nicht einfach etwas in dieser Art??
Code:
extern "C" MyClass* __declspec(dllexport) createMyClass(double a, double b)
{
return new MyClass(a,b);
}
|
|
|
17.09.2015, 17:35
|
galilio
LVF-Gelegenheitsschreiber
Beiträge: 175
Registriert seit: Mar 2014
2015
2013
EN
Schweiz
|
RE: Labview Import / DLL debuggen
Hallo Rolf,
Danke für deinen Tipp.
Ich habe es jetzt meine Dammy Projekt so angepasst:
Header File:
Code:
#pragma once
#ifdef __cplusplus
#endif
extern "C"__declspec(dllexport) void* createWrapper(double a, double b);
extern "C"__declspec(dllexport) void destoryWrapper(void *instance);
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b);
.....
#ifdef __cplusplus
#endif
Source Code:
Code:
#include "stdafx.h"
#include "MyClass.h"
#include "C_DllWrapper.h"
extern "C"__declspec(dllexport) void* createWrapper(double a, double b)
{
return new MyClass(a, b);
}
extern "C"__declspec(dllexport) void destoryWrapper(void *instance)
{
MyClass *myClass = static_cast<MyClass*>(instance);
delete myClass;
}
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b)
{
MyClass *myClass = reinterpret_cast<MyClass*>(instance);
return myClass->Add(a, b);
}
....
static_cast<> und reinterpret_cast<> brauche ich um das Pointer Objekt MyClass
zu casten sonst bekomme ich stets eine Fehlermeldung
|
|
|
18.09.2015, 18:38
|
galilio
LVF-Gelegenheitsschreiber
Beiträge: 175
Registriert seit: Mar 2014
2015
2013
EN
Schweiz
|
RE: Labview Import / DLL debuggen
(17.09.2015 17:35 )galilio schrieb: Hallo Rolf,
Danke für deinen Tipp.
Ich habe es jetzt meine Dammy Projekt so angepasst:
Header File:
Code:
#pragma once
#ifdef __cplusplus
#endif
extern "C"__declspec(dllexport) void* createWrapper(double a, double b);
extern "C"__declspec(dllexport) void destoryWrapper(void *instance);
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b);
.....
#ifdef __cplusplus
#endif
Source Code:
Code:
#include "stdafx.h"
#include "MyClass.h"
#include "C_DllWrapper.h"
extern "C"__declspec(dllexport) void* createWrapper(double a, double b)
{
return new MyClass(a, b);
}
extern "C"__declspec(dllexport) void destoryWrapper(void *instance)
{
MyClass *myClass = static_cast<MyClass*>(instance);
delete myClass;
}
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b)
{
MyClass *myClass = reinterpret_cast<MyClass*>(instance);
return myClass->Add(a, b);
}
....
static_cast<> und reinterpret_cast<> brauche ich um das Pointer Objekt MyClass
zu casten sonst bekomme ich stets eine Fehlermeldung
Diesem Code kann ich mit Visual Studio und LabVIEW in debug Modus durchlaufen.
Es ist keine Fehler aufgetreten.
Nun habe ich Verständnis Frage:
Als erste muss ich eigentlich das Objekt MyClass erst erzeugen können und das tue in dem ich
die Funktion createWrapper(double a, double b) aufrufe (damit wird das Objekt erzeugzt).
Bis hier ist alles okay.
Wenn ich diese Funktion z.B. :
header Files:
Code:
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b);
Source Files:
Code:
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(instance);
return myClass->Add(a, b);
}
aufrufe, wie kann ich meinem Objekt (Pointer) "MyClass" auf LabVIEW Seite für dieses Funktion bekannt machen?
|
|
|
18.09.2015, 19:50
(Dieser Beitrag wurde zuletzt bearbeitet: 18.09.2015 19:54 von rolfk.)
|
rolfk
LVF-Guru
Beiträge: 2.306
Registriert seit: Jun 2007
alle seit 6.0
1992
EN
2901GG
Niederlande
|
RE: Labview Import / DLL debuggen
(18.09.2015 18:38 )galilio schrieb: (17.09.2015 17:35 )galilio schrieb: Hallo Rolf,
Danke für deinen Tipp.
Ich habe es jetzt meine Dammy Projekt so angepasst:
Header File:
Code:
#pragma once
#ifdef __cplusplus
#endif
extern "C"__declspec(dllexport) void* createWrapper(double a, double b);
extern "C"__declspec(dllexport) void destoryWrapper(void *instance);
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b);
.....
#ifdef __cplusplus
#endif
Source Code:
Code:
#include "stdafx.h"
#include "MyClass.h"
#include "C_DllWrapper.h"
extern "C"__declspec(dllexport) void* createWrapper(double a, double b)
{
return new MyClass(a, b);
}
extern "C"__declspec(dllexport) void destoryWrapper(void *instance)
{
MyClass *myClass = static_cast<MyClass*>(instance);
delete myClass;
}
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b)
{
MyClass *myClass = reinterpret_cast<MyClass*>(instance);
return myClass->Add(a, b);
}
....
static_cast<> und reinterpret_cast<> brauche ich um das Pointer Objekt MyClass
zu casten sonst bekomme ich stets eine Fehlermeldung
Diesem Code kann ich mit Visual Studio und LabVIEW in debug Modus durchlaufen.
Es ist keine Fehler aufgetreten.
Nun habe ich Verständnis Frage:
Als erste muss ich eigentlich das Objekt MyClass erst erzeugen können und das tue in dem ich
die Funktion createWrapper(double a, double b) aufrufe (damit wird das Objekt erzeugzt).
Bis hier ist alles okay.
Wenn ich diese Funktion z.B. :
header Files:
Code:
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b);
Source Files:
Code:
extern "C"__declspec(dllexport) double Add(void *instance, double a, double b)
{
MyClass *myClass = static_cast<MyClass*>(instance);
return myClass->Add(a, b);
}
aufrufe, wie kann ich meinem Objekt (Pointer) "MyClass" auf LabVIEW Seite für dieses Funktion bekannt machen?
Kommt drauf an was Du hier meinst. Wenn es nur darum geht dies lauffähig zu machen dann sage LabVIEW halt einfach dass es ein pointer sized integer ist:
Code:
void *instance Pointer to some memory location
sagt es ja eigentlich schon. Es ist ein Pointer. LabVIEW kennt selber keine Pointer aber ein Pointer ist eigentlich einfach eine Integernummer. Vor LabVIEW 2009 war das immer equivalent zu einem 32 Bit Integer. Seit LabVIEW 2009 gibt es auch eine 64 Bit Version sodass das nicht mehr stimmt und daher hat LabVIEW in der Call Library Node hinzugelernt und kennt jetzt auch einen pointer sized Integer der je nach Bitgrösse von LabVIEW korrekt übergeben wird. Auf dem LabVIEW Diagram ist es IMMER ein 64 Bit Integer (das KGV von 32 und 64).
Wenn Du da ein richtiges seperates Kontrol haben willst, so dass ein Dummyuser nicht per Unglück seinen Geburtstag als MyClass Pointer eingeben kann und Deine DLL dann grausam crasht, dann hast Du ziemlich grosses Pech. Der alte Trick mit der Datalog Refnum mit einem speziellen Enum drin, sodass Du eine spezielle Refnum bekommst die nur zu sich selber verbunden werden kann, funktioniert leider nicht mehr. Da LabVIEW Refnums immer 32 bit sind, würdest Du so Deine 64 Bit Pointer korrumpieren wenn Du sie als LabVIEW refnum behandeln möchtest. Und nein, Refnums heutzutage dazu zu verwenden weil Deine DLL eh nur als 32 Bit DLL kompiliert wird ist keine gute Option mehr. Windows 10 ist nur noch 32 Bit wenn es von einer alten 32 bit Installation geupgradet wird ansonsten ist es nur noch als 64 Bit Version erhältlich. In ein paar Jahren bist Du ein Freak wenn Du 32 Bit Applikationen bei einem Kunden installieren willst.
Der saubere Workaround hier wäre wohl eine LabVIEW Klasse. Der Pointer wird dann als private data der Klasse behandelt und Du schreibst für jede Methode in Deiner DLL eine Klassenmethode als VI. somit kommt der Dummyuser nicht mehr an den Pointer und kann ihn nicht ungültig machen.
|
|
|
01.10.2015, 13:53
(Dieser Beitrag wurde zuletzt bearbeitet: 01.10.2015 13:56 von galilio.)
|
galilio
LVF-Gelegenheitsschreiber
Beiträge: 175
Registriert seit: Mar 2014
2015
2013
EN
Schweiz
|
RE: Labview Import / DLL debuggen
Ich brauche eure Hilfe:
Wie kann ich einen Funktionen mit einen Pointer auf Struct als Rückgabeparameter in Labview importieren?
--> Das Import an sich ist kein problem :
Problem bei Labview : die notwendige Anpassung in Labview (stehe auf dem schauch)
Anbei einen Codeabschnitt:
Header file
Code:
typedf struct MyWrapper
{
double a1;
double a2;
double a3;
double a4;
double a5;
double a6;
double a7;
double a8;
double a9;
double a10;
const char * dString;
double a11;
} MyWrapper;
....
extern "C"__declspec (dllexport) MyWrapper* ReturningAValue_PointerToSimpleStruct(void);
.....
Cpp file:
Code:
.....
extern "C"__declspec (dllexport) MyWrapper* ReturningAValue_PointerToSimpleStruct(void)
{
struct MyWrapper tempMyWrapper = {1.3,2.4,3.5,50.1,50.8,10.11,4.78,2.5,2.8,4.9,"STOP",10000.0}; // einfach zum testen
struct MyWrapper *temp = &tempMyWrapper;
return temp;
}
.....
|
|
|
01.10.2015, 23:15
(Dieser Beitrag wurde zuletzt bearbeitet: 01.10.2015 23:17 von rolfk.)
|
rolfk
LVF-Guru
Beiträge: 2.306
Registriert seit: Jun 2007
alle seit 6.0
1992
EN
2901GG
Niederlande
|
RE: Labview Import / DLL debuggen
(01.10.2015 13:53 )galilio schrieb: Ich brauche eure Hilfe:
Wie kann ich einen Funktionen mit einen Pointer auf Struct als Rückgabeparameter in Labview importieren?
--> Das Import an sich ist kein problem :
Problem bei Labview : die notwendige Anpassung in Labview (stehe auf dem schauch)
Anbei einen Codeabschnitt:
Header file
Code:
typedf struct MyWrapper
{
double a1;
double a2;
double a3;
double a4;
double a5;
double a6;
double a7;
double a8;
double a9;
double a10;
const char * dString;
double a11;
} MyWrapper;
....
extern "C"__declspec (dllexport) MyWrapper* ReturningAValue_PointerToSimpleStruct(void);
.....
Cpp file:
Code:
.....
extern "C"__declspec (dllexport) MyWrapper* ReturningAValue_PointerToSimpleStruct(void)
{
struct MyWrapper tempMyWrapper = {1.3,2.4,3.5,50.1,50.8,10.11,4.78,2.5,2.8,4.9,"STOP",10000.0}; // einfach zum testen
struct MyWrapper *temp = &tempMyWrapper;
return temp;
}
.....
Das geht nicht wirklich. Und mit dem Stringpointer darin wird es noch hässlicher zu machen.
Wenn Du es wirklich machen willst ohne Wrapper, musst Du den Parameter als pointer sized Integer konfigurieren und dann halt selber die entsprechenden Elemente mit zum Beispiel MoveBlock() herauskopieren.
Da Du aber ja schon einen Wrapper machst solltest Du das ganz vermeiden. Behandle es als ein Pointer ohne in LabVIEW Dinge herauskopieren zu wollen und exportiere entsprechende Accessfunktionen denen Du diesen Pointer übergeben kannst und die Dir dann die Elemente daraus zurückgeben. Oder mache die ganze Funktion gleich ganz LabVIEW freundlich.
|
|
|
| |