Overview
|
LabVIEW für Windows XP und für Windows Vista verfügt über eine „Call Library Function“, die den Zugriff auf 32-bit-DLLs erlaubt. Dieses Dokument bietet eine kurze Einführung in die Erstellung einfacher 32-bit-DLLs und deren Aufruf unter LabVIEW für Windows XP und Vista. Auf die C-Programmierung und die verschiedenen Compiler zur Erstellung von DLLs wird hier nicht eingegangen. Da in etlichen Entwicklungsumgebungen das Erzeugen von DLLs Standard ist, sollte es möglich sein, fast jede beliebige Entwicklungsumgebung zu nutzen, um eine DLL zu erstellen und diese aus LabVIEW aufzurufen.
Einführende Informationen zur „Call Library Function“ in LabVIEW stehen im Dokument Wie werden Win32 Dynamic Link Libraries (DLLs) von LabVIEW aufgerufen? zur Verfügung. Gegenwärtig verfügt LabVIEW nicht über Support für den Aufruf einer 64-bit-DLL, wie unter dem Link Kann eine 64-bit-DLL aus LabVIEW aufgerufen werden? erläutert wird.
Table of Contents
Die Anatomie einer DLL
Das Dynamic Linking ist ein Mechanismus, der Anwendungen während der Laufzeit mit Bibliotheken verbindet. Die Bibliotheken bleiben in ihren eigenen Dateien und werden nicht in die ausführbaren Dateien der Anwendung kopiert. DLLs erstellen eine Verbindung zu einer Anwendung, wenn diese ausgeführt und nicht, wenn sie erstellt wird. DLLs können auch Links zu anderen DLLs beinhalten.
Hinweis: Häufig werden DLLs in Dateien mit unterschiedlichen Endungen wie beispielsweise .EXE, .DRV oder .DLL abgelegt.
Anwendungen und DLLs können automatisch eine Verbindung zu anderen DLLs herstellen, falls die DLL-Verknüpfung im IMPORTS-Abschnitt der Moduldefinitionsdatei festgelegt und kompiliert wurde. Sie können auch explizit mithilfe der Funktion Windows LoadLibrary geladen werden.
Kurze Darstellung des Quellcodes einer DLL
Der folgende Beispielcode veranschaulicht die grundlegende Struktur einer DLL:
BOOL WINAPI DllMain(HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
/* Init Code here */
break;
case DLL_THREAD_ATTACH:
/* Thread-specific init code here */
break;
case DLL_THREAD_DETACH:
/* Thread-specific cleanup code here.
*/
break;
case DLL_PROCESS_DETACH:
/* Cleanup code here */
break;
}
/* The return value is used for successful DLL_PROCESS_ATTACH */
return TRUE;
}
/* One or more functions */
__declspec (dllexport) DWORD Function1(.... ){}
__declspec (dllexport) DWORD Function2(.... ){}
Wie aus dem Beispielcode hervorgeht, wird die DllMain-Funktion aufgerufen, wenn eine DLL ge- oder entladen wird. Die DllMain-Funktion wird auch aufgerufen, wenn ein neuer Thread in einem Prozess erstellt wird, der bereits zur DLL gehört, oder ein Thread beendet wurde. Schließlich umfasst eine DLL noch Funktionen, welche die eigentlichen Aufgaben definieren, die die DLL verarbeiten soll. Diese Funktionen werden mithilfe der DllEntryPoint-Funktion exportiert. Das Kennwort _declspec (dllexport) ist eine Microsoft-spezifische Erweiterung der Programmiersprachen C und C++. Der Abschnitt EXPORTS in den Moduldefinitionsdateien kann ebenfalls verwendet werden, um Funktionen zu exportieren.
Beispiel: Erstellen einer DLL mit Microsoft Visual C++
Manchmal ist es erforderlich, dass der Anwender eine eigene DLL erstellt, beispielsweise zur Kommunikation mit seiner benutzerspezifischen Hardware. In diesem Abschnitt wird anhand von Beispielcode gezeigt, wie sich eine einfache DLL erstellen lässt.
Zur Erstellung einer DLL sind die folgenden vier Dateien erforderlich:
- Quelldatei in C (erforderlich)
- Benutzerspezifische Header-Datei (optional – kann Teil des Quellcodes sein)
- Moduldefinitionsdatei [eventuell erforderlich, falls die _stdcall-Aufrufkonvention verwendet wird – oder Funktionen können mit dem Kennwort _declspec (dllexport) exportiert werden]
- Make-Datei oder Einstellungen der Compiler-Optionen zur Erzeugung einer DLL (erforderlich – einige Entwicklungsumgebungen erstellen die Make-Datei und führen sie aus)
C-Quellcodedatei
Der folgende Programmcode ist die C-Quellcodedatei für die DLL, die in diesem Beispiel erstellt werden soll. Er verwendet die C-Aufrufkonvention. Zudem werden hier Funktionen unter Nutzung des Kennworts _declspec (dllexport) exportiert. Die Angabe des dllexport-Kennworts macht eine Moduldefinitionsdatei überflüssig. Soll jedoch die Standard-Aufrufkonvention mit dem Kennwort _stdcall verwendet werden, müssen eventuell die Funktionen mithilfe der Moduldefinitionsdatei in die DLL exportiert werden. Das kann erforderlich sein, weil _stdcall die Namen der Funktionen verstümmelt. Kann der verstümmelte Name gefunden werden, ist es immer noch möglich, die Funktion mithilfe dieses Namens aufzurufen.
Die Beispiel-DLL definiert drei einfache Funktionen:
- add_num addiert zwei Integer-Werte
- avg_num ermittelt den einfachen Durchschnitt eines Arrays numerischer Werte
- numIntegers bestimmt die Anzahl der Integer-Werte in einem String
In diesem Beispiel wird ein VI erstellt, das nur eine dieser Funktionen aufruft. Als weiterführende eigenständige Übung können VIs erstellt werden, die „Call Library Function“ nutzen, um die anderen Funktionen der DLL aufzurufen.
Nach der Auflistung des Quellcodes für die DLL folgt eine Auflistung für die benutzerspezifische Header-Datei, ourdll.h.
/* OurDLL.c source code */
#include "stdafx.h"
#include <windows.h>
#include <string.h>
#include <ctype.h>
#include "ourdll.h"
BOOL WINAPI DllMain (
HANDLE hModule,
DWORD dwFunction,
LPVOID lpNot)
{
return TRUE;
}
/* Add two integers */
_declspec (dllexport) long add_num(long a, long b){
return((long)(a+b));}
/* This function finds the average of an array of single precision numbers */
_declspec (dllexport) long avg_num(float *a, long size, float *avg)
{
float sum=0;
if(a != NULL)
{
for(int i=0;i < size; i++)
sum = sum + a[i];
}
else
return (1);
*avg = sum / size;
return (0);
}
// Counts the number of integer numbers appearing in a string. */
// Note that this function does not check for sign, decimal, or exponent
_declspec (dllexport) unsigned int numIntegers (char *inputString) {
int lastDigit = 0;
int numberOfNumbers = 0;
int stringSize;
stringSize = strlen(inputString);
for(int i = 0; i < stringSize; i++)
{
if (!lastDigit && isdigit(inputString[i]))
numberOfNumbers++;
lastDigit = isdigit(inputString[i]);
}
return numberOfNumbers;
}
Hinweis: LabVIEW kann DLLs aufrufen, welche die Aufrufkonvention stdcall und die C-Aufrufkonventionen nutzen.
Die Header-Datei
Der folgende Code stellt den Inhalt der Header-Datei OurDLL.h dar. Alle Funktionen, die im Quellcode erstellt wurden, stehen anderen Anwendungen zur Verfügung, da sie mit dem Kennwort _declspec (dllexport) exportiert wurden.
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD,LPVOID);
_declspec (dllexport) long add_num(long, long);
_declspec (dllexport) long avg_num(float *, long, float *);
_declspec (dllexport) unsigned int numIntegers (unsigned char *);
Die Moduldefinitionsdatei
Eine DLL kann eine zugehörige Moduldefinitionsdatei (.def) haben. Die .def-Datei enthält die Anweisungen zur Definition einer DLL, beispielsweise den Namen der DLL und die Funktionen, die sie exportiert. Die Option LINK in den Projekteinstellungen des Compilers von Visual C++ stellt entsprechende Kommandozeilenoptionen für die meisten Moduldefinitionsanweisungen bereit. Daher erfordert ein typisches Programm für Win32 gewöhnlich keine .def-Datei. Die einzigen Pflichteingaben in die .def-Dateien sind die Anweisung LIBRARY und die Anweisung EXPORT. Die Anweisung LIBRARY muss die erste Anweisung in der Datei sein. Der in der Anweisung festgelegte Name identifiziert die Bibliothek in der Importbibliothek der DLL. Die EXPORTS-Anweisung führt die Namen der Funktionen auf, die von der DLL exportiert wurden.
Wurde die Aufrufkonvention _stdcall in der DLL verwendet, dann muss die Moduldefinitionsdatei für den Export der Funktionen genutzt werden, die die DLL bereitstellt (siehe unten). Dies ist erforderlich, da der Compiler die Funktionsnamen verstümmelt. Die nachfolgende Liste ist die Auflistung der Moduldefinitionsdatei:
LIBRARY OurDLL
EXPORTS
avg_num
add_num
num_Integers
Erstellen eines Projekts in Microsoft Visual C++ 2005
Die meisten Windows-Compiler verfügen über eine integrierte Entwicklungsumgebung und es ist möglich, die Optionen auszuwählen, um ein Projekt als eine DLL zu kompilieren. Zuerst muss das Projekt erstellt werden. Die Schritte zur Erstellung einer Beispiel-DLL werden hier für den Compiler Microsoft Visual C++ 2005 beschrieben. Wählen Sie aus dem Start-Menü Programme->Microsoft Visual Studio 2005->Microsoft Visual Studio 2005, um Microsoft Visual C++ 2005 zu starten. Erstellen Sie als nächstes ein neues Projekt, indem Sie aus dem Dateimenü Neu->Projekt… auswählen. Markieren Sie unter Projekttypen: Visual C++ und unter Vorlagen Win32 Project. Benennen Sie das Projekt OurDLL und bestätigen Sie mit OK. Klicken Sie bei Win32 Application Wizard auf Weiter und wählen Sie unter Applikationstyp DLL. Klicken Sie dann auf Beenden. Ersetzen Sie den Vorlagencode durch den oben aufgeführten Code und fügen Sie dann eine Datei zum Ordner Header-Dateien hinzu. Nennen Sie diese OurDLL.h. Kopieren Sie den Header-Code in die Header-Datei und speichern Sie das Projekt. Um den Code in eine DLL zu kompilieren, wählen Sie aus dem Build-Menü Build Solution. (Sollten Sie einen anderen Projektnamen als OurDLL wählen, dann erscheint dieser Name im Menü Projekt.)
Aufruf der DLL
Diese einfache DLL kann jetzt mithilfe des Knotens „Call Library Function“ in LabVIEW aufgerufen werden. Es soll ein VI namens ArrayAvg.vi erstellt werden, das die Funktion avg_num in OurDLL.dll aufruft. Platzieren Sie dazu eine „Call Library Function“ auf dem Blockdiagramm und konfigurieren Sie diese so, dass der Knoten die Funktion avg_num aufruft (siehe Abbildung 1).
[+] Enlarge Image
a. Funktionsreiter des Knotens Call Library Function
[+] Enlarge Image
b. Parameterreiter des Knotens Call Library Function
Abb. 1: Konfiguration des Funktionsaufrufs avg_num aus OurDLL.dll
Wie im Diagramm bereits festgelegt, muss zuerst der Speicherort der DLL durch Eingabe des Pfads oder das Durchsuchen spezifiziert werden. Der Name der Funktion ist im Dropdown-Menü Function Name zu finden. In diesem Fall ist das avg_num. Die Aufrufkonvention für diese Funktion in der DLL ist C.
Der Rückgabetyp ist ein vorzeichenbehafteter 32-bit-Integer. Die Parameter der Funktion sind ein Zeiger auf ein Array aus Fließkommawerten mit einfacher Genauigkeit von 4 Byte, ein vorzeichenbehafteter 32-bit-Integer, der die Größe des Arrays enthält, sowie ein Zeiger auf einen Fließkommawert mit einfacher Genauigkeit von 4 Byte, der den Durchschnitt der Elemente des Arrays liefert.
Nun wird das Frontpanel gemäß Abbildung 2 a. erstellt. Anschließend werden die entsprechenden Bedien- und Anzeigeelemente mit dem Knoten Call Library Function verbunden. Abbildung 2 b. zeigt die entsprechenden Verbindungen auf dem Blockdiagramm.
a. Frontpanel
b. Blockdiagramm
Abb. 2: ArrayAvg.VI
Nach Erstellen der passenden Verbindungen kann jetzt das VI ausgeführt werden. Dies ist die Vorgehensweise für das Schreiben, Kompilieren, Verlinken und Aufrufen einer DLL-Funktion aus LabVIEW. Sollte die DLL fehlerhafte Ergebnisse ausgeben oder abstürzen, müssen die Datentypen und die Verdrahtung verifiziert werden, um festzustellen, ob die falsche Art von Informationen verbunden wurde.
Array- und String-Optionen
In diesem Abschnitt des Dokuments werden einige wichtige Konzepte betrachtet, die dem Anwender vertraut sein sollten, wenn er die Call Library Function gemeinsam mit Array- und String-Daten einsetzt. Das Verarbeiten von Arrays und Strings nutzt Zeiger (Pointer). Diese Informationen unterstützen den Anwender bei der Implementierung der Call Library Function.
Arrays numerischer Daten
Arrays numerischer Daten können aus beliebigen Integerarten oder aus Fließkommazahlen mit einfacher (4 Byte) oder mit doppelter (8 Byte) Genauigkeit bestehen. Wird ein Array von Daten an eine DLL-Funktion übergeben, bestehen zur Übergabe zwei Optionen: ein Zeiger auf das Array oder ein LabVIEW Array Handle.
Wird ein Zeiger auf das Array übergeben, kann auch die Anzahl der Dimensionen im Array festgelegt werden. Angaben über die Größe der Array-Dimension(en) werden nicht mit eingeschlossen. DLL-Funktionen gehen entweder davon aus, dass die Daten eine spezifische Größe haben oder erwarten, dass die Größe als separate Eingabe übergeben wird. Zudem darf die Größe das Array innerhalb der DLL nie verändert werden, da der Array-Zeiger sich auf LabVIEW-Daten bezieht. Wird das nicht beachtet, kann der Computer abstürzen. Soll ein Array-Daten übergeben werden, muss der Anwender ein Array ausreichender Größe in LabVIEW erzeugen, es an die Funktion übergeben und als Puffer arbeiten lassen. Nehmen die Daten weniger Platz ein, kann die richtige Größe als separater Parameter zurückgegeben werden. Anschließend kann der Anwender im Blockdiagramm die Array-Teilmenge der gültigen Daten extrahieren. Wahlweise können, falls die Array-Daten als ein LabVIEW Array Handle übergeben werden, die LabVIEW-CIN-Funktionen zur Größenänderung des Arrays innerhalb der DLL genutzt werden. Damit man die LabVIEW-CIN-Funktionen aus einer eigenen DLL unter Verwendung des Compilers Visual C++ aufrufen kann, muss die Bibliothek labview.lib (im Verzeichnis LabVIEW x.x/cintools/Win32) enthalten sein. Zudem muss eine Verbindung zur Bibliothek labview.sym.lib (im Verzeichnis LabVIEW x.x/cintools/win32) hergestellt werden, falls der Anwender auf diese CIN-Funktionen mittels des Symantec-Compilers zugreifen möchte.
String-Daten
LabVIEW speichert Strings in einem Format, das sich von Strings in C unterscheidet. Die Call Library Function arbeitet mit LabVIEW String Handles oder String-Zeiger im C-Stil. Der Unterschied zwischen diesen Formaten wird nachfolgend erläutert.
Einen String kann man sich als Array bzw. eine Kette von Zeichen vorstellen, der die Zeichen zusammenführt, um einen String zu bilden. LabVIEW speichert einen String in einem besonderen Format, bei dem die ersten vier Bytes des Arrays einen vorzeichenbehafteten 32-bit-Integer bilden, der die Anzahl der im String enthaltenen Zeichen speichert. Somit benötigt ein String mit n Zeichen n + 4 Bytes, um im Speicher abgelegt zu werden. Beispielsweise enthält der String-Text vier Zeichen. Wenn LabVIEW den String speichert, enthalten die ersten vier Bytes den Wert 4 als eine vorzeichenbehaftete 32-bit-Zahl. Jedes der folgenden vier Bytes enthält ein Zeichen des Strings. Der Vorteil dieser Art der String-Speicherung besteht darin, dass NUL-Zeichen im String zulässig sind. Strings sind praktisch in ihrer Länge unbegrenzt (bis zu 231 Zeichen). Diese Methode der String-Speicherung wird in Abbildung 3 dargestellt. Wird ein LabVIEW String Handle von der Call Library Function an die DLL übergeben, dann kann der Anwender LabVIEW-CIN-Funktionen wie DSSetHandleSize verwenden, um die Größe des LabVIEW String Handle anzupassen. Überdies muss das Projekt um labview.lib ergänzt werden, falls Visual C++ genutzt wird, und um labview.sym.lib, falls der Symantec-Compiler aus dem Verzeichnis LabVIEW x.x/cintools/Win32 während der Erstellung der DLL verwendet wird.
Abb. 3: Das LabVIEW-String-Format
C-Strings sind wahrscheinlich die Art von Strings, mit der ein Anwender am häufigsten zu tun hat. Die Ähnlichkeiten zwischen dem C-Sring und normalen numerischen Arrays in C werden viel deutlicher, wenn man feststellt, dass C-Strings mit char * bezeichnet werden, wobei char gewöhnlich ein vorzeichenloses Byte ist. C-Strings umfassen keine Informationen, die direkt die Länge des Strings angeben. Das ist hingegen bei LabVIEW-Strings der Fall. Stattdessen nutzen C-Strings ein spezielles Zeichen namens NULL-Zeichen (\0), welches das Ende des Strings markiert. NULL ist im ASCII-Zeichensatz mit dem Wert Null belegt. Zu beachten ist hierbei, dass es sich um die Zahl Null und nicht um das Zeichen „0“ handelt. Somit erfordert in C ein String mit n Zeichen n + 1 Bytes, um im Speicher abgelegt zu werden: n Bytes für die Zeichen im String und ein zusätzliches Byte für das NULL-Endzeichen. Der Vorteil von C-Strings besteht darin, dass sie in ihrer Größe nur durch den verfügbaren Speicherplatz beschränkt werden. Werden allerdings Daten von einem Messgerät erfasst, das numerische Daten als binären String ausgibt (wie bei seriellen oder GPIB-Geräten üblich), sind Nullwerte im String möglich. Für binäre Daten mit NULL sollte besser ein Array aus vorzeichenlosen 8-bit-Integern verwendet werden. Wird der String als C-String behandelt, geht das Programm fälschlicherweise davon aus, dass das Ende der Zeichenkette erreicht wurde. Dabei gibt das Messgerät in Wirklichkeit einen numerischen Wert von Null zurück. Die folgende Abbildung zeigt, wie ein String im C-Stil im Speicher abgelegt wird.
Abb. 5: Das C-String-Format
Werden String-Daten an eine DLL übergeben, sind dieselben Richtlinien wie für Arrays zu befolgen. Besonders wichtig ist es, dass die Größe eines Strings nie verändert und ein String nie verbunden werden darf. Noch dürfen Operationen ausgeführt werden, die eventuell die Länge der String-Daten erhöhen, die von LabVIEW übergeben werden, falls die C-String-Zeiger verwendet werden. Müssen Daten als String zurückgegeben werden, sollte zuerst ein String der entsprechenden Länge in LabVIEW erzeugt und anschließend an die DLL übergeben werden, um als Puffer zu fungieren. Wird aber ein LabVIEW String Handle von der Call Library Function an die DLL übergeben, dann kann man die LabVIEW-CIN-Funktionen wie etwa DSSetHandleSize einsetzen, um die Größe des LabVIEW String Handle zu verändern
Hinweis: Um die LabVIEW-CIN-Funktionsaufrufe nutzen zu können, muss der Anwender sein Projekt um labview.lib ergänzen, wenn er den Visual-C++-Compiler, oder um labview.sym.lib, wenn er den Symantec-Compiler verwendet.
Tipps zu Arrays und Strings
Muss eine DLL-Funktion ein Array erstellen, seine Größe ändern oder einen Daten-String in Bezug auf die Größe anpassen, ohne LabVIEW Handles zu verwenden, sollte die Funktion in zwei Schritte unterteilt werden. Im ersten Schritt wird die Anzahl der Elemente bestimmt, die im Array benötigt werden, bzw. die Länge des zurückzugebenden Strings. Diese Funktion soll die gewünschte Größe an LabVIEW übergeben. Der Anwender initialisiert das Array oder den String in LabVIEW mit Standardwerten. Danach wird dieses Array an eine zweite Funktion in der DLL übergeben, welche die Daten in das Array schreibt. Nutzt der Anwender eine auf Strings basierende Messgerätesteuerung, kann es einfacher sein, ein Array aus 8-bit-Integern anstelle von C-Strings zu übergeben, da die Möglichkeit besteht, dass NULL-Werte im String vorhanden sind. Alternativ können bei Verwendung des Visual-C++- oder des Symantec-Compilers und bei Übergabe eines LabVIEW Array Handle oder LabVIEW String Handle vom Knoten „Call Library“ an die DLL die LabVIEW-CIN-Funktionen genutzt werden, um die Größe eines Arrays oder eines Strings anzupassen oder diese zu ermitteln.
Fehlerbehebung bei der Call Library Function und der DLL
Erscheint nach Konfigurierung des Dialogfelds der Call Library Function immer noch ein unterbrochener Ausführungspfeil in LabVIEW, sollte der Pfad zur DLL-Datei überprüft werden. Zeigt LabVIEW die Fehlermeldung „Function not found in library“, sollte die Schreibweise des Namens der Funktion, die aufgerufen werden soll, geprüft werden. Dabei ist zu beachten, dass bei Funktionsnamen zwischen Groß- und Kleinschreibung unterschieden wird. Zudem ist zu beachten, dass die Funktion mit dem Kennwort _declspec (dllexport) in der Header-Datei und im Quellcode deklariert oder im Abschnitt für den Export der Moduldefinitionsdatei definiert werden muss. Selbst wenn das Kennwort _declspec (dllexport) verwendet wurde und die Aufrufkonvention _stdcall eingesetzt wird, muss der Anwender den DLL-Funktionsnamen in Abschnitt EXPORTS der Moduldefinitionsdatei deklarieren. Geschieht das nicht, wird die Funktion mit einem verstümmelten Namen exportiert und der eigentliche Funktionsname steht Anwendungen, welche die DLL aufrufen, nicht zur Verfügung. Wurde die Funktion nicht korrekt exportiert, muss die DLL neu kompiliert werden. Vor der Neukompilierung muss sichergestellt werden, dass alle Anwendungen und VIs geschlossen sind, die die DLL nutzen. Ist die DLL immer noch im Speicher, schlägt die Neukompilierung fehl. Die meisten Compiler warnen den Nutzer, wenn die DLL noch von einer Anwendung verwendet wird.
Sollten die bisher genannten Möglichkeiten nicht der Grund sein, sollte noch geprüft werden, ob ein C- oder ein C++-Compiler zum Erstellen der DLL verwendet wurde. Ist ein C++-Compiler verwendet worden, dann wurden die Namen der Funktionen in der DLL durch einen Prozess namens „Name mangling“ verändert. Am einfachsten wird dies dadurch behoben, dass die Deklarationen der Funktionen, die in die Header-Datei exportiert werden sollen, mit dem externen „C“-Befehl eingeschlossen werden:
extern “C”
{
/* your function prototypes here */
}
Nach der korrekten Konfigurierung der Call Library Function, kann das VI ausgeführt werden. Wird es nicht erfolgreich ausgeführt, können Fehler oder eine allgemeine Schutzverletzung auftreten. Für eine allgemeine Schutzverletzung gibt es mehrere mögliche Ursachen. Zuerst sollte sichergestellt werden, dass genau die Parameter übergeben werden, welche die Funktion in der DLL erwartet. Wurde beispielsweise ein int16 und kein int32 übergeben, wenn die Funktion einen int16 erwartet? Der Anwender sollte auch prüfen, ob die richtige Aufrufkonvention _stdcall oder C verwendet wird.
Zudem kann die DLL mithilfe des Debuggers auf Fehler untersucht werden, der zusammen mit dem Compiler bereitgestellt wird. Bei Microsoft Visual C++ 2005 kann im Abschnitt Build-> Settings->Debug die labview.exe als Executable for Debug session zur Fehlerbehebung der DLL eingerichtet werden. Das Arbeitsverzeichnis und das Programmargument sollen auf das VI verweisen, welches die DLL aufruft. Mithilfe des Debuggers des Compilers kann der Anwender Haltpunkte setzen, Programmcode schrittweise prüfen, die Werte der Variablen beobachten und vieles mehr. Die Fehlerbehandlung mittels konventioneller Werkzeuge kann äußerst vorteilhaft sein. Weitere Informationen zur Fehlerbehandlung finden Sie in den entsprechenden Handbüchern und Hilfeverzeichnissen des jeweiligen Compilers.
Das Aufrufen der DLL aus einem anderen C-Programm ist auch eine sehr gute Möglichkeit, die DLL auf Fehler zu untersuchen. Dadurch hat der Anwender die Gelegenheit, die DLL unabhängig von LabVIEW zu testen und so mögliche Fehlerursachen schneller festzustellen.
Wichtige Hinweise
Folgende Tipps sollten beim Schreiben einer DLL beachtet werden:
- Die richtige Aufrufkonvention (C oder stdcall) muss verwendet werden.
- Die richtige Reihenfolge der an die Funktion übergebenen Argumente muss bekannt sein.
- Die Größe von Arrays darf NIEMALS mithilfe der Argumente geändert werden, die direkt an eine Funktion übergeben werden. Das gleiche gilt für verknüpfte Strings. Die Parameter, die übergeben werden, sind LabVIEW-Daten. Das Ändern von Array- oder String-Größen kann einen Absturz zur Folge haben, falls andere im LabVIEW-Speicher abgelegte Daten überschrieben werden. Die Größe von Arrays kann EVENTUELL geändert werden, wenn ein LabVIEW Array Handle oder LabVIEW String Handle übergeben und der Visual-C++- oder der Symantec-Compiler verwendet wird, um die DLL zu kompilieren. Dasselbe gilt für das Verknüpfen von Strings.
- Bei der Übergabe von Strings an eine Funktion ist die Auswahl des richtigen zu übergebenden String-Typs zu beachten: C oder LabVIEW String Handle.
- Zu beachten ist, dass am Ende von C-Strings eine Null steht. Gibt eine DLL-Funktion numerische Daten in einem binären String-Format aus (beispielsweise über GPIB oder den seriellen Anschluss), kann sie eventuell Nullwerte als Teil des Daten-Strings ausgeben. In solchen Fällen ist die Übergabe von Arrays mit kurzen (8 bit) Integern am zuverlässigsten.
- Beim Arbeiten mit Arrays oder Strings von Daten sollte IMMER ein Puffer oder ein Array übergeben werden, der bzw. das groß genug für alle Ergebnisse ist, die von den Funktionen in den Puffer platziert werden. Das gilt nicht für deren Übergabe als LabVIEW Handles. In diesem Fall kann man ihre Größe mithilfe von CIN-Funktionen unter Visual-C++- oder Symantec-Compiler ändern.
- Es muss beachtet werden, dass die DLL-Funktionen im Abschnitt EXPORTS der Moduldefinitionsdatei aufgeführt werden, falls _stdcall verwendet wird.
- Auch muss beachtet werden, dass DLL-Funktionen, die andere Anwendungen aufrufen, im Abschnitt EXPORTS in der Moduldefinitionsdatei aufgeführt werden. Alternativ kann auch das Kennwort _declspec (dllexport) in die Funktionsdeklaration eingeschlossen werden.
- Wird ein C++-Compiler verwendet, müssen Funktionen mit dem externen Befehl „C“{} in der Header-Datei exportiert werden, um ein Verstümmeln des Namens zu verhindern.
- Schreibt ein Anwender seine eigene DLL, sollte eine DLL nicht erneut kompiliert werden, während sie von einer anderen Anwendung (z. B. einem VI) in den Speicher geladen wurde. Vor Neukompilierung einer DLL ist sicherzustellen, dass alle Anwendungen, die sie nutzen, aus dem Speicher entfernt werden. So wird gewährleistet, dass die DLL selbst sich nicht im Speicher befindet. Eine Neuerstellung kann möglicherweise fehlschlagen, falls das vergessen wird und der Compiler keine Warnung anzeigt.
- DLLs sollten mit einem anderen Programm getestet werden, um sicherzustellen, dass sich die Funktion (und die DLL) richtig verhält. Ein Test mit dem Debugger des Compilers oder mit einem einfachen C-Programm, in dem der Anwender eine Funktion in einer DLL aufrufen kann, hilft festzustellen, ob mögliche Schwierigkeiten von der DLL herrühren oder im Zusammenhang mit LabVIEW stehen.
Weitere Tutorien zum Thema:
Building DLLs in LabVIEW 6.x or later
Managing Large Data Sets in LabVIEW
An Overview of Accessing DLLs or Shared Libraries from LabVIEW
Calling a C-built DLL in LabVIEW to Generate and Sort an Array of Numbers
Building a DLL with Visual C++
Legal
This tutorial (this "tutorial") was developed by National Instruments ("NI"). Although technical support of this tutorial may be made available by National Instruments, the content in this tutorial may not be completely tested and verified, and NI does not guarantee its quality in any way or that NI will continue to support this content with each new revision of related products and drivers. THIS TUTORIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND AND SUBJECT TO CERTAIN RESTRICTIONS AS MORE SPECIFICALLY SET FORTH IN NI.COM'S TERMS OF USE (http://ni.com/legal/termsofuse/unitedstates/us/).

