![]() |
HexCalc: Der Gipfel der Faulheit aka. Dialog mit Klassenname
Liste der Anhänge anzeigen (Anzahl: 3)
Vor
![]() 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. 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:
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:
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.
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"
Delphi-Quellcode:
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.
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.
Delphi-Quellcode:
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.
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; 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:
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.
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;
Delphi-Quellcode:
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.
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; 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:
So, dann wünsche ich viel Spaß beim Hex-Rechnen :-D
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 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! :thumb: |
Version 1.0.1.1
Liste der Anhänge anzeigen (Anzahl: 2)
Ich habe noch einige Bugs behoben.
Version 1.0.1.1
Daniel |
Re: HexCalc: Der Gipfel der Faulheit aka. Dialog mit Klassen
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 |
DP-Maintenance
Dieses Thema wurde von "Luckie" von "Windows API / MS.NET Framework API" nach "Open-Source" verschoben.
Besser später als nie. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:25 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz