Programmierung

Come together – CONZEPT 16 und C#

Für die Interoperabilität mit anderen Entwicklungswerkzeugen (bzw. damit erzeugten Anwendungen) stellt CONZEPT 16 eine allgemeine Schnittstelle zur Verfügung: die externe Programmierschnittstelle. Diese ist, wie auch die anderen Komponenten in Form einer DLL im Setup von CONZEPT 16 enthalten und kann separat installiert werden.

Einleitung

Die CONZEPT 16-Programmierschnittstelle wird als dynamische Linkbibliothek in drei Varianten ausgeliefert:

  • Programmierschnittstelle (c16_pgxw.dll / 32-bit)
    Diese DLL bietet den Zugriff auf die Datenstruktur- und Benutzerinformationen sowie Funktionen für die Datensatz-Verarbeitung. Darüber hinaus stehen Funktionen für den Zugriff auf interne Texte der Datenbank zur Verfügung und es können Prozeduren ausgeführt werden.
  • Programmierschnittstelle (c16_pgx_w64.dll / 64-bit)
    Wie die erste Variante, jedoch für die Verwendung in 64-bit-Anwendungen.
  • Erweiterte Programmierschnittstelle (c16_pgxe.dll)
    Diese DLL bietet zusätzlich die Möglichkeit, Prozeduren auszuführen, welche Befehle der Oberfläche (z.B. WinDialog) beinhalten. Damit die Bibliothek geladen werden kann, müssen weitere DLLs vorhanden sein (wie z.B. c16_objw.dll).

Das Einbinden der Programmierschnittstelle in bestehende C- bzw. C++ Projekte ist durch die mitgelieferten Header-Dateien (c16.h, c16_pgx.h) sowie die Link-Bibliotheken sehr einfach möglich. Der vorliegende Artikel zeigt auf, wie die DLL in einer Anwendung verwendet werden kann, die mit Visual C# entwickelt wurde.

Visual C#

Die Sprache C# ist C++ zwar im Ansatz sehr ähnlich, die von CONZEPT 16 bereitgestellten C-Header-Dateien können hier jedoch nicht verwendet werden. Gleiches gilt auch für die Link-Bibliotheken. Deshalb muss zunächst eine Möglichkeit gefunden werden, wie die Funktionen, welche von der DLL bereitgestellt werden, genutzt werden können. Glücklicherweise bietet auch Visual C# die Möglichkeit der Interoperabilität. Ausgangsbasis ist der Namensraum ‘System.Runtime.InteropServices’, der u.a. auch Methoden für die Verwendung von DLLs zur Verfügung stellt, die nicht mit C# erstellt wurden.

Funktionen

Zum Einbinden von Funktionen aus externen DLLs bietet C# die DllImportAttribute-Klasse.

        [DllImport("c16_pgxw.dll")]
        public static extern Int32 C16_InitPgif(Int32 memoryLimit, ref IntPtr pgifHdl);

Die Anweisung importiert die Funktion C16_InitPgif aus der DLL “c16_pgxw.dll” und definiert, dass diese zwei Argumente (memoryLimit und pgifHdl) besitzt. Die Reihenfolge und der Typ der Argumente muss mit der durch CONZEPT 16 definierten Funktionsdeklaration (c16_pgx.h) identisch sein (siehe Codeausschnitt unten).

/* initialize interface module */
C16API C16_InitPgif
(
  const vLONG		aMemoryLimit, /* in:	upper memory limit  */
  vPHANDLE*		aPgifHdl      /* out: module handle	    */
);

Der von CONZEPT 16 definierte Typ vLONG stellt einen vorzeichenbehafteten 32-bit-Integer dar. Die Entsprechung in C# ist Int32. Das zweite Argument (aPgifHdl) liefert einen Wert vom Typ vPHANDLE zurück. Hierfür kann in C# der Datentyp IntPtr verwendet werden. Wichtig ist, dass das Argument als ‘ref’ deklariert wird, damit klar ist, dass die Funktion einen Wert zurückschreibt.

Strukturen

Bei vielen Funktionen der Programmierschnittstelle werden Zeiger auf Strukturen übergeben. Der folgende Code-Ausschnitt zeigt die Deklaration der Funktion C16_QueryPgifInfo, welche Informationen zur Programmierschnittstelle zurückliefert.

/* query interface informations */
C16API C16_QueryPgifInfo
(
  const vPHANDLE	aPgifHdl,		/* in:	module handle	    */
  vC16_PgifInfo*	aInfoBlock		/* out: information block   */
);

Das zweite Argument liefert einen Zeiger auf eine Struktur vom Typ vC16_PgifInfo. Die Struktur ist in der Header-Datei c16.h wie folgt definiert.

/* interface information structure */
typedef struct
{
  vINT			InfoSize;		/* size of information block	    */

  vINT			PgifType;		/* type of interface		    */
  vCHAR 		PgifLicense[20];	/* license number		    */
  vCHAR 		PgifRelease[8]; 	/* release info 		    */
  vINT			PgifUserLimit;		/* user limit			    */
  vINT			PgifMemory;		/* current allocated memory	    */
  vINT			PgifMemoryPeak; 	/* peak allocated memory	    */
}
vC16_PgifInfo;

Damit die Funktion aus C# aufgerufen werden kann, muss die Struktur nach C# portiert werden.

    [StructLayout(LayoutKind.Explicit)]
    public struct PgifInfo
    {
        [FieldOffset(0)]
        public UInt32 InfoSize;
        [FieldOffset(4)]
        public UInt32 PgifType;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        [FieldOffset(8)]
        public Byte[] PgifLicense;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        [FieldOffset(28)]
        public Byte[] PgifRelease;
        [FieldOffset(36)]
        public UInt32 PgifUserLimit;
        [FieldOffset(40)]
        public UInt32 PgifMemory;
        [FieldOffset(44)]
        public UInt32 PgifMemoryPeak;
    }

Durch ‘public struct PgifInfo’ wird die Struktur in C# deklariert. Hierbei spielt das Layout eine entscheidende Rolle. Dieses definiert, an welchem Byte-Offset die einzelnen Member der Struktur stehen. Durch ‘LayoutKind.Explizit’ wird angegeben, dass jedes einzelne Feldoffset explizit definiert wird. Dies geschieht durch die Angabe von ‘FieldOffset’ vor dem entsprechenden Member. InfoSize steht an erster Stelle in der Struktur und besitzt deshalb das Feldoffset 0. Da InfoSize vom Typ vINT (UInt32 in C#) und somit 4 Byte groß ist, beginnt das Member PgifType an Offset 4, usw.

Die Beispielanwendung
ApiDemo - AllgemeinAbb. 1: Allgemeine Informationen

Die Beispiel-Anwendung ‘ApiDemo’ wurde in Microsoft Visual Studio 2013 entwickelt. Das Archiv am Ende des Artikels enthält das Visual Studio-Projekt inklusive Source-Code und ausführbarer Anwendung. Nachdem das Projekt ‘ApiDemo.sln’ in Visual Studio geöffnet wurde, kann das Beispiel über die Funktionstaste F5 ausgeführt werden.

ApiDemo - DatenstrukturAbb. 2: Datenstruktur-Informationen

Über den Menüpunkt ‘Verbindung’ im Hauptmenü der Anwendung kann eine Datenbankverbindung hergestellt bzw. getrennt werden. Der Ausgabebereich unterteilt sich in zwei Seiten. Die Seite ‘Allgemein’ zeigt Informationen zur verwendeten Programmierschnittstelle, die Datenbank und den verbundenen Datenbankserver an. Die Seite ‘Datenstruktur’ zeigt alle Dateien der ausgewählten Datenbank an. Durch Doppelklick auf ein Datei-Symbol werden die Teildatensätze, Schlüssel und Verknüpfungen gelistet. Durch Betätigung der Pfeil-Schaltfläche kann zur übergeordneten Ebene gewechselt werden.

Fazit

Die Programmierschnittstelle wurde speziell für den Einsatz in heterogenen Anwendungsstrukturen entwickelt. Für bestehende Anwendungen, die nicht oder nur teilweise mit CONZEPT 16 entwickelt wurden, bietet die Schnittstelle einen universellen Weg für die Anbindung an eine CONZEPT 16-Datenbank.

Download

ApiDemo - Beispielanwendung ApiDemo.zip (851.40 KB)
Sie müssen angemeldet sein, um die Datei herunterladen zu können.

3 Kommentare

3 Kommentare “Come together – CONZEPT 16 und C#”

  1. Pro Thread ist eine entweder eine eigene Instanz (C16_InitInstance) notwendig (praktisch wie Thread Local Storage), alternativ muss eine Synchronisation derThreads stattfinden, da beispielsweise nur ein Standard-Recbuffer pro Tabelle vorhanden ist.

  2. Ist die über die beschriebenen Bibliotheken bereitgestellte C-API thread safe?

    Ich habe neulich im Rahmen eines proof of concept ein wenig mit der Programmierschnittstelle experimentiert und hatte Probleme beim Multithreading.

    Ich habe die API-Funktionen C16_InitPgif, C16_InitInstance, C16_OpenArea, C16_ProcArgument, C16_ProcCall, C16_CloseArea, C16_TermInstance, C16_TermPgif
    in einer eigenen Funktion kombiniert.

    Um meine Tests nicht unnötig kompliziert zu machen, habe ich ausschliesslich lokale Variablen verwendet (also gab es kein shared memory), da ich trotzdem auf Probleme gelaufen bin, habe ich vermutet, dass einige dieser Funktionen nicht thread safe sind und bin kurzfristig auf Prozesse ausgewichen, was für den POC auch ausreichend war.

    Jetzt würde ich einfach gerne wissen, ob die C16-API thread safe ist, denn falls nicht, brauche ich ja nicht weiter in dieser Richtung zu experimentieren, sondern kann bei der Verwendung von Prozessen bleiben.

Kommentar abgeben