Exitcode einer Dialogbox nutzen


Es kann öfters mal vorkommen, dass man mit den DialogBox-Funktionen einen neuen Dialog erstellt. In diesem Dialog nimmt der Benutzer Eingaben vor, die dann im Hauptprogramm benötigt werden. Nur wie bekommt man nun die Daten aus dem neuen Dialog in das Hauptprogramm? (Wir gehen mal davon aus, dass die Fensterprozedur des neuen Dialoges in einer separaten Unit liegt, um das ganze etwas übersichtlicher zu gestallten.) Eine Möglichkeit wäre es, eine Unit zu benutzen, die man in der Unit für den Dialog und im Hauptprogramm einbindet und dort eine globale Variable zu deklarieren. Über das Für- und Wider bezüglich globaler Variablen, will ich an dieser Stelle nicht diskutieren, aber dass sie eine potentielle Fehlerquelle sind, dürfte wohl unumstritten sein und somit sollte man sie möglichst vermeiden. Aber was tun sprach Zeus? Gestossen auf das Problem bin ich beim Refactoring des Usermanagers und wie ich an die Eingaben kommen, die der Benutzer im Dialog zum Auswählen eines Remoterechners vornimmt. Meine Lösung sah dann wie folgt aus:

Die DialogBox-Funktionen haben einen Rückgabewert. Dieser Rückgabewert ist vom Datentyp Integer. Gesetzt wird er beim Aufruf von EndDialog, um das Dialogfenster zu schließen. Nun ja, was fällt uns denn zum Datentyp Integer ein? Was ist denn noch eigentlich nur ein Integer? Und was könnte man damit anstellen? Ein Zeiger zum Beispiel ist im Endeffekt auch nur ein 32-Bit Integer. Warum also nicht einen Zeiger auf einen Speicherbereich als ExitCode zurückgeben? Gucken wir doch mal, ob es funktioniert:

Wir definieren uns in der Unit für den zweiten Dialog eine entsprechende Datenstruktur:

type
  TSelCmpData = packed record
    Computer: String[255];
    User: String[255];
    Password: String[255];
  end;
  PSelCmpData = ^TSelCmpData;

Dadurch, dass wir die Unit im Hauptprogramm einbinden, ist sie auch dort bekannt. Nun deklarieren wir eine lokale Variable in der Fensterprozedur Unit für den zweiten Dialog:

var
  SelCmpData        : PSelCmpData;

Und füllen sie entsprechend:

SelCmpData := GetMemory(sizeof(TSelCmpData));
SelCmpData.Computer := Computer;
SelCmpData.User := User;
SelCmpData.Password := PW;

Beim Aufruf von EndDialog wird dieser Zeiger nun auf einen Integer gecastet und als Exitcode verwendet:

EndDialog(hDlg, Integer(SelCmpData));

Unser Exitcode enthält also nun den Zeiger auf eine Adresse im Speicher. Im Hauptprogramm, wo wir den Dialog aufrufen, casten wir den Exitcode einfach wieder zurück und erhalten so unsere Daten:

ret := DialogBoxParam(HInstance, MAKEINTRESOURCE(200), hDlg, @ChooseCompDlgFunc, hDlg);
if ret <> 0 then
begin
  CurComputer := PSelCmpData(ret)^.Computer;
  User := PSelCmpData(ret)^.User;
  PW := PSelCmpData(ret)^.Password;
  FreeMemory(PSelCmpData(ret));

OK, im Grunde genommen ist es ein Missbrauch des ExitCodes, aber warum nicht? Warum soll man die Mittel die man hat, nicht kreativ einsetzen, um zum Ziel zu kommen? Und eleganter als eine globale, unitübergreifende Variable ist es alle male. Was ich aber damit auch zeigen will: Auch als Programmierer muss man eben kreativ sein und man muss wissen, wie man die gegebenen Mittel effektiv einsetzen kann, um sein Ziel zu erreichen.

Nachtrag

Nico hat in der Delphipraxis noch angemerkt, dass man auch die API-Funktionen GetProp und SetProp nutzen kann, um die Daten an den Dialog zu binden. Sein Beispiel dazu findet sich hier in der Delphipraxis.

Noch eine Anmerkung zu meinem oben erwähnten Artikel: Muss man nicht auf die WM_CLOSE Nachricht reagieren, kann man den Dialog auch mit EndDialog beenden, womit man dann die globale Variable für die daten lokal in der Dialogfunktion deklarieren kann. Somit hat man dann gar keine globalen Variablen mehr im Quellcode.

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