AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Delphi Handles
Tutorial durchsuchen
Ansicht
Themen-Optionen

Handles

Ein Tutorial von Luckie · begonnen am 9. Jun 2002 · letzter Beitrag vom 9. Jun 2002
Antwort Antwort
Benutzerbild von Luckie
Luckie
Registriert seit: 29. Mai 2002
Alles was sie schon immer über Handles wissen wollte, aber nie zu fragen wagten (frei nach Woody Allen)

In diesm Tutorial soll es also um das Schreckgespenst Handles gehen. Aber wie wir sehr bald sehen werden ist das Schreckgespenst ein recht armloser, alter Hausgeist, der uns bei Zeiten recht nützlich werden kann.

"Wo simmer denn dran? Aha, heute kriejen mer de Handles. Also, wat is en Handle? Da stelle wir uns janz dumm. Und da sage mer so: En Handle, dat is ene Zahl, die hat nen Datentyp und ne Funktion. Der Datenty ist vom Typ DWORD und de Funktion bekommen wer später." (So mehr können wir erst mal nicht aus Heinrich Spoerls "Feuerzangenbowle" raus holen, für den Rest müßte man Bill Gates bemühen .)

Gut, ich denke, es ist klar geworden um was es hie rgehen soll: Handles und was man mit ihnen anstellen kann. Ich werde hier erst mal etwas Theorie machen und dann die Theorie in die Praxis umsetzten, damit man auch ein Ergebnis auf der Hand hat.

1. Theorie
Um mit Handles um gehen zu können, muß man erst mal verstehen, wie Windows grundsätzlich funktioniert. Windows ist ein multitasking System, dass heißt, der Benutzer kann mehrere Programme gleichzeitig gestartet haben und mit ihnen arbeiten, zum Beispiel kann er wärend ein Grafikprogramm eine große Grafik bearbeitet mit einem Textverarbeitungsprogramm seinen ersten Roman fertigstellen. Dies Stellt gewisse Anforderungen an das System, zum einen was die Speicherverwaltung betrifft (darum wollen wir uns aber hier nicht kümmer) und zum anderen was die Kommunikation mit den Programmen angeht und das ist schon eher Gegenstand diese Tutorials.

Kann in einem System immer nur ein Programm in den Speicher geladen werden, bedeutet das für die Kommunikation kein großes Problem. Nehem wir ein Textverarbeitungsprogramm unter DOS: Stellt es eine Anfrage, um eine Datei zu öffnen, leitet es die Anfrage an das System, welches die Anfrage verarbeitet und die gewünschten / benötigten Informationen zurückliefert. Da es nur ein aktives Programm gibt, hat das System kein Problem damit, von welchen Programm die Anfrage kommt bzw. an welches es die Informationen liefern muß.

Unter einem multitasking System funktioniert das nun nicht mehr, da muß man sich was anderes einfallen lassen und das haben auch die Entwickler getan. Windows-Programme kommunizieren mit dem Betriebssystem und gegebenenfalls auch mit anderen Programmen über Nachrichten (Messages). Und der Rest funktioniert auch wie im wirklichen Leben mit der Post: Wenn ich einen Brief verschicke muß ich zu mindest dem Empfänger angeben und dieser sollte nach Möglichkeit eindeutig sein und nichts anderes ist ein Handle, ein eindeutiger Bezeichner für ein Fenster. Hier müssen wir uns von der Vorstellung lösen, dass nur es sich nur um das Hauptfenster um ein Fenster handelt. Windows versteht unter Fenster eigentlich alles, was sich auf unserem Programmfenster befindet, also Schaltflächen (Buttons), Texteingabefelder (Edits), Bezeichner (Lables) und alles andere, also auch ein Listview, Treeview oder eine Listbox usw. Und bei jedem Start eine Programmes vergibt Windows an alle Fenster des Programmes eindeutige Bezeichner und unter Windows nennt man diese Bezeichner - ihr dürft raten - genau, Handles.

Windows verwaltet diese Handles in einer Tabelle und kann daher zu jedem Zeitpunkt ein Objekt in einem Programm genau identifizieren.

Beispiel:
Code:
SendMessage(hEdit, WM_GETTEXT, sizeof(buffer), Integer(@buffer));
Ich will jetzt diese Win32-API Funktion nicht zerlegen. Nur so viel, sie schreibt den Inhalt eines Textfeldes in eine Variable mit den Namen buffer. Damit Windows jetzt weiß, von welchem Textfeld es den Text holen soll, muß als erster Parameter der Handle des Fensters angegeben werden. In unserem Fall handelt es sich um die Variable hEdit. Dieses Handle muß man sich vorher holen - wie auch immer - bzw. man kennt es schon von vorherigen Aktionen, beispielsweise, wenn man das Edit mit der Win32-API-Funktion CreateWindow erstellt hat liefert es den Handle zurück. Klevere Programmierer merken sich dieses Handle gleich in einer globalen Variable zu späteren Verwendungszwecken.

Ich denke, dies sollte genug Theorie sein und für das Grundverständnis reichen. Zusammen fassend kann man sich merken:
Ein Handle ist ein eindeutiger Bezeichner für ein Fenster.
Hat man erst mal dieses Handle kann man alles Windows mögliche damit, besser mit dem Fenster machen.


2. Praxis
Eine beliebte Frage ist immer wieder: Wie kann ich ein bestimmtes Fenster minimieren oder schließen? Wer wissen will, wie beliebt diese Frage ist braucht nur mal die Suchfunktion des Forums zu bemühen und wird feststellen, dass regelmäßig alle 14 Tage diese Frage auftaucht. Dies ist auch ein gutes Beispiel, um mal zu demonstrieren, wie man mit Handles umgeht und was man mit ihnen machen kann. Weitaus besser eigent sich hierzu natürlich die nonVCl oder direkte Win32-Api Programmierung, da man da ohne Handles nicht sehr weit kommt, aber das will ich euch nicht auch noch zu muten, deswegen ein Beispiel mit VCL.

Also was wollen wir machen: Wir wollen ein anderes Fenster schließen. (Wichtig: Wir wollen keinen Prozess beenden, denn ein Prozess kann mehrere Fenster haben und wir würden nur den Prozess beenden, wenn wir zufälligerweise das Hauptfenster erwischen.) Ein Fenster schließen wir mit der Windows-Nachricht WM_CLOSE. Diese müssen wir an das Fenster senden. Senden tuen wir eine Nachricht mit SendMessage(). SendMessage erwartet als ersten Paramter den Empfänger der Nachricht, also um es mal fachmännisch auszudrücken und wer alles bisher gelesen und verstanden hat, sollte dazu in der Lage sein, den Handle. Wenn wir den Handle haben, können wir unsere Nachricht an dan betreffend Fenster schicken und das stellt sich dann wie folgt dar:
Code:
SendMessage(hWnd, WM_CLOSE, 0, 0);
hWnd ist in diesem Fall das gewünschte Handle.
So jetzt wollen wir mal sehen, wie sich das programmtechnisch um setzen läßt. Dazu ein kleines Beispiel-Programm von mir:

Wir benötigen einen Listview mit zwei Spalten: 1. Spalte: "Handle", 2. Spalte: "Fenstertext" und zwei Buttons Button1: "Handles holen", Button2: "Fenster schließen". Das war schon alles. Ich habe die Komponenten nicht umbenannt und stelle hier nur den für uns interessante Programmteil dar, nämlich die Prozeduren für die Buttons.

Code:
procedure TForm1.Button1Click(Sender: TObject);
var
  buffer: array[0..255] of Char;
  ListItem: TListItem;
  hWnd: DWORD;
begin
  { Inhalt des Listviews löschen }
  Listview1.Clear;
  { mit diesem Handle starten wir }
  hWnd := Application.Handle;
  { Listitem hinzufügen }
  ListItem := Listview1.Items.Add;
  { Listviewitem mit Handle beschriften }
  ListItem.Caption := IntToStr(hWnd);
  { Fenstertext in das Subitem schreiben }
  ListItem.SubItems.Add(Form1.Caption);
 
  { alle Fenster durchgehen }
  while hWnd > 0 do begin
    { das Handle des nächsten Fensters in der Z-Order holen, unseres ist das }
    { oberste, da aktiv }
    hWnd := GetNextWindow(hWnd, GW_HWNDNEXT);
    { buffer leeren }
    ZeroMemory(@buffer, sizeof(buffer));
    { Fenstertext holen, hier brauchen wir das Handle }
    GetWindowText(hWnd, buffer, sizeof(buffer));
    { uns interessieren nur die Fenster mit Text und die sichtbar sind }
    if (buffer <> '') AND (IsWindowVisible(hWnd) = TRUE) then
    begin
      { Listitem hinzufügen }
      ListItem := Listview1.Items.Add;
      { mit Handle beschriften }
      ListItem.Caption := IntToStr(hWnd);
      { Und den Fenstertext in das Subitem schreiben }
      ListItem.SubItems.Add(buffer);
    end;
  end;
end;


procedure TForm1.Button2Click(Sender: TObject);
var
  hWnd: DWORD;
begin
  { Handle auslesen }
  hWnd := StrToInt(Listview1.Selected.Caption);
  { Nachricht schicken }
  SendMessage(hWnd, WM_CLOSE, 0, 0);
  { Listview aktualisieren }
  Form1.Button1Click(Form1);
end;
So das war eigentlich schon alles. Ich denke, ich muß dazu nicht mehr sagen, die Kommentare sollten für sich sprechen.

Ich hoffe, ich konnte hier ein paar Unklarheiten beseitigen und euch die Angst vor diese doch so geheimnisvollen Handles nehmen. Nur weil es sich um ziemlich systeminterne Dinge handelt braucht man keine Angst davor zu haben. Man sollte allerdings genau wissen, was man tut. Aber alles in allem, kann man wohl sagen, dass alles nur halb so wild ist. Auch Windows kocht nur mit Wasser, um mal meinen alten Fußballtrainer zu zitiern.

Also Jungs und Mädels viel Spaß beim Programmieren, Ball flach halten und immer dran denken: "Und immer eine Hand voll Handles." (<- stammt glaube ich von unserem NIcoDE)

Das Tutorial dürfte fürs erste von meiner Seite als abgeschlossen betrachtet werden. Wer Ergänzungen hat oder meint ich hätte hier den totalen Schrott erzählt, kann ja posten.
Ein Teil meines Codes würde euch verunsichern.
 
Benutzerbild von Luckie
Luckie

 
Delphi 2006 Professional
 
#2
  Alt 9. Jun 2002, 16:56
Auch HDC's sind nur Handles

Dem ein oder anderen von euch ist bestimmt schon mal das Buchstabenkürzel DC oder HDC oder der Begriff Device Context untergekommen und hat sich dann gefragt, was es damit auf sich hat. Nun da das Hauptthema dieses Kurses Handels sind und wie man schon an der Überschrift erkennen kann, handlet es sich bei einem Device Context auch nur um einen Handle.

Was ist nun ein Device Context (DC)?
Na ja, da fragen wir doch mal die, die es wissen müßten oder sollten: die Leute von Microsoft:
Zitat:
Device Contexts
A device context is a structure that defines a set of graphic objects and their associated attributes, as well as the graphic modes that affect output. [...]
Aus dem PSDK

Also ein DC hat was mit zeichnen zu tun. Und was soll das Gefasel von einer Struktur? In dieser Struktur sind einfach die Eigenschaften von diversen Zeichenoperationen festgelegt. Die man ändern kann aber nicht muß, da die Struktur mit vordefinierten Weten besetzt wird. In den meisten Fällen wird man sie aber doch ändern, da die standard Werte meist unbrauchbar sind.

Und ich will noch mal das PSDK bemühen:
Zitat:
About Device Contexts
Device independence is one of the chief features of Microsoft® Windows®. Win32®-based applications can draw and print output on a variety of devices. The software that supports this device independence is contained in two dynamic-link libraries. The first, Gdi.dll, is referred to as the graphics device interface (GDI); the second is referred to as a device driver. The name of the second depends on the device where the application draws output. For example, if the application draws output in the client area of its window on a VGA display, this library is Vga.dll; if the application prints output on an Epson FX-80 printer, this library is Epson9.dll.
Kurz übersetzt und zusammen gefaßt, heißt das so viel wie:
"Ein Hauptmerkmal von Windows ist die Geräteunabhängigkeit. Windows-Anwendungen können ihre Ausgabe auf vielen verschiedenen Geräten stattfinden lassen. Die benötigten Funktionen werden von der GDI.dll (GDI: grafic device interface) bzw von speziellen Gerätetreibern, wie Druckertreiber dll's bereitgestellt."
Soll so viel heißen wie: Wenn ich mit Rectangle() ein Rechteck auf den Bildschirm zeichne, kann ich mit der selben Funktion auch das Rechteck auf den Drucker zeichnen oder sonst wohin schmieren.

Und noch mal das PSDK, weil es so schön funktioniert:
Zitat:
An application must inform GDI to load a particular device driver and, once the driver is loaded, to prepare the device for drawing operations (such as selecting a line color and width, a brush pattern and color, a font typeface, a clipping region, and so on). These tasks are accomplished by creating and maintaining a device context (DC).
So jetzt wären wir da wo wir hin wollen und worauf es mir hier ankommt. Man lasse sich mal den ersten Teil des ersten Satzen auf der Zuge zergehen: "An application must inform GDI to load a particular device driver and, [...]". Um es mal ganz platt zu sagen, man muß sich erst mal das Handle für ein Gerät holen. Wobei 'Gerät' nicht unbedingt was sein muß, was man im Computerladen für viel Geld kaufen kann. Gerät kann in diesem Fall auch das Fenster der eigenen Anwendung oder der Desktop sein.

Man merke sich:
Aus den ertenen Teil wissen wir, dass ein Handle nichts weiter als eine Kennziffer ist, die Windows intern als Querverweis auf Objekte benutzt. Ein Handle für einen Gerätekontext stellt so zusagen den Schlüssel eines Programmes für sämtliche GDI-Funktionen dar.

Wie funktioniert das nun mit den DC's?
Nun, wenn wir ein Handle zu einem entsprechenden Gerätekontext anfordern, reagiert Windows auf diese Anforderung mit der Initialisierung einer internen Datenstruktur, die erst mal mit standard Werten besetzt wird und einem Handle, welches den Zugriff auf diese Struktur für diese Gerät ermöglicht. Wichtig: An dieser Stelle sei noch angemerkt, dass wann immer man einen DC anfordert diesen auch wieder freizugeben hat, wenn man mit im fertig ist.

Die Praxis
Da ich ja weiß, was ihr hier für böse Buben und Mädels seit und nur blödsinn im Kopf habt ) gleich mal was, um euren Spieltrieb zu befriedigen und was, wo ihr so richtig böse sein könnt, denn was ich in dem folgenden kleinen Programm demonstriere macht man aus Höflichkeit normalerweise nicht - nämlich in fremden Fenster rumschmieren.
Normaler weise kommt man, wenn man mit der VCl programmiert recht gut ohne DC aus, wenn man auf dem eigenen Fenster rumpinseln will. Einfach
Code:
Form1.Canvas.Textout();
Den ganzen DC-Kram erledigt hier das Canvas-Objekt von der Form. Was aber nun wenn ich wo ganz anders rumschmieren will, als auf dem Fenster meiner Anwedung? Wenn wir so etwas links oder rechts an unserem Anwendungsfenster vorbei schielen, kann man doch ganz schnell auf dumme Gedanken kommen... Und man stellt sich die Frage: "Kann ich nicht irgend wie auf den Desktop malen?" Antwort: "Kann man. Und in genau diesem Fall braucht man den DC vom Desktop.
Das Beispiel-Programm besteht einfach nur aus zwei Buttons, die ich auch nicht umbannt habe. Hier nur der Implementaion-teil des Programmes:
Code:
procedure TForm1.Button1Click(Sender: TObject);
var
  DC: HDC; // Handle für Device Context
  Brush: HBRUSH; //  Handle für Brush
  Pen: HPEN; // Handle für Zeichenstift
  s: String; // variable für den Text
begin
  { Device Context holen, 0 für den De3sktop, Application.Handle für eigene Anwendung}
  DC := GetDC(0);
  { Frabe wählen }
  Brush := CreateSolidBrush(RGB(255,0,0));
  { in DC einsetzen }
  SelectObject(DC, Brush);
  { Zeichenstift erzeugen }
  Pen := CreatePen(PS_SOLID, 3, RGB(75,0,255));
  { in DC einsetzen }
  SelectObject(DC, Pen);
  { Rechteck zeichnen }
  Rectangle(DC, 10, 10, 375, 100);
  s := 'In fremden Fenstern rumschmieren macht man nicht.';
  { Hintergrundmodus auf transparent setzen, sonst hat der Text einen weißen Hintergrund }
  SetBkMode(DC, Transparent);
  { Text ausgeben }
  TextOut(DC, 20, 50, PChar(s), length(s));;
  { und aufräumen, DC wieder freigeben }
  ReleaseDC(0, DC);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  { Rechteck für ungültig erklären: 0 für Desktop, nil für gesamtes Fenster, TRUE um den Hintergrund neu zu zeichnen }
  InvalidateRect(0, nil, TRUE);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  InvalidateRect(0, nil, TRUE);
end;
Die GDI-Funktionen will ich hier jetzt nicht näher erläutern, die könnt ihr in der Windows SDK Hilfe von Delphi nachschlagen, es ging mir hier halt nur um den DC. Und die Kommentare sollten wieder für sich sprechen

So, ich hoffe, ich habe euch wieder mal etwas über die Interna von Windows aufklären können und euch die interne Funktionsweise von Windows etwas näher bringen können.

Viel Spaß mit den DC wünscht euch noch euer Luckie. Bis zum nächsten mal in diesen Lichtspieltheater.
Michael
  Mit Zitat antworten Zitat
Antwort Antwort


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 13:55 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