Beispiel zum Einbinden einer DLL
Beispiel zum Einbinden einer DLL Aufrufen des Quicksort-Algorithmus aus einer DLL
- Siehe: DLL-Befehle , DLL in CONZEPT 16 einbinden
In diesem Beispiel wird die Programmierung einer DLL und deren Aufruf in CONZEPT 16 vorgestellt. Um das Beispiel möglichst einfach zu halten, wird ein statisch vordefinierter Array sortiert und zur Ausgabe der externe Debugger verwendet.
Der Quellcode ohne Kommentare befindet sich hier .
Die Implementation besteht aus zwei Teilen: zum einen aus dem Prozedurtext in CONZEPT 16, zum anderen aus dem Quelltext in C. Der Quelltext in C muss in Form einer DLL vorliegen, bevor er in CONZEPT 16 eingebunden werden kann. Bei der Übersetzung der DLL ist darauf zu achten, dass die Funktion C16_DLLCALL von außerhalb der DLL aufgerufen werden kann. Entsprechende Parameter werden in der Regel beim Linken angegeben.
In den Sourcecode werden die Header-Dateien windows.h und c16_dll.h eingebunden. In der c16_dll.h befinden sich alle notwendigen Definitionen.
#include <windows.h>
#include <c16_dll.h>
Die Funktion QuickSort übernimmt die Sortierung eines Arrays. Ihr wird der vollständige Array übergeben, zusammen mit der Anzahl der zu sortierenden Einträge und dem Bereich der zu sortieren ist.
static void QuickSort
(
char** aTab, // character table
vINTs aCount, // number of strings
vINTs aLoPos, // lowest entry
vINTs aHiPos // highest entry
)
Zu dem zu sortierenden Bereich wird das mittlere Element ermittelt.
{
vINTs LVar; // left variable
vINTs RVar; // right variable
char* MidElm; // mid element
char* ExcElm;
LVar = aLoPos;
RVar = aHiPos;
// get mid element
MidElm = aTab[(LVar+RVar) >> 1];
Die Elemente links und rechts von dem mittleren Element werden solange vertauscht, bis auf der linken Seite alle Elemente, die kleiner und auf der rechten Seite alle Elemente, die größer als das mittlere Element sind.
do
{
// set left border
while (LVar < aCount && strcmp(aTab[LVar],MidElm) < 0)
++LVar;
// set right border
while (RVar >= 0 && strcmp(MidElm,aTab[RVar]) < 0)
--RVar;
// exchange left and right element
if (LVar <= RVar)
{
ExcElm = aTab[LVar];
aTab[LVar] = aTab[RVar];
aTab[RVar] = ExcElm;
++LVar;
--RVar;
}
}
while (LVar <= RVar);
Für den vorderen und den hinteren Abschnitt wird noch einmal die Funktion QuickSort aufgerufen.
// sort left
if (aLoPos < RVar)
QuickSort(aTab,aCount,aLoPos,RVar);
// sort right
if (LVar < aHiPos)
QuickSort(aTab,aCount,LVar,aHiPos);
}
Die Rekursion wird solange durchgeführt, bis zum Schluss einelementige Abschnitte sortiert sind. Zu diesem Zeitpunkt liegt eine sortierte Tabelle vor.
Die Funktion QuickSort wird von der Einstiegsfunktion der DLL aufgerufen. Der übergebene Call Control Block enthält die Instanz der DLL und alle notwendigen Funktionsadressen.
vERROR WINAPI C16_DLLCALL
(
vC16_CCB* aCCB /* call control block */
)
{
vLONG tCmd; /* command */
vLONG tArgCount; /* number of arguments */
/* argument 1 */
vINT tType1;
/* argument 2 */
vINT tType2;
vINT tOpt2;
vINT tLen2;
vINT tCount2;
/* table */
char* tBuf;
char** tTab;
vINT tSize;
vINT tLoop;
Über die im Call Controll Block übergebene Funktionsadresse können unterschiedliche Funktionen aufgerufen werden. Zunächst wird die Anzahl der übergebenen Argumente mit C16_ArgCount() ermittelt. Die Funktion wird mit zwei Argumenten aufgerufen: einem Funktionscode und der zu sortierenden Tabelle. In diesem Beispiel wird der Funktionscode nicht benötigt, da nur eine Funktion von der DLL ausgeführt wird. Es zeigt aber, auf welche Weise mehrere Funktionen implementiert werden können. Die Parameter werden beim Prozedur-Befehl DllCall () angegeben.
Wurde mindestens ein Parameter übergeben, werden zu diesem mit der Funktion C16_ArgInfo() weitere Informationen ermittelt.
aCCB->C16_ArgInfo(aCCB->InstHdl,1,&tType1,NULL,NULL,NULL,NULL);
Handelt es sich beim ersten Übergabeparameter um einen ganzzahligen Wert, kann die Verarbeitung fortgesetzt werden. Im ersten Parameter steht der Funktionscode. Dieser wird mit der Funktion C16_ArgRead() gelesen und mit dem anschließenden switch-Konstrukt ausgewertet.
if (tType1 == _TypeInt)
{
/* read command value */
aCCB->C16_ArgRead(aCCB->InstHdl,1,0,&tCmd);
switch (tCmd)
{
// quicksort of alpha array
case 1:
Soll die Funktion zum Sortieren eines Arrays aufgerufen werden, muss ein weiterer Parameter angegeben werden. Diesem Parameter muss ein Array, bestehend aus Zeichenketten und mit Call-By-Reference, übergeben worden sein.
Für das übergebene Array wird ein Puffer angelegt, bevor der Parameter eingelesen wird. Um die einzelnen Einträge des Arrays zu erreichen, werden die einzelnen Adressen des Arrays in einer Tabelle gespeichert.
/* read array */
tBuf = malloc(tLen2);
aCCB->C16_ArgRead(aCCB->InstHdl,2,0,tBuf);
tTab = malloc(sizeof(char*) * tCount2);
tSize = tLen2 / tCount2;
*tTab = tBuf;
/* define sort table */
for (tLoop = 1; tLoop < tCount2; ++tLoop)
tTab[tLoop] = tTab[tLoop-1] + tSize;
Jetzt kann die Funktion QuickSort für die ganze Tabelle aufgerufen werden. Ist die Tabelle sortiert, wird das Ergebnis (die sortierte Tabelle) mit der Funktion C16_ArgWrite() zurück geschrieben. Da die Tabelle mit der Methode Call-By-Referenz übergeben wurde, steht sie in der CONZEPT 16-Prozedur dann zur Verfügung.
QuickSort(tTab,tCount2,0,tCount2-1);
/* store back */
for (tLoop = 0; tLoop < tCount2; ++tLoop)
aCCB->C16_ArgWrite(aCCB->InstHdl,2,tLoop+1,tTab[tLoop]);
free(tTab);
free(tBuf);
}
}
break;
}
}
}
return(0);
}
Jede DLL benötigt eine Funktion, die immer beim Laden und Entladen der Bibliothek aufgerufen wird. Diese Funktion wird vom Betriebssystem gestartet und dient dazu, Funktionsaufrufe der Bibliothek vorzubereiten und Ressourcen zu belegen oder freizugeben.
vINT APIENTRY LibMain
(
HINSTANCE hInstance,
DWORD fdwReason,
PVOID pvReserved
)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return(TRUE); // signal success to Windows
}
Die Bibliothek muss so übersetzt werden, dass die Funktion C16_DLLCALL von außerhalb der DLL aufgerufen werden kann. Dies sind in der Regel Einstellungen, die beim Linken der DLL angegeben werden müssen.
Nachdem die Bibliothek geschrieben und übersetzt ist, kann sie von CONZEPT 16 aus aufgerufen werden. Zunächst werden das Funktionskommando und die maximale Größe des Array in einem Define festgelegt.
@A+
@C+
define
{
sCmdSort : 1 // function command
sTabCount : 16 // size of string table
}
In der Funktion main erfolgt das Füllen des Arrays mit Werten, die Ausgabe des unsortierten Arrays, der Aufruf der DLL und die Ausgabe des sortierten Arrays.
main
local
{
tHdl : handle; // DLL handle
tLoop : int; // loop counter
tStrTab : alpha(40)[]; // string table
}
Die Ausgaben der Funktion erfolgen über den Debugger. Dazu wird zunächst eine Verbindung mit dem Debugger hergestellt. Mit dem Befehl VarAllocate () wird der dynamische Array angelegt und anschließend mit Werten gefüllt.
{
DbgConnect('*', false, false);
VarAllocate(tStrTab, sTabCount);
// define table
tStrTab[ 1] # 'Meier, Karl';
tStrTab[ 2] # 'Neumann, Andrea';
tStrTab[ 3] # 'Bauer, Manfred';
tStrTab[ 4] # 'Gronwald, Sven';
tStrTab[ 5] # 'Unger, Andreas';
tStrTab[ 6] # 'Schmidt, Josef';
tStrTab[ 7] # 'Kramer, Ute';
tStrTab[ 8] # 'Velten, Markus';
tStrTab[ 9] # 'Hannemann, Georg';
tStrTab[10] # 'Dietz, Katharina';
tStrTab[11] # 'Schmidt, Alexander';
tStrTab[12] # 'Lichtenberg, Doris';
tStrTab[13] # 'Retzbach, Tanja';
tStrTab[14] # 'Fischer, Gudrun';
tStrTab[15] # 'Jankowski, Wilfried';
tStrTab[16] # 'Ackermann, Bernd';
Die Ausgabe des unsortierten Arrays erfolgt in einer Schleife über alle Elemente mit dem Debugger. Der externe Debugger muss zuvor gestartet worden sein.
// show table
DbgTrace('Unsortiert:');
for tLoop # 1;
loop Inc(tLoop);
while (tLoop <= sTabCount)
{
SysSleep(10);
DbgTrace(tStrTab[tLoop]);
}
Mit dem Befehl DllLoad () wird die DLL geladen. Zu diesem Zeitpunkt wird die Funktion LibMain durchgeführt. Da die Einstiegsfunktion den Namen C16_DLLCALL hat, muss der Name der Einstiegsfunktion nicht als Parameter an DllLoad () übergeben werden.
Der Befehl DllCall () ruft die Einstiegsfunktion mit den angegebenen Parametern auf. Als erster Parameter wird ein Wert übergeben, der die durchzuführende Funktion bestimmt. Der zweite Parameter enthält das zu sortierende Array. Das Array wird mit der Methode Call-By-Reference (var) übergeben, damit der sortierte Array zurückgegeben werden kann.
Mit DllUnload () wird die DLL wieder aus dem Speicher entfernt. Es wird wieder die Funktion LibMain aufgerufen.
// sort table
tHdl # DllLoad('DLL\C16TEST');
tHdl->DllCall(sCmdSort, var tStrTab);
tHdl->DllUnload();
Das sortierte Array wird wiederum in einer Schleife über den externen Debugger ausgegeben. Anschließend wird der Speicherbereich für das Array freigegeben und die Verbindung zum Debugger geschlossen.
// show table
DbgTrace('');
DbgTrace('Sortiert:');
for tLoop # 1;
loop Inc(tLoop);
while (tLoop <= sTabCount)
{
SysSleep(10);
DbgTrace(tStrTab[tLoop]);
}
VarFree(tStrTab);
DbgDisconnect();
}
In dem hier aufgeführten Beispiel wurden in der DLL nur Funktionen zum Ermitteln der Parameter verwendet. Es stehen über den Call Control Block noch eine ganze Reihe weiterer Funktionen zur Verfügung. Eine vollständige Liste aller Funktionen befindet sich im Abschnitt DLL in CONZEPT 16 einbinden .