"Hello, world" auf die umständliche Art


Ich habe ja schon viele Versionen von dem "Hello, world"-Programm gesehen und auch schon in so mancher Programmiersprache geschrieben. Aber so, wie ich es jetzt letzte Woche gemacht habe, habe ich es noch nie gesehen.

Zur Vorgeschichte: Folgende Situation, man hat einen Webservice, der auf dem lokalen Rechner läuft und auch lokal angesprochen werden soll. Der übliche Weg ist nun der, dass auch lokal die Kommunikation über den localhost und http läuft. Was natürlich lokal etwas doof ist. Also hatte mein Chef folgende Idee. Man kann mit der Bibliothek xfire [1] die Schnittstelle zum Webservice lokal verfügbar machen ohne über localhost und http gehen zu müssen. So weit so gut. Dann kam seine nächste Idee, schön wäre es doch, wenn man eine universelle Brücke hätte, die man zwischen den Webservice und einer Endanwendung hängen könnte, sprich eine DLL, die die Schnittstelle zwischen dem Webservice und einer Win32-Anwendung bildet. Und ganz zum Schluss kam seine dritte und schlechteste Idee ... ich solle das machen. ;)

Gut, kurz zusammengefasst heißt das: Mit einer Win32-Anwendung (Exe oder DLL) auf die Methode einer Java-Klasse zugreifen. Klingt dramatisch, ist es aber gar nicht. Mittels Java Native Interfaces [2] (JNI), kann man zum einem aus Java raus auf native Funktion in anderen Programmiersprachen zugreifen. Das macht auch Sinn, denn da Java weitgehend platformunabhängig ist, stehen nicht alle Betriebssystemfunktionen zur Verfügung, die man eventuell braucht. Man kann diese dann in einer Programmiersprache, die das kann, zum Beispiel C/C++ oder Delphi, in einer DLL implementieren und von Java aus, kann man dann diese Funktionen aus der DLL aufrufen und nutzen. Und es geht auch umgekehrt, man kann auch mittels JNI auf eine Java Methode in einer Klasse aus einer anderen Programmiersprache (C/C++ oder Delphi) zugreifen.

Um das ganze zu testen habe ich mir also ein kleines Java-Programm geschrieben:

/*
 *  Project    : PVS Soap Bridge Demo Java Programm
 *  Copyright  : © 2006 Datawerk 
 *  File Name  : HelloWorld.java
 *  Author     : mp
 *  Date       : 2006-09-06
 *  Comment    : Java Demo-Programm mit Demo-Klassen für externe JNI-Aufrufe
 *
 */

public class HelloWorld {
    public static void main(String[] argv) throws Exception
    {
    	System.out.print("Hello, world");
    }
    
    // einfache Interger Rückgabe
    public int intTest()
    {
    	return 42;
    }
    
    // Integer Rückgabe mit Integer Parameterübergabe
    public int intTest2(int Value)
    {
    	return Value * 2;
    }
    
    // einfache String Rückgabe
    public String strTest()
    {
    	return "Hello, world";
    }
    
    // String Rückgabe mit String Parameterübergabe
    public String strTest2(String s)
    {
    	return s + "world";
    }
}

Wie man sieht beinhaltet dieses Java-Programm mehrere Methoden, um ein paar Fälle auszuprobieren. Dann habe ich erst eine Anwendung in Delphi geschrieben, die diese Methoden mittels JNI aufruft und dann zum Schluss eine entsprechende DLL in Delphi:

{*
 *  Project    : PVS [SOAP Bridge]
 *  Copyright  : © 2006 Datawerk
 *  Unit Name  : Not available
 *  Author     : mp
 *  Date       : 2006-09-07
 *  Comment    : DLL-Demo - Zugriff auf Java Methoden via Java Native Interface (JNI)
 *
 *}

library SoapBridge;

{$WARN SYMBOL_PLATFORM OFF}

uses
  Windows,
  UJvm in 'units\UJvm.pas',
  JNI in 'units\JNI.pas';

var
  Jvm: TJvm;

{*
 *  Procedure : Init
 *              Java Virtual Machine initialisieren
 *  Author    : mp
 *  Date      : 2006-09-07
 *
 *  Laden der JVM scheint beim Laden der DLL noch nicht zu funkltionieren, wenn
 *  die DLL dynamisch geladen wird. Deswegen die sepaaten Routinen zum Starten
 *  und Beenden der JVM, die manuell vom Programm aufgerufen werden müssen!
 *}
function Init: Boolean; stdcall
begin
  if not Assigned(Jvm) then
    Jvm := TJvm.create('..\..\JavaDemo');
  result := Assigned(Jvm);
end;

procedure DeInit; stdcall
begin
  Jvm.Free;
end;

{*
 *  Procedure : ReturnString
 *  Author    : mp
 *  Date      : 2006-09-07
 *
 *  Gibt einen String zurücvk und nimmt einen String als Parameter
 *}
function ReturnString(InStr: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall
var
  cls               : JClass;
  mid               : JMethodID;
  res               : JString;
  instance          : JObject;
  s                 : String;
begin
  cls := jvm.JniEnv.FindClass('HelloWorld');
  Assert(Assigned(cls), 'Class HelloWorld not found');

  mid := jvm.JniEnv.GetMethodID(cls, '<init>', '()V');
  Assert(Assigned(mid), 'Constructor not found');

  instance := jvm.JniEnv.NewObject(cls, mid, []);
  // Signatur: String Parameter, Rückgabetyp String
  mid := jvm.JniEnv.GetMethodID(cls, 'strTest2', '(Ljava/lang/String;)Ljava/lang/String;');
  Assert(Assigned(mid), 'Method "strTest2" not found');

  res := jvm.JniEnv.CallObjectMethod(instance, mid, [String(InStr)]);

  s := jvm.JniEnv.JStringToString(res);

  if length(s) < lenBuffer then
  begin
    result := Length(s);
  end
  else
  begin
    lstrcpy(Buffer, PChar(s));
    result := Length(s);
  end;
end;

exports
  Init index 1,
  DeInit index 2,
  ReturnString index 4;

begin

end.

Und ganz zum Schluss eine Delphi-Anwendung, um die DLL zu testen:

procedure TForm1.btnRetStrClick(Sender: TObject);
type
  TReturnStr = function(InStr: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;
var
  ReturnStr         : TReturnStr;
  Buffer            : PChar;
  len               : Integer;
begin
  Buffer := nil;
  @ReturnStr := GetProcAddress(hLib, 'ReturnString');
  if Assigned(ReturnStr) then
  begin
    len := ReturnStr('Hello ', nil, 0);
    try
      GetMem(Buffer, len + 1);
      ReturnStr('Hello ', Buffer, len);
      StB.SimpleText := string(Buffer);
    finally
      FreeMem(Buffer, len + 1);
    end;
  end
  else
    StB.SimpleText := SysErrorMessage(GetLastError);
end;

Hier nur die Routine, die die Methode strTest2 aus dem Java-Programm aufruft. Und das Verrückte an der ganzen Sache ist ... es funktioniert auch noch.

Ein paar Anmerkungen warum gerne die Ausgabe des Textes "Hello, world" genommen wird aus der Wikipedia:

Einfache Beispiele von Computerprogrammen, die zum Beispiel zur Demonstration verwendet werden, bestehen häufig nur aus ein paar Zeilen Programmcode, die den Text Hallo, Welt! oder auf Englisch Hello, world! ausgeben. Dieses Programm soll als eines der einfachst möglichen zeigen, was für ein vollständiges Programm (in der betreffenden Programmiersprache) benötigt wird, und einen ersten Einblick in die Syntax geben. Ein solches Programm ist auch geeignet, die erfolgreiche Installation eines Compilers für die entsprechende Programmiersprache zu überprüfen.

Die Verwendung des Textes "Hello, world!", der natürlich auch durch einen beliebigen Text ersetzt werden kann, aber dennoch gerne unverändert benutzt wird, ist eine Tradition und geht auf ein internes Programmierhandbuch der Bell Laboratories über die Programmiersprache C zurück, das Brian Kernighan dort 1974 verfasste, nachdem er dort schon ein Jahr zuvor die Worte "hello" und "world" in einer Einführung in die Programmiersprache B verwendet hatte. Bekanntheit erlangte der Text jedoch erst durch die Veröffentlichung in dem Buch The C Programming Language ( deutsch: Programmieren in C) von Brian Kernighan und Dennis Ritchie, auch wenn in dem dortigen Beispiel die Schreibung "hello world" verwendet wurde.

Downloads


SoapBridge_HelloWorld.zip Wednesday, 29-Dec-2010 23:46:00 CET 584K

Links

[1] http://xfire.codehaus.org/Local+Transport
[2] http://www.haertfelder.com/jni.html
[3] http://www.michael-puff.de/Artikel/2006/files/SoapBridge_HelloWorld.zip


2010-12-29T23:44:48 +0100, mail+homepage[at]michael-puff.de