.NET-Komponenten in Team Developer 3.1 einbinden

Einer der großen Vorteile des TD ist, dass man die Sal-Funktionalität durch externe Komponenten erweitern kann. Damit kann man die Vorteile der hohen Produktivität der 4GL-Sprache – geringe Lernkurve, sehr schnelle Programmierung – mit den Vorteilen des riesigen Funktionsumfangs des .NET-Frameworks kombinieren. Leider lässt sich .NET erst ab Version 6.0 des TD ohne weiteres einbinden. Für Versionen darunter gibt es jedoch die Möglichkeit, .NET-Funktionen mittels COM-Objekten im TD zu. Beispielhaft möchte ich hier die MD5-Hashfunktion aus .NET einbinden, so dass man vom TD aus den MD5-Hashwert für Strings und Blobs ermitteln kann. Ohne weiteres wäre es möglich, auch die moderneren Hash-Funktionen RIPEMD160, SHA1, SHA256, SHA384 und SHA512 zu verwenden. Siehe MSDN-Library.

 COM-Objekt in C# erstellen

Hier das Kochrezept, um die Funktion MD5.ComputeHash(..)  als COM-Objekt zur Verfügung zu stellen:

  1. In Visual Studio (z.B. mit dem kostenlosen Visual C#  2010 Express) ein neues Projekt vom Typ ClassLibrary mit dem Namen DotNet2Com erstellen.
  2. Die Assembly muss für COM registriert werden können. Dazu im Projektmappenexplorer das Kontextmenü von DotNet2Com aufrufen, dort Eigenschaften wählen. Auf dem Reiter Erstellen die Checkbox Für COM Interop registrieren anhaken.
  3. Im Projektmappenexplorer unter Properties „AssemblyInfo.cs“ aufrufen, dort den Eintrag [assembly: ComVisible(true)] ggf. von false auf true setzen.
  4.  Die Datei Class1.cs per rechtem Mausklick auswählen und via Kontextmenü in Md5.cs umbenennen (inklusive der Verweise).
  5. In der Datei Md5.cs die Using-Direktive
    using System.Runtime.InteropServices;
    eintragen.
  6. In  der Datei Md5.cs das Interface definieren – noch außerhalb der Klasse Md5 aber innerhalb des Namespaces:
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IMd5
    {
    String ComputeHash(Object buffer);
    }
    Das Interface ist notwendig, damit Visual Studio beim Erstellen der DLL diese und ggf. weitere Funktionen mit einem COM-Interface ausstattet und sie somit z.B. für den TD sichtbar sind. Da der TD offensichtlich nur Latebound-Funktionen importieren kann, muss das Attribut InterfaceIsDispatch gesetzt werden. Möglich wäre noch InterfaceIsDual (das VS anweist, beide Interfacetypen zu implementieren), nicht jedoch InterfaceIUnknown.
    Der Input-Buffer wird hier vom Typ Object gewählt und nicht etwa das näherliegende byte[]. Grund dafür ist, dass ActiveX-Explorer des TeamDeveloper den Typ byte[] als SafeArray wrappt. Der C#-Typ Object wird vom TD hingegen als Variant interpretiert. Das ist für die Übergabe von Strings (der in TD sowohl Text als auch einen BLOB aufnehmen kann) wesentlich geeigneter und schneller.
  7. Nun muss die eigentliche Klasse Md5 das Interface implementieren:
    public class Md5 : IMd5
    {
    }

Jetzt kann die eigentliche Programmierung losgehen:
Um die MD5-Funktionen nutzen zu können, müssen sie per
using System.Security.Cryptography;
eingebunden werden.

Die eigentliche Funktion sieht so aus:

public String ComputeHashAsHex(Object buffer)
{
            // Parameter auf Gültigkeit prüfen
if (buffer == null)
{
return String.Empty;
}

            // Parametervariable buffer vom Typ Object in ein byte[] Array wandeln
byte[] bIn = (byte[])buffer;
            // Länge prüfen
if (bIn.Length == 0)
{
return String.Empty;
}

            // Instantiierung von MD5 ist ungewöhnlich (an sich wäre MD5 md5Hash = new MD5() zu erwarten gewesen)
            // ist in der MSDN-Library jedoch so beschrieben:
MD5 md5Hash = MD5.Create();

byte[] output = md5Hash.ComputeHash(bIn);

// Umwandeln der 16 bytes im Output in einen HEX-String bestehend aus 32 Zeichen
StringBuilder sb = new StringBuilder(32);
foreach (byte bt in output)
{
// jedes Byte eine zweistellige Hexzahl wandeln
sb.Append(bt.ToString(„X2„));
}
return sb.ToString();
}

Das war’s, das COM-Objekt kann gebaut werden.

Einbinden des COM-Objekts in den Team Developer

Um ein COM-Objekt nutzen zu können, muss ein .apl-Wrapper erzeugt werden. Im TD muss dazu mittels des ActiveX Explorers (Menü Tools) die Library „DotNet2Com“ ausgewält werden. Dieser inspiziert das COM-Objekt und zeigt seinen Inhalt an. Es genügt, das Interface IMd5 auszuwählen und per „Generate Full by Name“ die DotNet2Com.apl zu erzeugen.

Die Datei findet sich in \Gupta\AxLibs wieder. Sie ist nun auch automatisch – zusammen mit der Automation.apl – im aktuellen TD-Projekt als Library eingebunden. Verwendet werden kann das COM-Objekt z.B. so:

Function: HelloWorldMd5
 Returns
 Parameters
 Static Variables
 Local variables
FunctionalVar: cMd5
Class: DotNet2Com_IMd5
FunctionalVar: vBuffer
Class: Variant
String: sMd5
String: sIn
 Actions
  ! COM-Objekt instanziieren – Name ist per Default: Namespace.Classname
Call cMd5.CreateObject( ‚DotNet2Com.Md5‘ )
  ! Eingabe-String setzen
Set sIn = ‚Hello World‘
  ! Besonderheit bei Text-Strings: Länge des Buffers auf Länge des Texts begrenzen – ansonsten würde die abschließende 0 in die Hash-Berechnung mit aufgenommen
Call SalStrSetBufferLength(sIn, SalStrLength(sIn) )
  ! String im Variant als Blob hinterlegen
Call vBuffer.SetBlob( sIn )
Call cMd5.ComputeHashAsHex( vBuffer, sMd5 )
Call SalMessageBox( ‚The Md5 Hash for
„Hello World“
is
‚ || sMd5, ‚Success‘, MB_Ok)

Das Ergebnis:

Das war’s fürs erste zu diesem Thema.

Zum Schluss noch die Downloads zum oben beschriebenen Code:

Visual Studio Projekt DotNet2Com.zip

TD Projekt Md5App.zip

Über Feedback würde ich mich freuen.

Happy coding!

Advertisements

Über thomasuttendorfer
Ich bin Entwicklungsleiter bei der Softwarefirma [ frevel & fey ] in München. Wir entwickeln Business-Software für Verlage und verwenden dafür den Gupta Team-Developer sowie Visual Studio.

3 Responses to .NET-Komponenten in Team Developer 3.1 einbinden

  1. Es kam eine Anfrage bezüglich der Portierung dieses Projekts auf TD6 sowie einige Fragen zur Klärung, die ich hier beantworten möchte:
    Die Portierung auf TD6 war recht einfach:
    – Mit dem ActiveX-Explorer des TD6 eine DotNet2Com.apl erstellen. Dazu hakte ich dort lediglich die CoClass „Md5“ an und stellte die Generate-Option auf „Generate Full by name“.
    – Leider lässt sich die resultierende APL nicht ohne weiteres kompilieren, da die DotNet2Com-Klasse u.a. von mscorelib abgeleitet war. Diese Ableitung hatte ich gelöscht und schon konnte man kompilieren.
    – Wenn man die DotNet2Com.dll mit Visual Studio erstellt, wird sie per Default auch registriert. Installiert man das .NET-COM-Objekt auf einem anderen Rechner, muss man sie manuell registrieren, was man für .NET-COM-Objekte mit dem RegAsm erledigen muss. Diese findet sich in den .NET-Verzeichnissen des Frameworks, bei mir z.B. C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe DotNet2Com.dll /codebase – zur Not einfach danach suchen.
    Die etwas bedrohliche Message, die man dabei sieht, kann man ignorieren. Entscheidend ist die letzte Zeile: Types wurden registriert. Ich verwende übrigens die RegAsm.exe in dem .NET-Framework, die mit der Zielplattform im VS-Projekt übereinstimmt. Ob das notwendig ist, weiß ich nicht.

  2. Albert says:

    Hab ich irgendwelche Möglichkeiten in einer Win32 TD 6.0 Anwednung von einer C# .NET dll Arrays zurückzubekommen? Am Besten als Receive Parameter, als Return Value wärs aber auch ok.

    • Hallo Albert,
      die Antwort ist nein.

      Weder mit COM noch mit der direkten Einbindung einer .NET-Assembly, was ja in TD6.0 möglich ist.
      Als Return-Wert ist ein Array in Gupta ja traditionell nicht zugelassen – das kann also auch mit COM oder .NET nicht funktionieren.
      Als Receive-Parameter ist es in .NET prinzipiell denkbar – aber leider hat Gupta hier offensichtlich etliche Implementierungsfehler gemacht, so dass es deshalb nicht möglich ist.
      Die einzige Möglichkeit, die ich sehe funktioniert sowohl mit COM als auch mit einer .NET-Assembly und geht so:
      Man hält das Array in .NET-Klasse als Instanzvariable und erstellt Zugriffsfunktionen wie z.B. Count(), GetItem(nIdx) und AddItem(someUdv) in dieser Klasse.
      Dann kann man die einzelnen Array-Elemente gezielt mit Gupta abholen.

      Viele Grüße
      Thomas

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: