AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte Delphi HexCalc: Der Gipfel der Faulheit aka. Dialog mit Klassenname

HexCalc: Der Gipfel der Faulheit aka. Dialog mit Klassenname

Ein Thema von Daniel Schuhmann · begonnen am 14. Jan 2008 · letzter Beitrag vom 8. Jan 2009
Antwort Antwort
Benutzerbild von Daniel Schuhmann
Daniel Schuhmann
Registriert seit: 16. Jul 2005
Vor etwa 2 Jahren habe ich einmal die Frage gestellt, ob es möglich sei, eine Dialogressource mit eigenem Klassennamen zu versehen. Da ich ungern einen derart angestaubten Thread aus der Versenkung holen möchte und mein jetziges Thema zudem vom ursprünglichen abweicht, habe ich mir erlaubt, ein neues Thema zu starten.

Nun ja, man entwickelt (sich) weiter, man probiert aus und vor allem: Man fängt an, Bücher zu lesen. Zum Beispiel den "Petzold", das Standardwerk für NonVCL-Entwickler.

http://www.amazon.de/exec/obidos/ASIN/3860631888/delphipraxis-21

Nun gut, ich wollte ja nicht Werbung für dieses Buch machen (es ist aber wirklich super!), sondern langsam zum eigentlichen Thema kommen. Charles Petzold stellt im Kapitel 11 (Dialogfelder) sein Programm HexCalc - einen Hexadezimalrechner - vor und beantwortet so ganz nebenbei meine Frage nach dem Dialog mit Klassennamen. Da mich neben dem Thema an sich auch der Code begeistert hat, habe ich das Programm nach Delphi übersetzt.

Zitat von Charles Petzold, Windows-Programmierung:
Das im folgenden wiedergegebene Programm HEXCALC darf man wohl ohne Übertreibung als Gipfel der Faulheit bezeichnen: Es enthält überhaupt keinen Aufruf von CreateWindow, kümmert sich keinen Deut um Nachrichten des Typs WM_PAINT, interessiert sich weder für Gerätekontexte noch für Mausereignisse und umfaßt insgesamt weniger als 150 Zeilen Quelltext. Dennoch kommt dabei ein Hexadezimalrechner mit 10 Funktionen sowie einer kompletten Tastatur- und Mausschnittstelle heraus, der sich in Abbildung 10.5 [...] bewundern lässt.
Im folgenden werde ich ein paar Besonderheiten des Programms generell sowie meiner Delphi-Umsetzung erklären.

Zunächst geht es um die Registrierung der Fensterklasse und die Erstellung des Dialogfeldes. Im Ressourceneditor wird für den Dialog der Klassenname "HexCalc" eingetragen; wird der Dialog über ein Script erstellt, sieht das ganze so aus:
Code:
100 DIALOGEX 0, 0, 205, 130
STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER | 
    WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Hexadezimalrechner"
CLASS "HexCalc"
FONT 8, "MS Shell Dlg"
Der Fensterprozedur-Zeiger lpfnWndProc wird einer herkömmlichen Window-Prozedur (WndProc) zugewiesen, die sonst übliche Dialogprozedur fehlt. Zudem ist der Wert DLGWINDOWEXTRA der Member-Variable cbWndExtra wichtig, diese reserviert einige zusätzliche Bytes für die Nutzung einer Dialog-Ressource. Im Aufruf von CreateDialog wird schließlich der String "HexCalc" (definiert in der Konstante szAppName) der Fensterklasse übergeben. Die sonst nötige Adresse der Dialogprozedur bleibt leer, da das Programm die Nachrichten selbst verarbeitet.

Delphi-Quellcode:
var
  WndClass: TWndClassEx;
  msg: TMsg;

begin
  WndClass.cbSize := SizeOf(TWndClassEx);
  WndClass.style := CS_HREDRAW or CS_VREDRAW;
  WndClass.lpfnWndProc := @WndProc;
  WndClass.cbClsExtra := 0;
  WndClass.cbWndExtra := DLGWINDOWEXTRA;
  WndClass.hInstance := HInstance;
  WndClass.hIcon := LoadIcon(hInstance, MAKEINTRESOURCE(100));
  WndClass.hCursor := LoadCursor(0, IDC_ARROW);
  WndClass.hbrBackground := COLOR_APPWORKSPACE;
  WndClass.lpszMenuName := nil;
  WndClass.lpszClassName := szAppName;
  RegisterClassEx(WndClass);

  InitCommonControls;

  hDialog := CreateDialog(hInstance, MAKEINTRESOURCE(100), 0, nil);

  while GetMessage(msg,0,0,0) do
  begin
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;

  ExitCode := msg.wParam;

  DestroyWindow(hDialog);
end.
Die Programmlogik kommt ganz ohne die sonst notwendigen Konstanten IDC_CLOSE und weitere aus. Zahlenwerte merken muss sich trotzdem niemand, die Lösung ist verblüffend einfach: Für die IDs der Schaltflächen wird einfach der ASCII-Code der jeweiligen Taste benutzt, über welche die Funktionen auch ausgeführt werden können. In den meisten Fällen entspricht der Code direkt der Buttonbeschriftung. Daraus ergibt sich, dass die Nachrichten WM_CHAR und WM_COMMAND letztendlich die gleiche Funktion aufrufen können. Programmlogik ist dabei nur für einige Spezialfälle notwendig: Die Taste "=" kann auch über die Eingabetaste angesprochen werden und statt der Rücktaste ist auch Pfeil-Links möglich.

Delphi-Quellcode:
function WndProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM;
  lParam: LPARAM): LRESULT; stdcall;
var
  hButton: THandle;
  cCommand: CHAR;

begin
  case uMsg of
  // [...]
    WM_KEYDOWN:
      If wParam = VK_LEFT then HandleKey(VK_BACK);
    WM_CHAR:
      begin
        cCommand := UpCase(Chr(wParam));
        If wParam = VK_RETURN then cCommand := '=';

        hButton := GetDlgItem(hWnd, Ord(cCommand));
        If hButton <> 0 then
        begin
          HandleKey(Ord(cCommand));
          If IsWindowsXPOrHigher
            then EnableWindow(hButton, False)
              else SendMessage(hButton, BM_SETSTATE, 1, 0);
          Sleep(100);
          If IsWindowsXPOrHigher
            then EnableWindow(hButton, True)
              else SendMessage(hButton, BM_SETSTATE, 0, 0);
        end else begin
          MessageBeep(0);
        end;
      end;
    WM_COMMAND:
      begin
        SetFocus(hWnd);
        {$IFDEF CloseOnESC}
        if(wParam = IDCANCEL) then SendMessage(hDlg,WM_CLOSE,0,0)
          else
          {$ENDIF}
          if HIWORD(wParam) = BN_CLICKED then
          begin
            HandleKey(LoWord(wParam));
          end;
      end;
  end;
  Result := DefWindowProc(hwnd, uMsg, wParam, lParam);
end;
Egal, ob die Maus oder die Tastatur verwendet wird: Der entsprechende Code wird an die Prozedur HandleKey weitergeleitet, welche die Eingabe verarbeitet. Bei der Tastatureingabe wird dazu noch kurzzeitig der Button gedrückt, um eine optische Rückmeldung zu erhalten. Leider funktioniert dies nur bis einschl. Windows 2000, so dass ich mich entschlossen habe, bei Windows XP oder darüber den Button kurz zu deaktivieren. Die Funktion IsWindowsXPOrHigher liefert True, wenn die Windows-Version mindestens Version 5.1 ist.

Der Befehl SetFocus im WM_COMMAND-Zweig ist nötig, damit das Hauptfenster auch weiterhin die WM_CHAR-Nachrichten erhält, wenn der Rechner mit der Maus bedient wurde.

Die Prozedur HandleKey habe ich erstellt, um den Programmablauf etwas logischer zu gestalten, eine "durchgehende Case-Anweisung", wie sie bei C (switch ohne break) vorhanden ist, gibt es bei Delphi nicht. Die Prozedur dient zum Einlesen der Tasten, Erstellen der Zahlen zum Rechnen und Setzen der globalen Variablen iOperation und bNewNumber, die bestimmen, welche Rechenoperation durchgeführt werden soll und ob eine neue Zahleneingabe begonnen werden soll. Die beiden Funktionen IsHex und HexCharToInt habe ich hinzugefügt, da Delphi die C-Funktionen isdigit und isxdigit nicht kennt.

Delphi-Quellcode:
procedure HandleKey(Key: Integer);

  function IsHex(C: Char): Boolean; // [...]

  function HexCharToInt(Key: Byte): Cardinal; // [...]

begin
  If Key = VK_ESCAPE then
  begin
    iNumber := 0;
    ShowNumber(hDialog, iNumber);
  end else if Key = VK_BACK then
  begin
    iNumber := iNumber div 16;
    ShowNumber(hDialog, iNumber);
  end else if IsHex(Chr(Key)) then
  begin
    if bNewNumber then
    begin
      iFirstNum := iNumber;
      iNumber := 0;
    end;
    bNewNumber := False;
    if (iNumber <= MAXDWORD shr 4) then
    begin
      iNumber := 16 * iNumber + HexCharToInt(Key);
      ShowNumber(hDialog, iNumber);
    end else MessageBeep(0);
  end else begin
    If not bNewNumber then
    begin
      iNumber := CalcIt(iFirstNum, iOperation, iNumber);
      ShowNumber(hDialog, iNumber);
    end;
    bNewNumber := True;
    iOperation := Key;
  end;
end;
Die Funktion CalcIt schließlich führt die eigentlichen Berechnungen durch und gibt das Ergebnis zurück. Auch hier musste der Code angepasst werden, da es die von C bekannten bedingten Anweisungen in Delphi nicht gibt - diese geringere Effizienz wird allerdings mit drastisch verbesserter Lesbarkeit belohnt.

Delphi-Quellcode:
function CalcIt(iFirstNum: Cardinal; iOperation: Integer;
  iNum: Cardinal): Cardinal;
begin
  Case Chr(iOperation) of
     '=': Result := iNum;
     '+': Result := iFirstNum + iNum;
     '-': Result := iFirstNum - iNum;
     '*': Result := iFirstNum * iNum;
     '&': Result := iFirstNum and iNum;
     '|': Result := iFirstNum or iNum;
     '<': Result := iFirstNum shl iNum;
     '>': Result := iFirstNum shr iNum;
     'X': Result := iFirstNum xor iNum;
     '%': If iNum <> 0
            then Result := iFirstNum mod iNum
              else Result := MAXDWORD;
     '/': If iNum <> 0
            then Result := iFirstNum div iNum
              else Result := MAXDWORD;
     else Result := 0;
  end;
end;
Bestandteil des Programms ist auch die Unit HexCalcUtils. Diese existiert lediglich, um nicht die Units CommCtrl und SysUtils verwenden zu müssen, damit das Kompilat schön klein bleibt.

Noch ein paar Worte zum eigentlichen Programm: Es verwendet die Infix-Notation, funktioniert also mit Berechnungen in der Form 3 + 4 = wie der normale Windows-Rechner. Neben den vier Grundrechenarten beherrscht das Programm den Divisionsrest, bitweises AND, OR und XOR sowie bitweises Links- und Rechtsschieben. Der Rechner arbeitet mit 32-Bit-Darstellung (DWORD). Auf Division und Modulo durch 0 reagiert das Programm mit der Anzeige FFFF FFFF.

Die Tastenkombinationen:

Code:
Funktion    Taste
==============================
0..9         0..9
A..F        A..F
+            +
-            -
*            *
/            ÷ oder /
Mod         %
And         &
Or          |
Xor         X
Shl         <
Shr         >
=           = oder Eingabetaste
C (Clear)   ESC
Korrektur   Backspace oder Pfeil-Links
So, dann wünsche ich viel Spaß beim Hex-Rechnen

In eigener Sache: Die Wahl des richtigen Forums war nicht leicht. Es ist ein NonVCL-Thema, aber eigentlich auch Open Source und genaugenommen auch ein Tutorial. Wenn's hier nicht passt, möge es bitte ein Moderator verschieben, Cross-posten oder was auch immer.

Edit: Nachdem mein "Vorkoster" ein paar Bissen dieses Beitrags probiert hat, konnte ich einige Schreibfehler beseitigen. Danke, Wastl!
Miniaturansicht angehängter Grafiken
hexcalc_983.gif  
Angehängte Dateien
Dateityp: rar hexcalc_168.rar (17,6 KB, 33x aufgerufen)
Dateityp: exe hexcalc_437.exe (21,5 KB, 25x aufgerufen)
 
Benutzerbild von Daniel Schuhmann
Daniel Schuhmann

 
Turbo Delphi für Win32
 
#2
  Alt 16. Jan 2008, 11:23
Ich habe noch einige Bugs behoben.

Version 1.0.1.1
  • Pfeil-Links-Taste: Beim Druck auf Pfeil-Links wurde der Korrigieren-Button nicht "eingedrückt", da die Ereignisbehandlung in WM_KEYDOWN erfolgt. Das Eindrücken des Buttons erfolgt jetzt in der eigenen Prozedur AnimateButton, die in WM_KEYDOWN und WM_CHAR aufgerufen wird.
  • Korrektur nach Rechenergebnissen: Wurde der Button Korrektur bei einem fertigen Rechenergebnis gedrückt, wurde nach Eingabe einer Zahlentaste ein neuer Zahlenwert begonnen. Durch Rücksetzen des Flags bNewNumber kann nun auch ein Rechenergebnis korrigiert werden.
Viele Grüße,
Daniel
Angehängte Dateien
Dateityp: rar hexcalc_195.rar (17,7 KB, 25x aufgerufen)
Dateityp: exe hexcalc_108.exe (21,5 KB, 29x aufgerufen)
Daniel Schuhmann
  Mit Zitat antworten Zitat
Benutzerbild von mschaefer
mschaefer

 
Delphi XE3 Enterprise
 
#3
  Alt 16. Jan 2008, 18:10
Also Daniel,
der Rechner ist ein feines Programm um mal mit Speicherbereichen zu rechnen
und man sieht mal, wie klein man exe´n bekommen kann. Prima! // Martin
Martin Schaefer
  Mit Zitat antworten Zitat
8. Jan 2009, 14:49
Dieses Thema wurde von "Luckie" von "Windows API / MS.NET Framework API" nach "Open-Source" verschoben.
Besser später als nie.
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:13 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz