Einzelnen Beitrag anzeigen

Olli
(Gast)

n/a Beiträge
 
#10

Re: [nonVCL] Subclassing eines StcCtrl's / falscher Font...

  Alt 1. Jul 2007, 17:48
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 MSDN-Library durchsuchenEndDialog, nicht MSDN-Library durchsuchenDestroyWindow!

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.
Angehängte Dateien
Dateityp: rar superclassbeispiel_194.rar (11,4 KB, 22x aufgerufen)
  Mit Zitat antworten Zitat