Zitat von
turboPASCAL:
Zitat von
Olli:
Wie wäre es denn mit SuperClassing statt Subclassing? Schon mal daran gedacht?
Nein, habe auch nicht den geringsten Schimmer was das ist.
Das Prinzip ist in etwa genauso, aber eben ein kleines bißchen einfacher wenn man es von Anfang an durchzieht. Ach ja, kannst du auch bei dialogbasierten
nonVCL-Programmen einsetzen, insofern du sicherstellst, daß du ein "custom control" mit dem von dir für die Superklasse vergeben Namen erstellst.
Das Prinzip:
- Du lädst alle notwendigen DLLs (weil das nicht automatisch passiert), also bspw. die Common Controls. Bei einem Static kannst du diesen Schritt weglassen.
- Dann ermittelst du die Eigenschaften der Klasse die su superclassen willst.
- Dann überschreibst du jene Eigenschaften, die du überschreiben willst.
- Dann registrierst du die neue Klasse im System.
Nähmen wir an die Fensterfunktion der neuen Klasse wäre jetzt nicht in deiner EXE sondern in einer
DLL und nähmen wir weiterhin an, daß diese
DLL nicht relozierbar ist (wahr für die System-DLLs), dann könntest du innerhalb eines anderen Prozesses auch ein Fenster dieser Fensterklasse erzeugen. Beschränkt wird das wohl nur durch den Desktop oder so (Reichweite von Atoms).
Logischerweise könntest du auch einfach ein Static in der Dialogvorlage erstellen lassen, welches du in WM_INITDIALOG wieder killst, aber zuvor dessen Parameter (Position, Text usw.) übernimmst. Manchmal ist eine solche Vorgehensweise sogar unumgänglich, bspw. wenn du ein einziges Control in einer Anwendung die
UNICODE ist als
ANSI haben möchstest. Da hilft dann nur das erneute Erstellen mit bekannten Parametern
ACHTUNG: Wenn die in der Dialogvorlage referenzierte Fensterklasse nicht existiert, wird der Dialog nicht erzeugt. Es gibt allerdings ein Flag, um solche Fehler einfach zu ignorieren (DS_NOFAILCREATE).
Nun zum eigentlichen Thema, das Beispiel. Habe mal ganz frech deinen Code als Vorlage genommen. An einigen Stellen noch sichtbar, woanders eher weniger
HINWEIS: Um einen Dialog zu Beenden nehme man bitte
EndDialog, nicht
DestroyWindow!
Hier erstmal die Dialogvorlage (veranschaulicht!!!, der eigentliche Klassenname ist unten ersichtlich):
Code:
IDD_DIALOG1 DIALOGEX 0, 0, 186, 84
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | [color=green]/* DS_NOFAILCREATE |*/[/color] WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,129,63,50,14
LTEXT "Static",IDC_STC1,7,7,172,8
CONTROL [color=red]"Text des Controls"[/color],IDC_CUSTOM1,[color=blue]"Klassenname"[/color],WS_TABSTOP | WS_CHILD | WS_VISIBLE,7,22,172,8
END
Delphi-Quellcode:
program SuperClassBsp;
// [...]
type
HANDLE = THandle;
TMyClassDescriptor =
record classname: PAnsiChar; parentclass: PAnsiChar; wndproc: TFNWndProc; oldwndproc: TFNWndProc; atom: TAtom;
end;
TMyWndClasses = (wcMyStatic);
// Forward-Deklaration, da es in der Variableninitialisierung benutzt wird!
function MyStaticWndProc(hWnd: HWND; uMsg: UINT; wParam, lParam: Integer): LRESULT;
stdcall;
forward;
var
_WndClasses:
array[TMyWndClasses]
of TMyClassDescriptor =
(
(
classname: '
MeineStaticKlasse';
parentclass: '
STATIC';
wndproc: @MyStaticWndProc;
oldwndproc:
nil;
)
);
function MyStaticWndProc(hWnd: HWND; uMsg: UINT; wParam, lParam: Integer): LRESULT;
stdcall;
// [...]
function DialogProc(hDlg: HWND; uMsg: UINT; wParam, lParam: Integer): BOOL;
stdcall;
// [...]
// Generische Funktion um Fensterklassen hinsichtlich ihrer Fensterfunktion zu
// superclassen. _WndClasses muß als globale Variable vorinitialisiert sein!!!
function RegisterMyWindowClasses(): Boolean;
var
i: TMyWndClasses;
wc: WNDCLASSEX;
begin
Result := True;
for i := Low(_WndClasses)
to High(_WndClasses)
do
begin
ZeroMemory(@wc, sizeof(wc));
wc.cbSize := sizeof(wc);
if (GetClassInfoEx(NULL, _WndClasses[i].parentclass, wc))
then
begin
// Sichern der alten Fensterfunktion
_WndClasses[i].oldwndproc := wc.lpfnWndProc;
// Zuweisen der neuen Fensterfunktion
wc.lpfnWndProc := _WndClasses[i].wndproc;
// Neuer Klassenname (so wird er auch in der Dialogvorlage benutzt!)
wc.lpszClassName := _WndClasses[i].classname;
// Diese Klasse wurde von uns erzeugt/registriert!
wc.hInstance := hInstance;
wc.style := (
not CS_GLOBALCLASS)
and wc.style;
// Jetzt sollte alles stimmen (der Rest muß hier schon korrekt sein)
_WndClasses[i].atom := RegisterClassEx(wc);
// Writeln('Atom == ', _WndClasses[i].atom, ' (', String(_WndClasses[i].classname), ')');
// Im Fehlerfall ...
if (0 = _WndClasses[i].atom)
then
begin
// ... sicherstellen, daß die Kombination aller Resultate False wird!
Result := Result
and False;
end;
end;
end;
end;
// Generischer Gegenpart zur Funktion RegisterMyWindowClasses().
procedure UnRegisterMyWindowClasses();
var i: TMyWndClasses;
begin
// ... und abermals gehen wir durch das Array mit den Werten
for i := Low(_WndClasses)
to High(_WndClasses)
do
begin
if (_WndClasses[i].atom <> 0)
then
if (UnregisterClass(_WndClasses[i].classname, hInstance))
then
begin
_WndClasses[i].atom := 0;
end;
end;
end;
begin
if (RegisterMyWindowClasses())
then
begin
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, @DialogProc);
end;
// Aufräumen in jedem Fall!
UnRegisterMyWindowClasses();
end.
Zum Thema wo man noch Daten des Fensters/der Fensterklasse abspeichern kann, findet sich
hier ein guter Beitrag.